DEV Community

Cover image for How I Traced Investigation Context Across Incidents With Hindsight
DESAR DEV RAJEEV
DESAR DEV RAJEEV

Posted on

How I Traced Investigation Context Across Incidents With Hindsight

# How I Traced Investigation Context Across Incidents With Hindsight

A security alert is rarely interesting in isolation. The useful question is usually, “Have I seen this pattern before, and what happened when I did?”

That question became the design constraint behind CyberMemory AI. I built the system around a simple idea: an incident-response agent should not have to rediscover the same investigation every time an alert arrives. It should be able to retain what happened, retrieve the parts that matter to a new alert, and use that history when deciding what to investigate next.

CyberMemory
 AI takes security alerts such as repeated failed logins, suspicious activity, or a potential malware detection and turns them into an investigation workflow.

The application has three important paths.

First, an alert enters the investigation layer. The agent normalizes the event, identifies the important entities and signals, and asks what context is available from previous incidents.

Second, the memory layer searches historical incidents. I use Hindsight as persistent memory rather than treating the language model's context window as the application's memory. Hindsight exposes three core operations that fit this workflow well: retain information, recall relevant memories, and reflect over those memories to produce a contextual answer. Its recall path combines multiple retrieval strategies, including semantic, keyword, graph, and temporal retrieval.

Third, the investigation result is presented as a report. The report includes a summary of the alert, related incidents, possible causes, and recommended investigation or remediation steps. When the incident is resolved, I write the outcome back to memory.

That last step is the part that changes the system over time.

The application is not simply doing retrieval-augmented generation over a static knowledge base. The incident itself becomes part of the future investigation context.

The important boundary: an alert is not a memory

One of the design decisions I cared about early was separating the current alert from historical experience.

Suppose I receive this:

{
  "type": "failed_login",
  "user": "alex",
  "source_ip": "10.20.14.8",
  "attempts": 17,
  "window": "4 minutes"
}
Enter fullscreen mode Exit fullscreen mode

The alert is evidence about something happening now. It should not automatically become a conclusion.

I first turn it into an investigation query that contains the signals that matter:

query = f"""
Investigate this security alert.

Type: {alert.type}
User: {alert.user}
Source IP: {alert.source_ip}
Attempts: {alert.attempts}
Time window: {alert.window}

Find previous incidents with similar authentication behavior,
including their root causes, investigation steps, and outcomes.
"""
Enter fullscreen mode Exit fullscreen mode

That distinction sounds small, but it keeps the memory layer from becoming a dumping ground for raw alerts. Hindsight's retain operation is designed to process content into structured memories rather than merely storing text verbatim, which is useful here because an incident contains facts, observations, entities, and experience that may be useful in different future investigations.

Why persistent memory changed the investigation flow

Without persistent memory, an LLM-based incident assistant has a familiar failure mode: every investigation starts from the same blank page.

The model may know general security practices. It may even produce a reasonable checklist. But it does not automatically know that our environment has already seen this exact combination of signals three times, that two cases were caused by a misconfigured identity provider, and that another was a compromised credential.

That information lives in incident history.

I use a dedicated Hindsight memory bank for the incident-response context:

from hindsight_client import Hindsight

memory = Hindsight(
    base_url=HINDSIGHT_URL,
    api_key=HINDSIGHT_API_KEY,
)

BANK_ID = "cybermemory-incidents"
Enter fullscreen mode Exit fullscreen mode

For a new investigation, I can recall historical experience before asking the reasoning layer to produce its report:

memories = memory.recall(
    bank_id=BANK_ID,
    query=query,
)

context = "\n".join(
    result.text
    for result in memories.results
)
Enter fullscreen mode Exit fullscreen mode

This is where Hindsight is more useful to me than simply putting embeddings into a vector database and doing one nearest-neighbor search.

Security incidents have relationships that are not purely semantic.

A previous incident may involve the same user, source IP range, hostname, malware family, authentication provider, or sequence of events. It may also matter because it happened recently, or because it represents a repeated operational pattern. Hindsight's multi-strategy recall is designed to combine semantic, keyword, graph, and temporal signals before returning a ranked result set.

For incident response, that combination is a much better fit than assuming “similar wording” means “similar incident.”

I also wanted the reasoning to stay grounded

Retrieval gives me evidence. It does not, by itself, give me an investigation.

That is where Hindsight's reflect operation becomes interesting.

Instead of manually concatenating every recalled memory into a huge prompt, the system can ask Hindsight to reason over the memory bank:

report = memory.reflect(
    bank_id=BANK_ID,
    query=f"""
    Analyze this security alert using relevant historical incidents.

    Current alert:
    {query}

    Explain:
    1. Which previous incidents are relevant
    2. What patterns are repeated
    3. What possible causes should be investigated
    4. What evidence should be collected next
    5. What remediation actions were effective previously
    """
)

return report.text
Enter fullscreen mode Exit fullscreen mode

The distinction between recall and reflect is useful architecturally. Recall gives the application retrieved memory results; reflect synthesizes an answer from the memory context. Hindsight documents reflect as an agentic reasoning operation that searches memory and produces a grounded response.

