Skip to content

basic_agent

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 needs pip 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):

python -m examples.agent.basic_agent

Expected output:

Response: I remember our conversation!
Run ID: <uuid>
Duration: <n>ms
Memory entries used: 0

Full code

"""Basic SymfonicAgent example -- multi-memory agent in <10 lines.

Usage:
    python -m examples.agent.basic_agent

Requires:
    pip install symfonic-core[anthropic]
    export ANTHROPIC_API_KEY=sk-...
"""

from __future__ import annotations

import asyncio

from symfonic.agent.config import FrameworkConfig
from symfonic.agent.engine import SymfonicAgent
from symfonic.agent.types import FrameworkTenantScope
from symfonic.core.testing import MockModelProvider


async def main() -> None:
    # Use MockModelProvider for demo (no API key needed)
    # Replace with AnthropicProvider() for real usage:
    #   from symfonic.core.providers import AnthropicProvider
    #   provider = AnthropicProvider()
    provider = MockModelProvider(response="I remember our conversation!")

    agent = SymfonicAgent(
        model_provider=provider,
        config=FrameworkConfig(auto_hydrate=False, auto_consolidate=False),
    )

    scope = FrameworkTenantScope(tenant_id="demo-tenant")
    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}")


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

Step by step

1. Choose a provider

provider = MockModelProvider(response="I remember our conversation!")

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

scope = FrameworkTenantScope(tenant_id="demo-tenant")

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

See also