Back to Theory
Technical Deep Dive13 min read · May 14, 2026

Closing the Loop: How Feather DB Turns LLM Outputs Into Persistent Context

Closing the loop is the architectural step most teams skip. Feather DB makes it a one-line call. This post is the implementation guide — what to write back, what edges to attach, what signals to capture, and how to verify the loop is working.

F
Feather DB Engineering
Engineering Team

Closing the Loop: How Feather DB Turns LLM Outputs Into Persistent Context

Technical Deep Dive · Context Engine Loop Series · May 2026


The Step Most Teams Skip

Of the four phases of the Context Engine Loop — Read, Reason, Update, Decay — the one production systems most often skip is Update. The reason is mundane: it is easy to wire retrieval, easy to call an LLM, easy to display the output. Writing the output back into the store, with the right edges, with the right metadata — that takes a few hours of work that nobody schedules.

The cost of skipping it is also mundane: the system never learns from itself. The fix is small, and this post is the implementation guide.

The Minimum Write-Back

The smallest useful Update step writes the agent's output back as a new node with edges to its inputs:

def close_loop(db, query_vec, agent_output, input_node_ids,
               edge_type="derived_from"):
    output_vec = embed(agent_output)
    output_id = db.insert(
        vector=output_vec,
        payload={"text": agent_output, "kind": "agent_output"},
        modality="text",
    )
    for src_id in input_node_ids:
        db.add_edge(src_id, output_id, edge_type=edge_type)
    return output_id

Four lines of work. The output now lives in the store. Future retrievals can surface it. The graph topology has densified by one node and N edges.

What to Write Back, What to Skip

Not every agent call is worth persisting. The Update phase needs a filter:

  • Write back: completed decisions. The agent finished a coherent unit of reasoning — a strategy brief, a creative recommendation, a support resolution. These are durable artifacts.
  • Write back: novel claims. The agent made an explicit claim that did not exist in the inputs. Capturing these grows the corpus past what was originally ingested.
  • Skip: intermediate scratch. Step-by-step reasoning that did not converge. Drafts the agent itself revised. Tool-call outputs that are already retrievable from the tool.
  • Skip: pure echoes. Outputs that paraphrased an input without adding novelty. They will inflate the corpus without adding signal.

The filter is a few lines of code, usually. A simple heuristic — was this output longer than 200 tokens, did the agent mark it as a final answer — captures most of the right cases.

Choosing Edge Types

Use a small, deliberate edge vocabulary at write time. Three types cover most production needs:

  • derived_from — the output used this input as a basis.
  • responds_to — the output exists because of this input (a brief, a question).
  • contradicts — the output disagrees with this input.

Resist the urge to invent edge types per use case. The fewer types, the more queryable the graph.

Capturing Downstream Signal

The richest version of the loop captures signal after the agent's output produces a downstream consequence:

  • The user clicked. The output drove behavior.
  • The campaign launched. The brief was used.
  • The ticket was resolved. The response worked.

Each of these is a signal that the input nodes contributed to a real outcome. Capture it by updating the importance multiplier or bumping the recall count on the input nodes:

def reinforce_inputs(db, input_node_ids, signal_strength=1.0):
    for nid in input_node_ids:
        node = db.get(nid)
        node.recall_count += 1
        node.importance = min(3.0, node.importance + 0.1 * signal_strength)
        db.update(node)

Now the loop closes with feedback. Inputs that produced real outcomes get stickier and more important. Inputs that produced ignored outputs decay normally.

Verifying the Loop Works

Three observable signals confirm the loop is running:

1. Node Count Grows With Usage

If your store has the same node count after a week of production traffic as it did at the start, the Update phase is not wired. The corpus should accrete on a productive workload.

2. Edge Density Increases

Average outgoing edges per node should rise over time. Track this metric. If it plateaus, your edge-writing logic is missing categories.

3. Quality on Fixed Evaluation Drifts Upward

Hold out a small evaluation set. Run it at week 0, week 4, week 12. Quality scores should improve as production usage accumulates. If they don't, either the loop is not closing or the signal-capture phase is too weak.

A Worked Example

A marketing AI agent generates a creative brief. The full loop in Feather DB:

# Phase 1 — Read
chain = db.context_chain(
    query_vec=embed("Q3 brand-x campaign brief"),
    k=5, hops=2,
    edge_types=["derived_from", "responds_to"],
)

# Phase 2 — Reason
brief = llm.generate(prompt_with(chain))

# Phase 3 — Update
input_ids = [n.id for n in chain.nodes]
output_id = close_loop(db, embed(brief), brief, input_ids,
                       edge_type="derived_from")

# Phase 4 — Decay (signal capture, post-launch)
if campaign_performed_well(brief):
    reinforce_inputs(db, input_ids, signal_strength=1.5)
    db.bump_importance(output_id, +0.3)

Six lines of orchestration. The system now learns from its own work. Every brief produced strengthens the next one. The Context Engine Loop is closed.

The Operational Lesson

Closing the loop is a few hours of work, not a rewrite. The architectural primitives are already in Feather DB — insert, add_edge, update — and the orchestration is application code. The leverage is asymmetric: the cost is low, the compounding is high, the alternative is permanent genericness.

Build the loop. Verify the three signals. Watch the system get specific to your business in a way no static memory can.


End of the Context Engine Loop series. The full architecture is documented at getfeather.store/docs.