Skip to main content
dagraph gives agent nodes two observability primitives: memory scopes so an agent can remember context from previous runs, and structured traces so you can see exactly what tokens were spent and where time went. Both are designed to work out of the box with no extra configuration, with opt-in persistence for memory and opt-in OTel export for traces.

Memory scopes

How memory works

When you add a memory: field to an agent node, dagraph loads any previous conversation messages for that scope and prepends them to the agent’s context before the LLM call. After the call completes, the new exchange is appended to the scope. This gives the agent a rolling conversation history across calls — it sees what it said before, not just the current prompt. Memory is keyed by scope name, not by run ID. Two separate runs that use the same scope name share the same history.

Short form (in-process)

The simplest way to use memory is to pass a bare string as the scope name. This keeps the history in memory for the duration of the current run only — it does not survive a process restart or a new agentgraph run invocation.

Long form (persistent)

Set persist: true to store conversation history in a local SQLite database. The history survives across runs, so the agent picks up exactly where it left off the next time you run the DAG with the same scope name.
string | object
A bare string is shorthand for {scope: "...", persist: false}. Use the object form when you need persistent memory across runs.

Example: recurring research agent

A recurring research agent that remembers which topics it has already covered is a common use case for persist: true. Each time you trigger the DAG, the agent sees the full history of previous sessions and avoids repeating itself:
Memory is tied to the scope name, not to a specific DAG or run. If two different DAGs use scope: "shared-thread", they share the same conversation history. Use unique scope names to keep contexts separate.

Run tracing

What gets traced

Every agentgraph run writes a trace.jsonl file in the run directory (runs/<run_id>/trace.jsonl). The file uses JSON Lines format — one JSON object per line — following the OpenTelemetry GenAI semantic conventions. Each run produces spans with these names: Span attributes include the model name, input tokens, output tokens, cache tokens, and wall-clock duration. All string values — including prompts and outputs — are automatically redacted before writing, so secrets that appear in prompts do not leak into trace files.

Viewing traces

Use agentgraph inspect <run_id> to see a summary of token counts and timing per node:
The raw trace.jsonl file contains the full detail. Here is an example of what a dag.node span looks like:

Integrating with OTel backends

dagraph’s trace format follows OTel GenAI semantic conventions. The trace.jsonl file produced in each run directory can be ingested by any OTel-compatible observability backend such as Langfuse, Braintrust, or Langsmith. Load the file using your backend’s import tool or script the upload against its API.
The trace.jsonl file is always written for every run. You can import it into any observability tool after the fact without re-running the DAG.
Secrets in prompts and model outputs are automatically redacted before being written to trace.jsonl or sent to an OTel backend. dagraph detects common secret patterns (API keys, tokens, bearer credentials) and replaces them with [REDACTED].