Start deploying
·8 min read·a2a cloud

Early Investigation: Dynamic Memory for Agents

A technical look at a schema-aware semantic memory system where Pydantic models, tool calls, agent traces, vector search, graph edges, and feedback loops become trainable memory.

agentsmemorysemantic grapha2amcpvector databasegraph databasemachine learningpydantic

Early Investigation: Dynamic Memory for Agents

This is an early investigation into a different kind of agent memory.

Most agent memory systems start with a simple idea: take some text, embed it, store it in a vector database, and retrieve it later. That works for notes, documents, and loose recall. But it breaks down when agents need to understand structured systems: users, invoices, tool calls, API responses, schemas, field values, intermediate plans, failures, retries, and the relationships between all of them.

The memory we are exploring here is not just a vector store. It is a schema-aware semantic graph that can grow, reinforce, prune, and retrain itself over time.

The long-term goal is direct:

Every agent action, subagent delegation, tool call, model object, and user correction should become training signal for a dynamic memory system.

That means memory is not a passive archive. It becomes part of the learning loop.

The Core Idea

Instead of storing one record as one blob, the system explodes structured data into layers of neurons.

A Pydantic model is not only saved as JSON. It becomes a small semantic graph:

TypeScript
ArtifactNeuron
  the full object

FieldNeurons
  one neuron per field and nested field

MetadataNeurons
  model type, schema id, tags, source

TypeNeurons
  model type and field type definitions

SummaryNeuron
  compact object summary

Value / Span / Meaning / Relation Neurons
  generated subgraph around field values

SessionNeurons
  query and feedback events

This gives the memory system multiple surfaces to retrieve from.

A query like customer email country support representative can hit field names, values, summaries, object types, learned co-activation edges, or generated meaning nodes. The system does not have to guess whether the whole document vector happens to be close enough.

The Storage Stack

The current prototype uses three backing systems:

Text
MinIO
  stores the canonical JSON document with metadata and JSON-LD

Qdrant
  stores embeddings and searchable payloads

Memgraph
  stores neurons, synapses, dynamic links, sessions, hits, and graph structure

Each saved object goes through a FileWritingSchema document format:

TypeScript
metadata
  title, tags, source, schema id, schema hash

json_ld
  identity, type, context, conformsTo

payload
  the actual model data

That document is written to MinIO. Its vector representation is written to Qdrant. Its exploded neuron graph is written to Memgraph.

The system currently uses local BGE embeddings for semantic search, with a hash embedder available for fast smoke tests.

Why Pydantic Matters

Pydantic models give the system a strong semantic starting point.

A record is not just arbitrary JSON. It has a model name, field names, field types, schema metadata, nested paths, and validation rules.

For example:

Python
class InvoiceRecord(BaseModel):
    invoice_id: int
    customer_id: int
    invoice_date: str
    billing_country: str | None
    total: float

The memory graph can derive several layers from this:

YAML
InvoiceRecord artifact
InvoiceRecord.invoice_id field
InvoiceRecord.customer_id field
InvoiceRecord.invoice_date field
InvoiceRecord.total field
type: InvoiceRecord
type: InvoiceRecord.customer_id
summary: invoice fields and preview

That gives retrieval more structure than a flat embedding.

Query Flow

A plain-language query follows this path:

Python
human query
  -> embed with BGE
  -> retrieve candidate neurons from Qdrant
  -> generate query-time dynamic edge proposals
  -> expand through Memgraph neighbors
  -> collect hit/impression stats
  -> compute base score
  -> rerank with small MLP router
  -> write a session neuron
  -> wait for feedback

The base score is intentionally simple:

Text
base = 0.55 * vector_score
     + 0.30 * graph_score
     + 0.15 * hit_rate

Then the trained router can override that with learned behavior:

Text
final = 0.70 * router_score + 0.30 * base

The router sees features such as:

TypeScript
query text
neuron layer
neuron type
model type
schema id
field path
field name
field type
vector score
graph score
hit rate
impressions
hits
value text

This is deliberately small. The point is not to build a giant model first. The point is to create a memory shape that produces useful training examples continuously.

Feedback Is Memory Training

The CLI lets a human query the system and then provide feedback:

Text
brain> customer email country support representative
brain> /accept 1 2
brain> /feedback 1 2 r 5 6

That feedback updates the graph:

Text
accepted neurons get hits
returned neurons get impressions
accepted pairs get CO_ACTIVATED edges
query-specific dynamic links get reinforced
rejected relationships get penalized
router training examples are exported

A query is therefore not just a read. It is an event that can become training data.

This matters for agents because future traces can feed the same loop:

Text
agent plan
subagent delegation
tool call
API response
file edit
command output
test result
user correction

Every step can become a session. Every session can become examples. Every example can tune retrieval and ranking.

Dynamic Edges Instead of Hardcoded Edges

Traditional knowledge graphs often rely on static edges:

Text
Customer HAS_INVOICE Invoice
Invoice HAS_LINE InvoiceLine
InvoiceLine LINE_TRACK Track

Those edges are useful, but they are also brittle. They require manual schema knowledge or hardcoded foreign-key logic.

This investigation takes a different approach: generate query-time edge candidates and cache them by query type.

For example, when a query touches customers, invoices, and tracks, the system can propose dynamic links between candidate fields that look related by field names, values, schema context, and query overlap. Those links are not permanent truth. They are hypotheses.

