Skip to content

real_agent

Level 6 · Real LLM — the first example that talks to an actual model.

Every other curated example runs on MockModelProvider so it's free and deterministic. real_agent is the one that calls Anthropic's Claude for real: it reads ANTHROPIC_API_KEY, sends your question, and prints the model's answer. It's the bridge from "the framework runs" to "the framework answers."

  • Prerequisites: pip install "symfonic-core[anthropic]" + ANTHROPIC_API_KEY
  • Key concepts: AnthropicProvider, real LLM integration, .env loading

Get it and run it

Installed via pip? Copy it in and run against a real model — no checkout:

pip install "symfonic-core[cli,anthropic]"
export ANTHROPIC_API_KEY=sk-ant-...
symfonic examples add real_agent
python -m real_agent "What is the largest planet in our solar system?"

Or from a source checkout (run from the repo root):

python -m examples.real_agent "Your question here"

Expected output:

Query: What is the largest planet in our solar system?
---
Response: Jupiter is the largest planet in our solar system.
Nodes: ['react']

Full code

"""Real LLM example — runs against Anthropic's Claude API.

Requires:
  pip install symfonic-core[anthropic]
  cp .env.example .env  # then fill in ANTHROPIC_API_KEY

Usage: python -m examples.real_agent
       python -m examples.real_agent "Your custom question here"
"""

import asyncio
import os
import sys

from dotenv import load_dotenv

load_dotenv()  # loads .env from project root

from symfonic.core import (
    AgentConfig,
    AgentGraph,
    AgentRuntime,
    BaseAgentDeps,
)
from symfonic.core.providers import AnthropicProvider


async def main() -> None:
    if not os.environ.get("ANTHROPIC_API_KEY"):
        print("Error: ANTHROPIC_API_KEY environment variable is required.")
        print("  export ANTHROPIC_API_KEY=sk-ant-...")
        sys.exit(1)

    query = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "What is symfonic? Make up a creative answer in 2 sentences."

    config = AgentConfig()
    deps = BaseAgentDeps(ModelProvider=AnthropicProvider())
    graph = AgentGraph()
    runtime = AgentRuntime(graph=graph, deps=deps, config=config)

    print(f"Query: {query}")
    print("---")

    result = await runtime.run(query)

    print(f"Response: {result['final_response']}")
    print(f"Nodes: {result['node_execution_log']}")


if __name__ == "__main__":
    asyncio.run(main())

Step by step

1. Load credentials

from dotenv import load_dotenv
load_dotenv()  # loads .env from the project root

AnthropicProvider reads ANTHROPIC_API_KEY from the environment. load_dotenv() (shipped with the [anthropic] extra) is a convenience — it picks the key up from a local .env so you don't have to export it every shell. Exporting the variable works just as well.

2. Fail fast without a key

if not os.environ.get("ANTHROPIC_API_KEY"):
    print("Error: ANTHROPIC_API_KEY environment variable is required.")
    sys.exit(1)

A real example should tell you why it can't run rather than surfacing a raw auth error from deep in the stack.

3. Wire the real provider

deps = BaseAgentDeps(ModelProvider=AnthropicProvider())

This is the only line that differs from minimal_agent: AnthropicProvider() in place of MockModelProvider. Everything downstream — graph, runtime, state — is identical. That's the payoff of the ModelProvider protocol: swapping mock for real is a one-line change.

4. Run and read the answer

result = await runtime.run(query)
print(f"Response: {result['final_response']}")

Same AgentRuntime.run as the mock examples; now final_response carries a genuine model completion. Pass a question on the command line (python -m real_agent "...") or it uses a built-in default.

Going further

  • Swap the provider for another vendor — OpenRouterProvider, AWSBedrockProvider — the rest of the code is unchanged. See Model Providers.
  • Add memory so the model reasons over stored context → see real_agent_with_memory in the Examples Index.
  • Ask for a validated object back → Structured Output.

See also