Recording Reasoning Traces

In the previous lesson, you learned the reasoning memory schema — how ReasoningTrace, ReasoningStep, and ToolCall nodes connect, and how cross-memory relationships tie all three layers together in a single graph. Now you will use the Pydantic AI integration to record those traces automatically.

In this lesson, you will learn how to use create_memory_tools() and record_agent_trace() to record a complete reasoning trace with minimal code.

Using the Pydantic AI integration

create_memory_tools() returns 3 pre-built agent tools that give a Pydantic AI agent access to memory search, preference saving, and preference recall. The agent can call these tools to store and retrieve memory as part of its normal reasoning process.

python
Create memory tools and attach them to a Pydantic AI agent
import os
from neo4j_agent_memory import MemoryClient, MemorySettings
from neo4j_agent_memory.config import Neo4jConfig, EmbeddingConfig
from neo4j_agent_memory.integrations.pydantic_ai import create_memory_tools
from pydantic_ai import Agent

async with MemoryClient(settings) as memory:
    # Returns 3 ready-to-use agent tools: search_memory, save_preference, recall_preferences
    tools = create_memory_tools(memory)

    agent = Agent(
        model="openai:gpt-4o-mini",
        system_prompt="""You are a helpful assistant with persistent memory.
        Use memory tools to recall past conversations and preferences.""",
        tools=tools
    )

This creates a Pydantic AI agent with the 3 memory tools registered. When the agent runs, it can call search_memory, save_preference, and recall_preferences to interact with the memory graph.

Recording a trace from an agent run

After the agent runs, pass the result to record_agent_trace() to write the full trace to Neo4j. record_agent_trace() takes the ReasoningMemory instance, a session ID, and the completed RunResult:

python
Run the agent and record the reasoning trace
from neo4j_agent_memory.integrations.pydantic_ai import create_memory_tools, record_agent_trace

async with MemoryClient(settings) as memory:
    tools = create_memory_tools(memory)
    agent = Agent(model="openai:gpt-4o-mini", tools=tools)

    # Run the agent
    result = await agent.run("What can you tell me about Jessica Norris?")
    print(result.data)

    # Record the trace from the completed run
    trace = await record_agent_trace(
        memory.reasoning,
        "user_123",
        result,
        task="Answer user question"
    )

record_agent_trace() inspects the completed RunResult, extracts every tool call and its result, and writes ReasoningTrace, ReasoningStep, and ToolCall nodes to Neo4j in a single pass. It returns the completed ReasoningTrace object.

Understanding what gets written to Neo4j

After calling record_agent_trace(), the following reasoning nodes will exist in your database:

  • One ReasoningTrace node for the agent run

  • One ReasoningStep per reasoning iteration

  • One ToolCall per tool the agent invoked, with full arguments and results

If your code also calls memory.short_term.add_message() to store the conversation — for example, before and after the agent run — you will additionally have Conversation and Message nodes linked to the trace. record_agent_trace() itself only writes the reasoning layer.

Using the low-level reasoning API

record_agent_trace() is a convenience wrapper around the four low-level methods. Use the low-level API when you need manual control — for example, when integrating with a framework other than Pydantic AI, or when you want to record a trace for a non-agent workflow:

python
Record a reasoning trace manually using the low-level API
from neo4j_agent_memory import MemoryClient, MemorySettings
from neo4j_agent_memory.config import Neo4jConfig

async with MemoryClient(settings) as memory:
    # 1. Start a trace — creates the ReasoningTrace node
    trace = await memory.reasoning.start_trace(
        task="Evaluate credit limit for Jessica Norris",
        session_id="user_123"
    )

    # 2. Record a reasoning step
    step = await memory.reasoning.add_step(
        trace_id=trace.id,
        thought="Retrieving customer entity from long-term memory",
        action="search_entities"
    )

    # 3. Record a tool call within the step
    await memory.reasoning.record_tool_call(
        step_id=step.id,
        tool_name="search_entities",
        arguments={"query": "Jessica Norris", "limit": 5},
        result={"entities": ["Jessica Norris (EntityPerson)"]},
        status="success"
    )

    # 4. Complete the trace with an outcome
    await memory.reasoning.complete_trace(
        trace.id,
        outcome="Approved — risk score within threshold",
        success=True
    )

start_trace() returns a trace object whose id you pass to subsequent calls. record_agent_trace() calls all four of these automatically, wrapping your agent run so every tool call becomes a ToolCall node without any extra code.

Check your understanding

Starting a Reasoning Trace

Which neo4j-agent-memory method starts recording a new reasoning trace?

  • memory.short_term.add_message()

  • memory.reasoning.record_step()

  • memory.reasoning.start_trace()

  • memory.long_term.add_entity()

Hint

Reasoning traces are recorded using the memory.reasoning.* namespace. A trace must be started before steps can be recorded inside it.

Solution

The correct answer is memory.reasoning.start_trace().

memory.reasoning.start_trace() creates the ReasoningTrace node in Neo4j and returns a trace object whose ID you pass to subsequent calls like add_step() and complete_trace(). memory.reasoning.record_step() does not exist — the correct method name is add_step(). memory.long_term.add_entity() stores long-term memory entities and has no effect on reasoning traces.

Summary

In this lesson, you learned the key integration components:

  • create_memory_tools(memory) — returns 3 Pydantic AI tools (search_memory, save_preference, recall_preferences) that give the agent access to the memory graph

  • record_agent_trace(memory.reasoning, session_id, result) — inspects a completed RunResult and writes the full trace (ReasoningTrace, ReasoningStep, ToolCall nodes) to Neo4j

  • Single agent run — after one run, all three memory layers are populated and cross-linked in Neo4j

In the next lesson, you will query the reasoning trace to understand what the agent did and why.

Chatbot

How can I help you today?

Data Model

Your data model will appear here.