Agents SDK
Build agents with tools, conversations and streaming in Python.
Overview
The Agents SDK provides ask() for one-shot requests, Agent for conversations, and explicit engine components for application integration. Add tools, stream results, and choose in-memory, SQLite or PostgreSQL persistence.
Documentation · Quickstart · Tools · Production integration
It is the runtime layer in Fareground's family of open agent building blocks,
alongside agent-id (identity),
agent-messaging (encrypted
agent-to-agent messaging), agent-memory
(per-agent memory), and agent-knowledge
(shared team knowledge).
What it deliberately does not do:
- Authentication — your app verifies users. You hand the router an
authenticatorthat turns each request into a tenant + user, and the framework enforces it: a caller only ever reaches its own tenant's sessions and memory, and the tenant comes from the authenticator, never the request body. (Omit it and the API runs as a single shared tenant for local/dev — it warns loudly so you never ship that by accident.) - The model — it talks to OpenAI, Anthropic, Google, and local models via Ollama. You bring the API key; pick any model at runtime, or let the framework detect a sensible default from your environment.
- The frontend — you build the chat UI; the framework streams events to it.
Package names. The distribution is published as
fg-agentsand the import package isfg_agents(e.g.from fg_agents import create_app). These are the names dependents rely on and are intentionally left unchanged; the repository name isagents-sdk.
Install
The core install is deliberately slim — engine, tools, ask/Agent, and
in-memory persistence. Everything else is an extra:
pip install fg-agents # core (engine + tools + in-memory persistence)
pip install "fg-agents[web]" # + FastAPI app/router (create_app)
pip install "fg-agents[postgres]" # + PostgreSQL backend (SQLAlchemy + asyncpg)
pip install "fg-agents[sqlite]" # + SQLite backend
pip install "fg-agents[openai]" # + OpenAI (and OpenAI-compatible) providers
pip install "fg-agents[anthropic]" # + Anthropic provider
pip install "fg-agents[google]" # + Google Gemini provider
pip install "fg-agents[all]" # everything (web + postgres + sqlite + all providers + scheduler)
| Extra | Adds | You need it for |
|---|---|---|
| (none) | — | ask/Agent, tools, engine, in-memory persistence |
web |
fastapi, uvicorn | create_app, create_agent_router, the HTTP/SSE API |
postgres |
sqlalchemy, asyncpg | PostgresRepository, memory="postgres:<url>" |
sqlite |
aiosqlite | SQLiteRepository, memory="sqlite" |
anthropic / openai / google |
provider SDK | that provider (openai also covers Ollama, Groq, and every other OpenAI-compatible provider) |
scheduler |
croniter, sqlalchemy | AgentScheduler |
Requires Python 3.11+.
Quickstart
With one API key env var set (export ANTHROPIC_API_KEY=...) and the matching
provider extra installed (pip install "fg-agents[anthropic]"), this is the
whole program:
import asyncio
from fg_agents import ask
async def main():
print(await ask("What's 2+2?"))
asyncio.run(main())
Already inside async code — or in a REPL with top-level await (python -m asyncio, IPython, Jupyter)? Then it's just:
print(await ask("What's 2+2?"))
ask() detects the provider from your environment — ANTHROPIC_API_KEY,
then OPENAI_API_KEY, then GOOGLE_API_KEY/GEMINI_API_KEY, then a local
Ollama server — and picks a current model for it. Pass
model="provider:model" to override. It takes tools, a system prompt, and a
memory backend too, and stream() is its streaming twin:
from fg_agents import ask, stream
answer = await ask("Greet Ada.", tools=[greet], system_prompt="Be friendly.")
async for event in stream("Tell me a story"):
print(event.type, event.data)
If you might break out of a stream() loop early, wrap it in
contextlib.aclosing(...) so the ephemeral agent is closed deterministically.
Conversations: the Agent facade
ask() is one-shot. For multi-turn conversations, the Agent facade is one
object that wires the LLM client, tool registry, persistence, and engine —
usable as an async context manager. Tools are plain functions; the schema is
derived from the signature and docstring:
def greet(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
async with Agent(
tools=[greet], # model= optional — auto-detected
system_prompt="You are a friendly greeter.",
memory="sqlite", # persist sessions; "memory" (default) or "postgres:<url>"
) as agent:
first = await agent.run("Please greet Ada.")
second = await agent.run("Now greet Grace.") # same conversation
run() returns an AgentRunResult (.text, .session_id, .events —
str() is the answer text), and agent.stream() yields raw StreamEvents
as they happen. Any failure in run() — an engine error event or a raised exception — surfaces
as a single AgentRunError carrying the session_id and the partial events,
with the original exception chained as __cause__. Each Agent instance keeps
one conversation by default; pass session_id= per call to target another.
Going lower level
The facade is sugar over four objects you can wire yourself when you need full
control (custom middleware, skills, shared registries across agents). Graduate
gradually via agent.engine, agent.llm, agent.tools, and
agent.repository — or build the stack directly:
import asyncio
from fg_agents import AgentDefinition, AgentEngine, AgentLLM, ToolRegistry, create_repository, tool
@tool(description="Greet someone by name")
def greet(name: str) -> str:
return f"Hello, {name}!"
async def main():
llm = AgentLLM()
repo = create_repository("memory") # or "sqlite", "postgres"
await repo.initialize() # explicit here; the facade does this lazily
tools = ToolRegistry()
tools.register_function(greet)
agent = AgentDefinition(
name="greeter",
model="anthropic:claude-sonnet-4-6", # required — no default model
system_prompt="You are a friendly greeter. Use the greet tool when asked.",
tools=["greet"],
)
engine = AgentEngine(llm=llm, tool_registry=tools, repository=repo)
async for event in engine.run("session-1", "Say hello to Alice", agent):
if event.data.get("text"):
print(event.data["text"], end="", flush=True)
asyncio.run(main())
A full web backend
create_app returns a FastAPI application exposing routes to start sessions,
post messages, and stream replies over SSE.
from fg_agents import AgentDefinition, ToolRegistry, create_app, tool
@tool(description="Search the knowledge base")
async def search(query: str) -> str:
return f"Results for '{query}'..."
assistant = AgentDefinition(
name="assistant",
model="anthropic:claude-sonnet-4-6",
system_prompt="You are a helpful assistant. Use your tools to answer.",
tools=["search"],
)
tools = ToolRegistry()
tools.register_function(search)
app = create_app(
agents={"assistant": assistant},
tool_registry=tools,
# db_url="postgresql+asyncpg://user:pass@localhost:5432/mydb", # production
)
# uvicorn myapp:app
The frontend then creates a session and streams messages:
POST /api/agent/sessions -> { "session_id": "..." }
POST /api/agent/sessions/{id}/messages -> text/event-stream (SSE)
See examples/ for five runnable apps, from a two-line facade
hello-world to a complete chat UI (examples/04_web_app.py),
plus frontend SSE clients in examples/frontend/.
Documentation
Start at docs/index.md for tutorials, concepts, provider setup, tools, hosting and troubleshooting.
docs/api.md— reference for the public API surfacedocs/streaming.md— the SSE wire format and every event type (for frontend authors)docs/persistence.md— memory / SQLite / PostgreSQL backends and initialization semantics
Concepts / Building blocks
| Building block | What it is |
|---|---|
Agent |
The facade — one object that wires LLM, tools, persistence, and engine; run() returns an AgentRunResult, failures raise AgentRunError. |
AgentDefinition |
Declarative agent config — name, model, system prompt, tools, sub-agents. No default model; you choose the provider at runtime. |
AgentEngine |
The ReAct loop: model → tools → model → …, emitting stream events at each step. |
Orchestrator |
A coordinator agent that plans, delegates to sub-agents (in parallel), and synthesizes results. |
| Tools | Python functions exposed to the agent via the @tool decorator and a ToolRegistry; built-in tools ship in tools/builtin.py. |
| Skills | Reusable bundles of prompt templates + tools that give an agent domain knowledge (SkillsManager). |
| Memory | WorkingMemory per-session scratch space and a ContextManager with health scoring and progressive compression. |
| Middleware | Hooks into the agent loop: audit, token tracking, permissions, rate limiting, loop guarding, context compression. |
| Persistence | Async repositories behind one interface — InMemoryRepository, SQLiteRepository, PostgresRepository via create_repository(...). |
| Streaming | StreamEvent objects serialized to SSE (event.to_sse()) with reconnection support. |
| Scheduler | Run an agent on a cron schedule (AgentScheduler, optional croniter dependency). |
| API layer | create_app / create_agent_router — FastAPI integration with pluggable authentication and per-tenant isolation. |
flowchart TB
User([User in browser])
subgraph Framework["Agent"]
direction TB
Skills["Skills<br/><sub>knowledge & playbooks</sub>"]
Tools["Tools<br/><sub>actions the agent can take</sub>"]
Subs["Sub-agents<br/><sub>specialists to delegate to</sub>"]
end
DB[("Your database<br/><sub>sessions, history</sub>")]
LLM([Any model<br/>OpenAI · Anthropic · local])
User <-->|SSE stream| Framework
Framework <--> Skills
Framework <--> Tools
Framework <--> Subs
Framework <--> LLM
Framework <--> DB
Project structure
src/fg_agents/
agent.py Agent facade — one-object setup (quickstart tier)
core/ Engine, LLM client, types, errors
tools/ @tool decorator, registry, built-in tools, handlers
orchestrator/ Multi-agent orchestration + sub-agent runner
skills/ Skill loading and injection
memory/ Working memory, context management, health scoring
middleware/ Audit, token tracking, permissions, rate limit, loop guard
persistence/ Multi-backend: in-memory, SQLite, PostgreSQL
streaming/ SSE event definitions
prompts/ System prompt construction
scheduler/ Cron-scheduled agents
api/ Service layer, FastAPI router, auth, schemas
app.py create_app() application factory
examples/ Five runnable apps + frontend SSE clients
docs/ Reference docs: API, streaming wire format, persistence
tests/ Test suite
Contributing
Development setup, tests, linting, and commit conventions live in CONTRIBUTING.md. See the changelog for what's changed.
Built by Fareground.
Licensed under Apache-2.0.
Metadata
Release files for fg-agents 0.4.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fg_agents-0.4.5.tar.gz | 227.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fg_agents-0.4.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 393.0 kB
Release files / fg_agents-0.4.5.tar.gz
| Download URL | fg_agents-0.4.5.tar.gz |
|---|---|
| Size | 227.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
66cc38679e7fc59f496b0b0b02085ec4f254940ebc7fa3930744e26263591933
|
|
BLAKE2b-256 checksum How to use checksums |
6b7418e63186448861f3f0d6fde4ad371b421199d5b6453dc1929c73b3fb3370
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency logRelease files / fg_agents-0.4.5-py3-none-any.whl
| Download URL | fg_agents-0.4.5-py3-none-any.whl |
|---|---|
| Size | 165.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1705a9f55229df48ee2c5a94f955c5d0f7595061f98fbc8779fad59dc34f7c81
|
|
BLAKE2b-256 checksum How to use checksums |
857f65d43a083bbb391575fd533a7ef5b2be27602d40e5b61212038dbdbed1dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.
Transparency log