I use that distinction deliberately.

When I need to show an analyst the underlying historical evidence, I want recall. When I need a coherent investigation narrative, I want reflection.

Closing the loop after remediation

The most important write happens after the alert is resolved.

An incident record contains more than the initial detection. It has the root cause, investigation path, actions taken, and outcome.

I retain that resolved experience as a single incident context:

incident_memory = f"""
Incident ID: {incident.id}
Alert type: {incident.alert_type}
Observed behavior: {incident.summary}

Root cause:
{incident.root_cause}

Investigation:
{incident.investigation_steps}

Actions taken:
{incident.actions_taken}

Outcome:
{incident.outcome}
"""

memory.retain(
    bank_id=BANK_ID,
    content=incident_memory,
    context="resolved security incident",
    metadata={
        "incident_id": incident.id,
        "alert_type": incident.alert_type,
    },
)
Enter fullscreen mode Exit fullscreen mode

This is the part I consider the real feedback loop.

If a failed-login incident is eventually confirmed as an identity-provider synchronization problem, I do not want the next investigation to see only the original alert. I want it to have access to the resolution.

That makes the historical record operationally useful.

I also keep the incident identity and metadata separate from the prose itself. The prose gives Hindsight enough context to extract meaningful memories, while the metadata gives the application stable identifiers for tracing the result back to the incident record.

A concrete investigation

Imagine the first incident contains 17 failed logins from one source address. The analyst investigates the identity provider and finds that a synchronization job repeatedly retried an expired credential. The incident is resolved by fixing the service credential and restarting the affected synchronization process.

Two weeks later, another alert arrives with a similar pattern.

A stateless assistant might produce a generic list:

  • check whether the account is compromised
  • inspect source IP reputation
  • review authentication logs
  • reset credentials
  • check for lateral movement

Those are reasonable checks, but they are not particularly specific.

With persistent incident memory, the investigation can surface the earlier resolution and say, in effect: this pattern resembles a previously resolved authentication incident; that incident was traced to a synchronization credential rather than interactive credential abuse; inspect the corresponding identity-provider logs and service-account activity before treating this as an account compromise.

That is the behavior I was aiming for.

The memory does not replace evidence collection. It changes the order in which I look for evidence.

The dashboard is deliberately boring

The dashboard is organized around the things an incident responder actually needs to see: current alerts, investigation reports, resolved incidents, and incident history.

I did not want the UI to become another abstraction over the investigation. A responder should be able to open an alert, see the current analysis, inspect related historical incidents, and understand what was eventually recorded as the resolution.

The most useful screen is therefore not “AI output.” It is the chain connecting:

alert → investigation → historical context → resolution → retained experience

That chain is what makes the system inspectable.

It also makes mistakes easier to correct. If the agent recommends the wrong next step, I can trace whether the problem came from the alert normalization, historical retrieval, or reasoning over the retrieved context.

What I learned

1. Memory quality matters more than memory volume

My first instinct was to retain everything. That is the wrong abstraction.

An incident contains lots of operational noise: repeated log lines, temporary identifiers, routine status messages, and details that are irrelevant outside the original investigation.

The useful memory is the part that explains what happened and why.

That is why I treat a resolved incident as a structured experience with context, cause, actions, and outcome rather than blindly storing every event.

2. Retrieval should be part of the investigation, not an afterthought

It is tempting to generate the LLM report first and attach memory afterward.

I found the opposite architecture more useful.

The investigation should start by asking what the system already knows. Historical context can change which hypotheses are worth investigating, which logs deserve attention, and which remediation actions have already worked.

Memory therefore belongs before reasoning, not after it.

3. recall and reflect solve different problems

I do not treat them as interchangeable.

recall is useful when I need evidence: which previous incidents were related, what facts were stored, and what context was attached.

reflect is useful when I need synthesis: given the available experience, what should I understand about this case?

Keeping those responsibilities separate also makes the application easier to debug.

4. The resolution is often more valuable than the alert

An alert tells me what the detector noticed.

A resolved incident tells me what the organization learned.

That distinction is why the write-back path is a first-class part of CyberMemory AI rather than an optional analytics feature.

5. Persistent memory changes what an agent can be

The interesting part of an incident-response agent is not that it can summarize a log file. LLMs are already good at summarization.

The harder engineering problem is continuity.

I want the system to be able to say, “We have seen something like this before,” and then make that statement useful by pointing to what happened, what was learned, and what should be checked now.

That is the role I am using Hindsight for.

The Hindsight GitHub repository and Hindsight documentation are useful references for the underlying memory model and API. The broader idea is also captured well in Vectorize's explanation of agent memory for persistent context.

For CyberMemory AI, persistent memory is not a feature bolted onto an otherwise stateless chatbot. It is the mechanism that connects one incident to the next.

That connection is where the system becomes substantially more useful: not because it remembers everything, but because it can remember the parts of previous investigations that change how I investigate the next one.

Top comments (0)