YAML
DYNAMIC_QUERY_LINK
  query_type: sales_path
  confidence: 0.72
  reason: value_match + field_overlap + cross_model
  impressions: 3
  hits: 1

If users accept results that depend on those links, the links get stronger. If they are ignored or rejected, they can decay and eventually be pruned.

This is the key distinction:

Edges are not only authored. They are learned through use.

The Value Subgraph Problem

A major discovery came from a simple query:

Text
album_id 176

A pure semantic search path finds “album-ish” things, but it does not reliably understand that this looks like a field/value intent.

The tempting fix is an exact payload lookup. But that is cheating if the goal is dynamic memory. It bypasses the learned memory graph.

So the architecture moved toward adaptive value subgraphs.

A field value is not treated as one scalar. It can generate as many supporting neurons as it needs:

Python
FieldNeuron
  album_id = 176

ValueNeuron
  raw field value

ValueNeuron
  field-scoped value candidate

MeaningNeuron
  candidate meaning from model + field + schema + value

For long strings, the value can create a richer subgraph:

Text
FieldNeuron
  note = "Paid 3 days after 9/11 after invoice #42"

SpanNeuron
  text chunk

RelationNeuron
  "3 days after 9/11"

MeaningNeuron
  candidate interpretation of the field value

The rule is:

Never destructively normalize. Always annotate.

The raw text stays intact. Generated neurons sit beside it as candidate interpretations.

Why Not Just Use Heuristics?

A hardcoded normalizer can turn "176" into 176. It can parse dates. It can detect emails.

But it cannot know what a value means in context.

Text
176 as album_id
176 as milliseconds
176 as invoice total
176 as quantity

These should not collapse into one global value. Meaning depends on schema, field, neighboring values, query context, and feedback history.

So the prototype stores generated candidates with metadata rather than claiming truth:

YAML
generated: true
generator_model: adaptive-value-subgraph
generator_version: v1
source_field_id: ...
confidence: 0.45
ttl_policy: prune_if_unreinforced

In the current implementation, the generator is still simple. The architectural direction is that this generator becomes an ML layer that proposes spans, meanings, canonical candidates, and relation candidates. The graph stores those proposals, and feedback determines which ones survive.

Pruning Generated Memory

Generated memory can easily become noise.

So every generated neuron needs lifecycle metadata:

Text
confidence
impressions
hits
source_field_id
generator_model
generator_version
last_seen
ttl_policy

The pruning rule is behavior-driven:

Text
keep if useful
prune if low-confidence, seen enough, and never reinforced

In practice:

SQL
low confidence + impressions + no hits -> delete neuron + delete vector + detach graph node

This lets the memory self-size:

Text
simple scalar -> small subgraph
rich text -> larger subgraph
unused generated detail -> pruned
useful generated detail -> reinforced

The Current Brain Shape

The current prototype has these layers:

TypeScript
artifact
field
metadata
type
summary
value
span
meaning
relation
session

And these major relation types:

Text
CONTAINS
HAS_FIELD
HAS_METADATA
INSTANCE_OF
HAS_VALUE
HAS_SPAN
HAS_MEANING
HAS_RELATION
DERIVED_FROM
DESCRIBES_VALUE
CO_ACTIVATED
DYNAMIC_QUERY_LINK
RETURNED_WITH
ACCEPTED_WITH
REJECTED_WITH

The important thing is not the exact names. The important thing is the separation of concerns:

Text
raw data
schema structure
value annotations
meaning hypotheses
query sessions
learned behavior

Why This Matters for Agents

Agents generate exactly the kind of data this architecture wants.

A future A2A agent runtime can emit memory events for:

Text
agent invocation
subagent call
tool selection
tool arguments
tool result
file written
command executed
test passed
test failed
human accepted output
human rejected output

Each event can be represented as a Pydantic model. Each model can be exploded into neurons. Each query over those neurons can create sessions. Each user or agent evaluation can produce feedback. Over time, the memory learns which schemas, fields, values, tools, and traces matter for which tasks.

That is the larger direction:

Text
agent traces -> structured neurons -> retrieval -> feedback -> router training -> better future retrieval

The memory becomes a shared learning substrate for agents and subagents.

What Is Still Early

This is not a finished architecture.

Several open problems remain:

  • The value generator should become a learned span and meaning proposal model.
  • Training queries need to explicitly teach the router that generated value, span, meaning, and relation neurons can be useful.
  • Query understanding needs to learn intent types such as field/value lookup without bypassing the graph.
  • Dynamic links need better decay, audit, and confidence calibration.
  • Long-running agent traces need compression so memory grows intelligently.
  • Evaluation needs real task datasets, not only synthetic or small relational datasets.

The prototype already shows the shape, but the learning loop needs more data.

The Thesis

The thesis is simple:

Agent memory should not be a folder of embeddings. It should be a dynamic semantic graph trained by use.

Schemas provide structure.

Embeddings provide recall.

Graph edges provide context.

Generated value subgraphs provide interpretability.

Feedback provides learning signal.

Pruning keeps the memory honest.

And agent traces provide the training stream.

This is an early investigation, but the direction is clear: future agent systems will need memory that can explain itself, reshape itself, and learn from every tool call, subagent handoff, and human correction.

That is dynamic memory.

discussion

0 comments

No comments yet.
Checking session