basic_agent¶
Legacy entry point: removed in 11.3.0
This page uses SymfonicAgent and has not been rewritten for
symfonic.Agent. From 11.3.0, SymfonicAgent(...) and
AgentBuilder().build() raise EntryPointRemovedError, a migration error,
before anything is built, with no compatibility shim, so the examples below
that construct either one do not run on 11.3.0 or later.
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 the 11.x features, and this page may use some
of them; pinning it does not migrate an 11.x application. For new code
start with
The Simple Agent, and migrate existing code with
Guide 22 and
Moving to 11.0.
Level 1 · Fundamentals — the high-level SymfonicAgent in under 10 lines.
Where minimal_agent wires the low-level symfonic.core
primitives by hand, this example uses the symfonic.agent orchestration layer.
SymfonicAgent composes the graph, the 5-layer memory system, and the model
provider for you — you construct it, call run(), and read a typed
AgentResponse.
- Lines: ~43
- Prerequisites: none to run as-is (uses
MockModelProvider); a real provider needspip install symfonic-core[anthropic]+ an API key - Key concepts:
SymfonicAgent,FrameworkConfig,FrameworkTenantScope,AgentResponse
Run it¶
Installed via pip? Copy this example into your project with the CLI (no checkout needed):
pip install "symfonic-core[cli]"
symfonic examples add basic_agent
python basic_agent/basic_agent.py
Or, from a source checkout (run from the repo root):
Expected output:
Full code¶
"""An agent that remembers, in about ten lines.
Usage:
python -m examples.agent.basic_agent
Requires:
pip install symfonic-core[anthropic]
export ANTHROPIC_API_KEY=sk-... # only for the real provider
Public surface used, and nothing else::
from symfonic import Agent
from symfonic.agent import FrameworkTenantScope
from symfonic.capabilities.memory import GraphBackedHms, memory_capabilities
from symfonic.capabilities.prompting import PromptingCapability
from symfonic.memory.backends import InMemoryGraphBackend
The previous version of this file was titled "multi-memory agent" and turned
memory off -- ``auto_hydrate``, ``auto_consolidate`` and ``lazy_tooling`` all
False, because nothing registered a layer for them to use. It demonstrated
construction.
Composing two capabilities is what makes the title true. ``memory_capabilities``
records and recalls against a store; ``PromptingCapability`` is what renders a
recollection into the next prompt. Either alone is a no-op you cannot see:
without prompting the agent stores perfectly and the model reads none of it.
The scope is the isolation boundary and it is passed, not discovered. Every
read and write below is checked against it.
"""
from __future__ import annotations
import asyncio
from symfonic import Agent
from symfonic.agent import FrameworkTenantScope
from symfonic.capabilities.memory import GraphBackedHms, memory_capabilities
from symfonic.capabilities.prompting import PromptingCapability
from symfonic.core.testing import MockModelProvider
from symfonic.memory.backends import InMemoryGraphBackend
def build(store: GraphBackedHms, scope: FrameworkTenantScope) -> Agent:
"""One agent, bound to one tenant, that remembers its own turns."""
# Swap MockModelProvider for AnthropicProvider() and nothing else changes:
# from symfonic.core.providers import AnthropicProvider
return Agent(
MockModelProvider(response="You mentioned the walnut allergy."),
instructions="You are a helpful assistant. Be brief.",
capabilities=[
*memory_capabilities(store, scope, limit=5),
PromptingCapability(sources=[]),
],
)
async def main() -> None:
store = GraphBackedHms(InMemoryGraphBackend())
scope = FrameworkTenantScope.root("org", "demo-tenant")
await build(store, scope).run("Remember that I am allergic to walnuts.")
second = await build(store, scope).run("What do you remember?")
print(f"Response: {second.text}")
print(f"Duration: {second.duration_ms:.1f}ms")
# No token count here on purpose: MockModelProvider reports none, and
# an example printing "0 tokens" teaches that accounting is broken.
# Rung 9 (journey_observability) measures spend with a provider that
# reports real usage.
# Two agents, one store: the second turn recalled what the first was told,
# and nothing in this file retrieved it. The capability did.
if __name__ == "__main__":
asyncio.run(main())
Step by step¶
1. Choose a provider¶
Same ModelProvider seam as minimal_agent. To go live,
swap the two commented lines:
from symfonic.core.providers import AnthropicProvider
provider = AnthropicProvider() # reads ANTHROPIC_API_KEY from env
Nothing else on the page changes — that is the point of the provider protocol.
2. Construct the agent¶
agent = SymfonicAgent(
model_provider=provider,
config=FrameworkConfig(auto_hydrate=False, auto_consolidate=False),
)
SymfonicAgent is the batteries-included orchestrator. FrameworkConfig layers
framework behavior (memory hydration, consolidation, lazy loading) on top of the
core AgentConfig. Here auto_hydrate=False and auto_consolidate=False keep
the demo self-contained — no memory backends are read or written. Flip them on
once you attach real stores.
3. Define a tenant scope¶
Every run is scoped to a tenant. FrameworkTenantScope is how the framework
isolates one customer's memory, documents, and skills from another's. Multi-tenant
isolation is a first-class concept — see
Hierarchical Tenant Scope.
4. Run and read the typed response¶
response = await agent.run("What do you remember?", scope=scope)
print(f"Response: {response.final_response}")
print(f"Run ID: {response.run_id}")
print(f"Duration: {response.duration_ms:.1f}ms")
print(f"Memory entries used: {response.memory_entries_used}")
Unlike the low-level runtime (which returns a raw state dict), SymfonicAgent.run
returns a typed AgentResponse. Beyond final_response it carries observability
fields (run_id, duration_ms, memory_entries_used) and — when you ask for it
— a validated structured payload. See
Structured Output.
Low-level vs high-level¶
minimal_agent (core) |
basic_agent (agent) |
|
|---|---|---|
| Entry type | AgentRuntime |
SymfonicAgent |
| You wire | graph, deps, stores | just the provider |
| Memory layers | none by default | 5-layer HMS available |
| Return | state dict |
typed AgentResponse |
| Best for | learning internals, custom graphs | building applications |
What to try next¶
- Add persistent memory and turn
auto_hydrateon → memory_agent - Delegate work to child agents → sub_agents
- Ask for a validated object back → Structured Output