LeoWalker's picture
MCP source
33516f7 verified
|
Raw History Blame Contribute Delete
7.29 kB

React Agent

A lightweight LangChain ReAct agent for querying the Future of Education podcast knowledge base.

Overview

This agent implements the ReAct pattern (Reason β†’ Act/tool call β†’ Observe β†’ repeat) using langchain.agents.create_agent. It's intended for fast, tool-grounded Q&A over the local stores (Chroma + Neo4j).

Features:

  • Conversation memory via LangGraph's SQLite checkpointer (survives process restart)
  • Multi-provider support (OpenAI, Anthropic, Google, Fireworks)
  • Streaming tool calls and responses

When to use this vs deep_research_agent:

  • React Agent: Quick questions, single-topic lookups, testing tools
  • Deep Research Agent: Complex multi-part research, comprehensive reports, cross-episode synthesis

File Structure

File Purpose
graph.py Main agent using langchain.agents.create_agent
checkpointer.py SqliteSaver helper (gitignored .checkpoints/react_agent.sqlite)
prompts.py System prompt guiding agent behavior
configuration.py Settings (model, max iterations, etc.) with model registry
utils.py Helper functions for model creation and tool management
tools.py Re-exports shared tools (Chroma, Neo4j)
chat_cli.py Interactive terminal interface
__init__.py Package exports

Architecture

User Query
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ReAct Agent (create_agent)         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚ 1. Reason: What do I need?  β”‚    β”‚
β”‚  β”‚ 2. Act: Call a tool         │◄───┼─── Tools:
β”‚  β”‚ 3. Observe: Read result     β”‚    β”‚    β€’ search_knowledge_base (Chroma)
β”‚  β”‚ 4. Repeat or Answer         β”‚    β”‚    β€’ query_knowledge_graph (Neo4j)
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚    β€’ inspect_graph_schema
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    β”‚
    β–Ό
  Answer

Implementation Details

Dynamic Schema Loading

The agent dynamically loads the Neo4j graph schema at startup (in prompts.py) to minimize tool calls. This allows the agent to write Cypher queries immediately without needing an initial inspect_graph_schema call, reducing latency and cost.

Multi-Provider Support

Model creation is delegated to the shared factory in src/llm/factory.py, which supports multiple providers and validates the required API key env var for the chosen model.

Model names accept stable aliases (recommended) that are mapped to provider-specific IDs under the hood:

  • OpenAI: gpt-5, gpt-5-mini
  • Anthropic: claude-sonnet-4-20250514 (alias), plus internal IDs like claude-sonnet-4-5
  • Google Gemini: gemini-flash-latest (alias)

Model Configuration

Models are configured via the Configuration class which supports:

  • Model selection (default: gemini-flash-latest)
  • Max tokens (default: 4000)
  • Temperature (default: 0.0)
  • Timeout (default: 30 seconds)
  • Max iterations (default: 25)

Configuration can be overridden at runtime via RunnableConfig.

Usage

Interactive Chat

# Interactive chat (prints the full thread_id)
uv run python -m src.react_agent.chat_cli

Replay a thread after restart

Checkpoints are stored at .checkpoints/react_agent.sqlite (gitignored; override with REACT_AGENT_CHECKPOINT_PATH). The CLI prints the full thread_id. Pass it back on the next process to continue the same conversation:

# Process 1 β€” start a chat and copy the printed thread_id
uv run python -m src.react_agent.chat_cli
# thread_id: 550e8400-e29b-41d4-a716-446655440000
# You: Remember that my favorite episode is about Two Hour Learning.
# ... type exit ...

# Process 2 β€” same thread_id continues the conversation
uv run python -m src.react_agent.chat_cli --thread-id 550e8400-e29b-41d4-a716-446655440000
# You: What did I say my favorite episode was?

You can also set REACT_AGENT_THREAD_ID instead of --thread-id.

Programmatic Usage

from src.react_agent.graph import react_agent, get_react_agent
from langchain_core.messages import HumanMessage

# Use default agent with durable SQLite conversation memory
# Pass a thread_id so the same conversation survives process restart
result = await react_agent.ainvoke(
    {"messages": [HumanMessage(content="What is Two Hour Learning?")]},
    config={"configurable": {"thread_id": "my-session-123"}}
)

# Follow-up questions in the same thread remember context
result = await react_agent.ainvoke(
    {"messages": [HumanMessage(content="Tell me more about that")]},
    config={"configurable": {"thread_id": "my-session-123"}}
)

# Create agent with custom config
from langchain_core.runnables import RunnableConfig

config = RunnableConfig(
    configurable={
        "model": "claude-sonnet-4-5",
        "max_tokens": 8000,
        "temperature": 0.1,
        "thread_id": "custom-thread"
    }
)
custom_agent = get_react_agent(config)
result = await custom_agent.ainvoke({
    "messages": [HumanMessage(content="What is Two Hour Learning?")]
}, config=config)

Comparison with deep_research_agent

Aspect React Agent Deep Research Agent
Framework LangChain create_agent LangGraph custom graph
Graph complexity Single ReAct loop Multi-node (clarify β†’ brief β†’ supervisor β†’ researchers β†’ report)
Use case Quick Q&A In-depth research
Tool access Direct Delegated via researchers
Output Single response Structured report
State Minimal (messages + checkpointer memory) Rich (brief, notes, iterations)
Memory SqliteSaver checkpointer (disk, by thread_id) Graph state
Model creation Provider-specific classes init_chat_model with configurable fields

Tools Available

The agent has access to these tools (re-exported from deep_research_agent.tools):

  1. search_knowledge_base(query) - Semantic search over podcast transcripts and episode summaries in ChromaDB
  2. query_knowledge_graph(query) - Run Cypher queries against the Neo4j knowledge graph
  3. inspect_graph_schema() - Get the Neo4j schema (nodes, relationships) to help write Cypher

Configuration

Default settings in configuration.py:

  • Model: gemini-flash-latest
  • Max iterations: 25 (safety limit)
  • Max tokens: 4000
  • Temperature: 0 (deterministic)
  • Timeout: 30 seconds

Override via RunnableConfig:

from langchain_core.runnables import RunnableConfig

config = RunnableConfig(
    configurable={
        "model": "gpt-5",
        "max_iterations": 5,
        "max_tokens": 8000,
        "temperature": 0.1
    }
)
result = await react_agent.ainvoke(input, config=config)

Or set environment variable:

export REACT_AGENT_DEFAULT_MODEL="claude-sonnet-4-20250514"

Dependencies

Dependencies are managed in the repo’s pyproject.toml. The key runtime pieces are LangChain (+ provider integrations) and python-dotenv.