Building context for prompts
Brain has no build-context verb — you assemble a prompt block yourself from recall output. This shows the pattern, with token budgeting and provenance.
- A Brain client with credentials.
- Memories already in the system for the active scope.
- A target LLM that accepts a system prompt.
- Familiarity with Recall.
A buildContext helper that recalls memories for a cue, formats them into a Markdown block sized to a token budget, and returns both the block and its source memory ids for provenance.
There is no context verb
Older drafts referenced a context.build() call and named "recipes". Neither exists. Brain's read surface is exactly one verb — recall — which returns a membership verdict over memories (see Recall). Assembling those memories into a prompt is application work, and it belongs in your code because only you know your prompt layout, model, and token budget.
The pattern is always the same: recall → format → inject.
Step-by-step
import { BrainHttpClient } from "@brain-db/sdk";
const brain = new BrainHttpClient({ apiKey: process.env.BRAIN_API_KEY! });
const res = await brain.recall({ query: userQuery, max_results: 8 });import os
from brain_db_sdk import BrainHttpClient
brain = BrainHttpClient(os.environ["BRAIN_API_KEY"])
res = brain.recall(query=user_query, max_results=8)use brain_db_sdk::BrainHttpClient;
use brain_db_sdk::http::RecallInput;
let brain = BrainHttpClient::new("https://api.arc-labs.ai", &std::env::var("BRAIN_API_KEY")?);
let res = brain
.recall(&RecallInput {
query: user_query.to_string(),
max_results: Some(8),
..Default::default()
})
.await?;If res.answer_kind is none, there is nothing to inject — skip the context block rather than injecting an empty one.
Render each hit as a bullet. A rough token budget is characters ÷ 4; drop the lowest-confidence hits until you fit. Keep the source ids alongside so you can attribute later.
function buildContext(memories: { text: string; memory_id: string; confidence: number }[], maxTokens = 1500) {
const sorted = [...memories].sort((a, b) => b.confidence - a.confidence);
const lines: string[] = [];
const sources: string[] = [];
let tokens = 0;
for (const m of sorted) {
const cost = Math.ceil(m.text.length / 4);
if (tokens + cost > maxTokens) break;
lines.push(`- ${m.text}`);
sources.push(m.memory_id);
tokens += cost;
}
const block = lines.length ? `## What I remember\n${lines.join("\n")}` : "";
return { block, sources, tokens };
}def build_context(memories, max_tokens=1500):
ordered = sorted(memories, key=lambda m: m.confidence, reverse=True)
lines, sources, tokens = [], [], 0
for m in ordered:
cost = (len(m.text) + 3) // 4
if tokens + cost > max_tokens:
break
lines.append(f"- {m.text}")
sources.append(m.memory_id)
tokens += cost
block = "## What I remember\n" + "\n".join(lines) if lines else ""
return {"block": block, "sources": sources, "tokens": tokens}use brain_db_sdk::http::MemoryHit;
struct Context {
block: String,
sources: Vec<String>,
tokens: usize,
}
fn build_context(memories: &[MemoryHit], max_tokens: usize) -> Context {
let mut ordered: Vec<&MemoryHit> = memories.iter().collect();
ordered.sort_by(|a, b| {
b.confidence
.partial_cmp(&a.confidence)
.unwrap_or(std::cmp::Ordering::Equal)
});
let mut lines: Vec<String> = Vec::new();
let mut sources: Vec<String> = Vec::new();
let mut tokens = 0usize;
for m in ordered {
let cost = m.text.len().div_ceil(4);
if tokens + cost > max_tokens {
break;
}
lines.push(format!("- {}", m.text));
sources.push(m.memory_id.clone());
tokens += cost;
}
let block = if lines.is_empty() {
String::new()
} else {
format!("## What I remember\n{}", lines.join("\n"))
};
Context { block, sources, tokens }
}Prepend the block to your base instructions, or (better for prompt caching) pass it as a separate context message so your system prompt stays stable across turns.
const { block, sources } = buildContext(res.memories);
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: SYSTEM_PROMPT },
...(block ? [{ role: "user" as const, content: `<context>\n${block}\n</context>` }] : []),
{ role: "user", content: userQuery },
],
});
log.info({ contextSources: sources }, "answered with brain context");Grouping by kind
If you want sections (facts, preferences, events) rather than a flat list, bucket the hits by their kind byte before formatting. Recall returns the kind on every hit; the enumeration is documented under GET /v1/memories. Keep the grouping in your code — it is presentation, and Brain has no opinion about it.
Log the source memory_ids alongside your own request id whenever you build a context block. When a model produces a surprising answer, the source list tells you exactly which memories it saw — and lets you GET /v1/memories/{id}/inspect to see how each was written.
Was this page helpful?
Recall
Recall is Brain's one read verb — it returns a membership verdict (Single / Many / None) over the caller's memory pool, never a fabricated answer.
Streaming
Real streaming lives on the wire client — the *Frames methods drain a verb's response frame-by-frame with cursors, cumulative counts, and isFinal. The HTTP client has none.