DEV Community

Yaswanth krishna Vadigella
Yaswanth krishna Vadigella

Posted on

RadarX: Building Competitive Intelligence That Actually Remembers

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.
Enter fullscreen mode Exit fullscreen mode

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          │
                    └─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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.",
]
Enter fullscreen mode Exit fullscreen mode

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,
    )
Enter fullscreen mode Exit fullscreen mode

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,
    },
)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Each row contains fields such as:

timestamp
company
event_type
title
description
impact_score
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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,
}
Enter fullscreen mode Exit fullscreen mode

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",
)
Enter fullscreen mode Exit fullscreen mode

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,
)
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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",
    ],
}
Enter fullscreen mode Exit fullscreen mode

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,
)
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode

and ignores groups with fewer than two events:

if len(group) < 2:
    continue
Enter fullscreen mode Exit fullscreen mode

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,
    }
)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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,
)
Enter fullscreen mode Exit fullscreen mode

If evidence is insufficient, the UI warns the user:

RadarX does not have enough relevant evidence
in Hindsight memory to answer this question.
Enter fullscreen mode Exit fullscreen mode

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": []}
Enter fullscreen mode Exit fullscreen mode

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."
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. Store useful information.
  2. Retrieve relevant information.
  3. Distinguish relevant evidence from irrelevant evidence.
  4. Recognize when evidence is insufficient.
  5. Separate facts from interpretation.
  6. 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
Enter fullscreen mode Exit fullscreen mode

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)