RadarX: Building Competitive Intelligence That Actually Remembers
Most competitive-intelligence systems can tell you what happened today.
The harder question is:
What does today's event mean when you compare it with everything that happened before?
A competitor changes its pricing.
A few months later, it launches a product update.
Then customer feedback starts changing.
Individually, these events may look unrelated. But when an AI agent can remember previous observations and retrieve them when answering a new question, those events can become part of a larger pattern.
That is the idea behind RadarX — Competitive Intelligence that remembers.
RadarX is a Streamlit-based competitive-intelligence agent that uses Hindsight persistent memory to retain dated market events, recall relevant historical evidence, and reason over that evidence before producing an answer.
The core loop is:
Question → Hindsight Recall → Evidence → Reflection → Grounded Answer
The problem with one-shot competitive analysis
A normal LLM conversation is very good at analyzing the information you give it.
But competitive intelligence is inherently cumulative.
Suppose an analyst asks:
"What has changed in our competitor's strategy?"
If the system only sees the latest event, it may describe that event correctly but miss the historical context.
For example:
January:
Competitor changes pricing.
April:
Competitor receives a new type of customer feedback.
July:
Competitor changes pricing again.
August:
Similar customer feedback appears.
Looking at only the August event gives you one observation.
Remembering the entire sequence allows the system to ask a more useful question:
Is this an isolated event, a repeated pattern, or part of a sustained trend?
RadarX was designed around that distinction.
What RadarX does
RadarX stores competitive events such as:
- Pricing changes
- Promotions
- Product updates
- Delivery changes
- Customer feedback
- Hiring signals
- Other market events
Each event is associated with information such as its date, company, event type, description, and impact score.
The system then uses Hindsight as the persistent memory layer.
Instead of treating every question as a completely new analysis, RadarX can retrieve historical competitive evidence relevant to the question.
The architecture
At a high level, RadarX has five stages:
┌─────────────────────┐
│ Market Events │
│ CSV / Signals │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Hindsight Memory │
│ Bank │
└──────────┬──────────┘
│
Recall Evidence
│
▼
┌─────────────────────┐
│ RadarX │
│ Reflection │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Intelligence Output │
│ Facts │
│ Significance │
│ Recommendation │
│ Limitations │
└─────────────────────┘
The important part is that memory sits between the incoming question and the reasoning step.
Why Hindsight?
RadarX needs more than temporary conversation context.
It needs a persistent place where historical competitive observations can be retained and recalled later.
The project creates a dedicated Hindsight memory bank:
BANK_ID = "radarx-competitive-intelligence"
The bank is configured with a mission describing what RadarX is supposed to do:
MISSION = """
You are RadarX, a competitive intelligence agent.
Analyze dated competitor pricing, product updates, customer feedback,
promotions, delivery changes, hiring signals, and market events.
Give evidence-based analysis and recommendations.
Never invent evidence.
Never assume facts that are not present in memory.
Use Hindsight memory as the primary source of competitive intelligence.
""".strip()
This is important because memory by itself does not automatically make an agent reliable.
RadarX also gives the memory system explicit behavioral directives:
DIRECTIVES = [
"Use only supplied evidence.",
"Clearly separate facts from recommendations.",
"Cite date, company, and event text whenever available.",
"State uncertainty when evidence is insufficient.",
"Never fabricate or assume competitive events.",
"Distinguish one-time events from repeated patterns and sustained trends.",
]
The last directive is particularly important for competitive intelligence.
Two similar events may indicate a pattern.
One event does not necessarily indicate a trend.
RadarX explicitly asks the reasoning layer to make that distinction.
Creating the Hindsight memory bank
The Hindsight client is initialized using an API key stored outside the application:
def create_hindsight_client():
api_key = os.getenv("HINDSIGHT_API_KEY")
if not api_key:
raise RuntimeError(
"HINDSIGHT_API_KEY is missing. "
"Check your .env file."
)
return Hindsight(
base_url=HINDSIGHT_BASE_URL,
api_key=api_key,
)
The memory bank is then created or updated:
await client.acreate_bank(
bank_id=BANK_ID,
name="RadarX Competitive Intelligence",
mission=MISSION,
background=background,
reflect_mission=MISSION,
disposition={
"skepticism": 5,
"literalism": 5,
"empathy": 3,
},
)
RadarX also applies its directives to the bank.
This gives the agent a persistent competitive-intelligence context rather than simply sending a large prompt every time.
Turning market events into memory
The next step is ingestion.
RadarX reads market events from:
data/market_events.csv
Each row contains fields such as:
timestamp
company
event_type
title
description
impact_score
The application converts each event into a structured memory entry.
For example:
content = f"""
Date: {timestamp}
Company: {company}
Event Type: {event_type}
Title: {title}
Event: {description}
Impact Score: {impact_score}
""".strip()
Metadata is retained alongside the event:
metadata = {
"source": "data/market_events.csv",
"date": timestamp,
"company": company,
"event_type": event_type,
"title": title,
"impact_score": impact_score,
}
The event is then stored in Hindsight:
await client.aretain(
bank_id=BANK_ID,
content=content,
metadata=metadata,
tags=[
company,
event_type,
"competitive-intelligence",
],
update_mode="replace",
)
This is where RadarX starts building its competitive memory.
The system is no longer looking only at the current question.
It has historical observations that can be retrieved later.
The interesting part: Recall → Reflection
The most important part of RadarX is what happens when a user asks a question.
RadarX does not immediately send the question to an LLM and ask it to answer.
First, it asks Hindsight to recall relevant evidence.
recall_response = await client.arecall(
bank_id=BANK_ID,
query=question,
budget="high",
max_tokens=8192,
include_chunks=True,
max_chunk_tokens=4096,
include_source_facts=True,
max_source_facts_tokens=4096,
)
The result can contain recalled text, chunks, and source facts.
Only after retrieving this historical context does RadarX move to the reflection stage.
Reflection is deliberately evidence-constrained
RadarX constructs a reflection prompt containing the user's question and the evidence recalled from Hindsight.
The reasoning instructions explicitly say:
Use only the competitive-intelligence evidence
recalled from Hindsight.
The agent is also instructed to determine whether the evidence is actually sufficient.
If the evidence is insufficient, RadarX should not fill the gap with general knowledge.
Instead, it should report that the current memory does not contain enough relevant evidence.
That distinction is important.
A competitive-intelligence system that confidently invents missing competitor activity can be worse than a system that simply says:
"I don't have enough evidence."
Structured intelligence output
RadarX uses a response schema rather than asking the model for an unstructured paragraph.
The schema contains:
RADARX_RESPONSE_SCHEMA = {
"type": "object",
"properties": {
"evidence_sufficient": {
"type": "boolean",
},
"threat_level": {
"type": "string",
"enum": ["High", "Medium", "Low"],
},
"facts_evidence": {
"type": "string",
},
"why_it_matters": {
"type": "string",
},
"recommended_action": {
"type": "string",
},
"confidence_limitations": {
"type": "string",
},
},
"required": [
"evidence_sufficient",
"threat_level",
"facts_evidence",
"why_it_matters",
"recommended_action",
"confidence_limitations",
],
}
The reflection call then uses that schema:
reflect_response = await client.areflect(
bank_id=BANK_ID,
query=reflection_query,
budget="high",
max_tokens=5000,
response_schema=RADARX_RESPONSE_SCHEMA,
include_facts=True,
apply_all_directives=True,
)
This creates a predictable intelligence output:
Facts / Evidence
What does the stored evidence actually say?
Why It Matters
What significance can reasonably be derived from that evidence?
Recommended Action
What should be considered based on the evidence?
Confidence / Limitations
What does the evidence fail to establish?
This separation helps keep facts and interpretation distinct.
Before vs. with persistent memory
Consider a new competitor event.
Without historical memory, the analysis might look like:
Latest event:
Competitor changed its delivery experience.
Conclusion:
The competitor changed its delivery experience.
That's factually useful, but limited.
With Hindsight memory, RadarX can retrieve previous observations and evaluate the new event against them.
The result can instead become:
Current event
↓
Historical competitor observations
↓
Repeated event types
↓
Evidence of patterns
↓
Strategic interpretation
The goal isn't simply to remember more.
The goal is to make historical context available when it becomes relevant.
Detecting repeated competitive patterns
RadarX also performs explicit pattern detection over its market-event data.
It groups events by:
["company", "event_type"]
and ignores groups with fewer than two events:
if len(group) < 2:
continue
That gives the system a simple but useful rule:
One observation is not enough to call something a repeated pattern.
For groups containing multiple events, RadarX calculates the average impact and preserves the supporting events:
patterns.append(
{
"company": company,
"event_type": event_type,
"count": len(group),
"average_impact": average_impact,
"dates": group["timestamp"]
.astype(str)
.tolist(),
"events": supporting_events,
}
)
The dashboard can then show the supporting evidence behind a detected pattern.
This is useful because the user can inspect why the application considers something repeated instead of seeing only a generated conclusion.
The memory inspector
Another important part of the interface is the Hindsight Memory Inspector.
After a question is analyzed, RadarX exposes:
- Recalled memory
- Recalled chunks
- Source facts
The application can therefore show the evidence chain behind the answer:
User Question
↓
Hindsight Recall
↓
Relevant Historical Evidence
↓
Evidence Sufficiency
↓
Reflection
↓
Grounded Intelligence
This makes the memory mechanism visible instead of treating it as a black box.
Handling insufficient evidence
One of the design decisions I'm most interested in with RadarX is what happens when memory doesn't contain the answer.
The application explicitly checks:
evidence_sufficient = reflection.get(
"evidence_sufficient",
False,
)
If evidence is insufficient, the UI warns the user:
RadarX does not have enough relevant evidence
in Hindsight memory to answer this question.
It also explains that the answer is intentionally limited to currently stored evidence.
This behavior is important because competitive intelligence often involves incomplete information.
The agent shouldn't convert missing information into invented information.
Adding a signal scanner
RadarX also contains an optional Groq-based signal-scanning component.
Its job is to retrieve potential new market signals.
But there is an important constraint in the implementation.
The scanner prompt explicitly tells the model:
Do NOT generate, simulate, predict, or invent events.
If you do not have verified source material containing recent
events, return:
{"events": []}
If no verified signals are available, RadarX doesn't create fake events just to make the dashboard look active.
Instead:
"No new verified signals were retrieved. "
"No synthetic events were added to memory."
That is a deliberate reliability boundary.
A small but important engineering detail
RadarX uses asynchronous Hindsight calls inside Streamlit.
Because Streamlit can already have an event loop running, the project includes a helper that detects whether a loop is active.
If no loop is running:
return asyncio.run(coro)
If one is already running, RadarX creates a separate thread and executes the coroutine there.
This lets the Streamlit application use the asynchronous Hindsight client without making the UI architecture unnecessarily complicated.
What the dashboard provides
The RadarX interface combines several views:
- Intelligence dashboard
- Memory event count
- Companies tracked
- Detected patterns
- Average impact
- Continuous memory scanner
- Remembered timeline
- Competitor radar
- Market signal matrix
- Ask RadarX query console
- Intelligence output
- Evidence chain
- Hindsight memory inspector
- Raw source data
The goal is to make the entire memory-to-intelligence pipeline visible in one place.
What changed compared with a stateless analysis?
The fundamental change is not simply that RadarX stores more data.
It changes the workflow.
Without persistent memory
Question
↓
LLM
↓
Answer
The answer depends heavily on the context available at that moment.
With RadarX + Hindsight
Question
↓
Hindsight Recall
↓
Historical Evidence
↓
RadarX Reflection
↓
Evidence Check
↓
Answer + Limitations
The agent can therefore use previously retained competitive observations when they are relevant to a new question.
What I learned
The biggest lesson from building RadarX is that memory alone does not create intelligence.
Persistent memory is useful only when the agent can:
- Store useful information.
- Retrieve relevant information.
- Distinguish relevant evidence from irrelevant evidence.
- Recognize when evidence is insufficient.
- Separate facts from interpretation.
- Explain the limitations of its conclusions.
That's why RadarX doesn't stop at retain.
The application uses a complete loop:
Retain → Recall → Reflect → Explain
Hindsight provides the persistent memory layer, while the RadarX logic determines how that memory is used for competitive intelligence.
Limitations
RadarX is a prototype, so there are several limitations.
First, the competitive events used by the current demonstration are stored market-event data rather than a complete production-grade competitive-intelligence feed.
Second, the signal scanner only adds events when source-backed information is available. It intentionally avoids generating synthetic market events.
Third, pattern detection currently uses relatively straightforward grouping logic: repeated company + event-type combinations are surfaced as patterns.
Finally, historical evidence does not automatically prove causation.
If two competitor events occur close together, RadarX can identify the relationship as an observation, but that does not mean one event caused another.
These limitations are important because the goal is not to make the system sound more certain than its evidence allows.
The core idea
Competitive intelligence is cumulative.
A pricing change matters differently when it is the first pricing change in a year versus the third related pricing change in six months.
A product update means something different when it appears alongside previous delivery, pricing, and customer-feedback signals.
That's the problem RadarX is designed to address.
Instead of asking:
"What happened?"
RadarX tries to help answer:
"What does this event mean in the context of what we already remember?"
And that is where persistent agent memory becomes useful.
Final architecture
The complete RadarX intelligence loop can be summarized as:
MARKET EVENTS
│
▼
┌─────────────────┐
│ RETAIN │
│ Hindsight Bank │
└────────┬────────┘
│
▼
USER QUESTION ──► RECALL
│
▼
Historical Evidence
│
▼
REFLECTION
│
┌────────┴────────┐
│ │
Sufficient Insufficient
Evidence Evidence
│ │
▼ ▼
Intelligence Evidence Gap
Output + More Data
│
▼
Recommendation
+ Limitations
RadarX's core principle is simple:
Today's competitive signal should not have to forget yesterday's evidence.
Project
RadarX is built with Streamlit, Python, Hindsight persistent memory, and an optional Groq-based signal-scanning layer.
GitHub:
https://github.com/vadigellayaswanth/Competitive-Intelligence-Agent.git
Hindsight:
https://github.com/vectorize-io/hindsight
The project focuses on one question:
What happens when a competitive-intelligence agent can actually remember?



Top comments (0)