Level 1: Your First Agent¶
This walkthrough uses symfonic.Agent. The Simple Agent
is the full reference for it; Moving to 11.0 covers the
transition boundaries.
SymfonicAgent and AgentBuilder: removed in 11.3.0
From 11.3.0, SymfonicAgent(...) and AgentBuilder().build() raise
EntryPointRemovedError, a migration error, before anything is built,
with no compatibility shim. Do not start new code on either. symfonic-core==9.12.* is the newest public
PyPI line on which both still construct (private 11.0 to 11.2.6 did too), but it predates Agent and the other
11.x features, so pinning it does not migrate an 11.x application. Earlier
versions of this page used both; their
arguments do not carry over to Agent by renaming, so use
Guide 22 to migrate.
Build a running symfonic agent in under 10 lines of code. No external services, no API keys, no infrastructure.
Prerequisites¶
- Python 3.11 or later
- pip (or uv, poetry, etc.)
Install¶
Public PyPI tops out at the 9.12 line, which does not ship symfonic.Agent.
Install the 11.x line from your approved private registry to run this page.
Your First Agent (10 Lines)¶
Create a file called my_agent.py:
import asyncio
from symfonic import Agent
from symfonic.core.testing import MockModelProvider
async def main():
agent = Agent(MockModelProvider(response="Hello! I'm your first symfonic agent."))
result = await agent.run("Hi there!")
print(result.text)
print(f"Tokens: {result.usage.total_tokens}")
asyncio.run(main())
Run it:
Output:
The mock provider reports no token usage; a real provider does.
What Just Happened¶
User query
-> Agent.run() (one turn, no session)
-> ModelProvider (LLM call -- mocked here)
-> AgentResult (text, messages, tool_calls, usage, stop_reason)
| Component | Role |
|---|---|
Agent |
The facade: one provider, optional instructions, tools and capabilities |
MockModelProvider |
Deterministic test double for the LLM |
AgentResult |
The turn's text, transcript, tool calls and token usage |
There is no configuration object, tenant scope or memory here. Memory,
delegation and the other capabilities are opt-in; see
Guide 22. To serve one agent per
tenant, compose Agents under symfonic.platform.AgentHost; the package
README shows a minimal host and Moving to 11.0 a host
with durable human continuation.
Switch to a Real LLM¶
Install the Anthropic extra and set your API key:
Replace the mock provider with a real one:
import asyncio
from symfonic import Agent
from symfonic.core.providers import AnthropicProvider
async def main():
agent = Agent(AnthropicProvider()) # uses ANTHROPIC_API_KEY env var
result = await agent.run("What is dependency injection?")
print(result.text)
asyncio.run(main())
Everything else stays the same. The ModelProvider protocol means you can
swap backends (Anthropic, OpenAI, local models) without changing any other
code.
Tools¶
Agent takes its tools at construction. This replaces the
AgentBuilder().tools(...) chain earlier versions of this page showed:
import asyncio
from symfonic import Agent
from symfonic.core import symfonic_tool
from symfonic.core.providers import AnthropicProvider
@symfonic_tool()
def add(a: int, b: int) -> int:
"""Add two integers."""
return a + b
async def main():
agent = Agent(
AnthropicProvider(),
instructions="You are a calculator agent.",
tools=[add],
)
result = await agent.run("What is 17 + 25?")
print(result.text)
asyncio.run(main())
The builder's other knobs (.sub_agent_spec(), .enable_hms(),
.lazy_tooling(), .memory(), .option()) have no keyword on Agent.
Guide 22 maps the legacy patterns
it covers to capabilities; do not assume a knob carries over by name.
Using the Low-Level Core API¶
If you want direct access to the graph runtime without a facade, use the core API:
import asyncio
from symfonic.core import (
AgentConfig,
AgentGraph,
AgentRuntime,
BaseAgentDeps,
ModelProvider,
)
from symfonic.core.documents.in_memory import InMemoryDocumentStore
from symfonic.core.models import Document
from symfonic.core.protocols import DocumentStore
from symfonic.core.testing import MockModelProvider
async def main():
config = AgentConfig()
provider = MockModelProvider(response="Hello from the core API!")
doc_store = InMemoryDocumentStore()
await doc_store.write(Document(id="greeting", content="Welcome to symfonic."))
deps = BaseAgentDeps()
deps.register(ModelProvider, provider)
deps.register(DocumentStore, doc_store)
graph = AgentGraph()
runtime = AgentRuntime(graph=graph, deps=deps, config=config)
result = await runtime.run("Hello, agent!")
print(f"Response: {result['final_response']}")
print(f"Nodes executed: {result['node_execution_log']}")
asyncio.run(main())
The core API gives you full control over dependency injection via
BaseAgentDeps and protocol-based service registration.
Choosing Your API Level¶
| API | Best for | Memory? | Streaming? |
|---|---|---|---|
Agent |
New agents; capabilities composed explicitly | Opt-in capability | Yes (stream()) |
AgentHost |
One Agent per tenant scope over shared resources |
Per composed agent | Per composed agent |
AgentRuntime |
Custom graph topologies, fine-grained DI | Manual | Manual |
Most users should start with Agent and drop to the core API only when they
need custom graph topologies.
Next Steps¶
Your agent works, but it forgets everything between runs. Level 2 adds persistent memory with Postgres.
Next: Memory Persistence
Ready to ship a real app instead of a script? symfonic init scaffolds a
full FastAPI + Postgres + auth + Docker project wired to symfonic-core โ
see Scaffold a Production App.
For a grouped overview of every feature across the v6.0-v7.0.3 sprint, see the Feature Catalog โ 11 capability clusters each with opt-in examples.