docs / agents-sdk / conceptsgithub.com/Fareground/agents-sdk ↗

Core concepts

The execution loop

The agent sends context and tool descriptions to a model. The model returns text or tool calls. The engine executes permitted tools, adds their results to the conversation, and calls the model again until completion or a limit.

A tool is application code. A model's decision to call it does not replace input validation, authorization or business rules in that code.

Three levels of control

ask and stream are convenient one-shot functions. They own a short-lived agent and close it when finished.

Agent owns a conversation, tool registry, model client, repository and engine. It initializes its repository lazily. Use an async context manager to close it reliably.

AgentEngine accepts explicitly constructed components and middleware. Initialize the repository before using it. Use this level when integrating with an existing application lifecycle.

Definitions and instances

AgentDefinition declares a name, model, system prompt, tools and limits. Its tools list contains registered names. Agent accepts functions directly and registers them for you. Lower-level callers construct a ToolRegistry themselves.

The Agent facade can auto-detect a model from configured providers. A lower-level AgentDefinition should receive an explicit model: the facade's convenience detection is not a promise that every API picks a default.

Sessions and storage

A session identifies a conversation and its history. Reusing a session ID continues it. The engine serializes runs for a session within that engine instance; this is not a distributed lock across independent processes.

Memory storage disappears with the process. SQLite stores locally. PostgreSQL supports shared durable storage, but application deployment still owns database migrations, credentials, pooling and cross-process coordination.

Results and events

Agent.run() returns final text, a session ID and collected events. Streaming yields events as they happen: text deltas, tool activity, errors and lifecycle changes. Keep structured events if you need to explain what an agent did.

What the SDK does not decide

Your application chooses model access, authorized tools, tenant identity, data retention, idempotency and spending policy. A tool timeout does not prove that an external write was canceled. Use application-owned request IDs and reconciliation for consequential operations.


Models and providers

Explicit model selection

Use a provider-qualified name: anthropic:<model-id>, openai:<model-id>, google:<model-id> or ollama:<model-id>. Choose the model available in your deployment and pass it explicitly for reproducible configuration.

import os
from fg_agents import Agent

agent = Agent(model=os.environ["AGENT_MODEL"])

Model availability and capabilities belong to the provider. A provider-qualified identifier does not guarantee that every model supports every tool or streaming feature.

Installation and credentials

Provider Extra Credentials
Anthropic fg-agents[anthropic] ANTHROPIC_API_KEY
OpenAI fg-agents[openai] OPENAI_API_KEY
Google fg-agents[google] GOOGLE_API_KEY or GEMINI_API_KEY
Local Ollama OpenAI-compatible client path Local service and an installed model

AgentLLM accepts an api_keys mapping for host-managed credentials and a custom_providers mapping for compatible base URLs. Keep keys out of agent prompts and persisted user-visible context.

Automatic detection

When the facade's model is omitted, detection checks configured Anthropic, OpenAI and Google credentials, then probes local Ollama. If nothing is usable it raises ModelDetectionError. API reference lists the exact defaults for this release.

Automatic detection is convenient locally. Explicit model configuration avoids a different installed credential changing your deployed model unexpectedly.

Limits and failures

AgentDefinition includes max_turns, max_tokens_per_turn, llm_timeout_seconds, max_llm_retries and fallback_models. Configure them for the task. A per-response token cap is not a total account spending limit.

Review fallback and retry behavior against your workload before enabling it. If a request's outcome is uncertain, reconcile externally visible work before retrying it. See production integration.


Tools and middleware

Define a focused tool

from fg_agents import tool

@tool(description="Calculate a reorder quantity from a target and available stock.")
def reorder_quantity(target: int, available: int) -> dict:
    if target < 0 or available < 0:
        return {"error": "target and available must be non-negative"}
    return {"quantity": max(0, target - available)}

Simple type hints create parameter schemas. Descriptions should tell the model when to use the tool, which units it expects, and what it returns. Validate business constraints inside the handler.

Explicit schemas

Use json_schema= when you need nested shapes, enums or constraints beyond the simple annotation inference. The decorator exposes that schema to the model; do not assume a model provider enforces every constraint. Handler validation remains necessary.

from fg_agents import tool

@tool(
    description="Look up a supported product category.",
    json_schema={
        "type": "object",
        "properties": {"category": {"type": "string", "enum": ["coffee", "tea"]}},
        "required": ["category"],
        "additionalProperties": False,
    },
)
def category_info(category: str) -> dict:
    if category not in {"coffee", "tea"}:
        raise ValueError("Unsupported category")
    return {"category": category}

Register and expose

Pass functions to Agent(tools=[...]), or register them in a ToolRegistry for the engine. A definition's tools list selects which registered names are available. Keep that list explicit for each role.

Sync functions run in a thread; async functions run natively. A correctly typed ExecutionContext parameter named ctx or context is injected by the framework and hidden from the model's argument schema.

Failure behavior

Use concise structured errors that tell the model what to fix. The engine rejects incomplete or malformed model tool arguments instead of executing the handler with invented defaults. Timeouts, permission denials and execution failures are distinct tool outcomes; inspect events for the cause.

Retries are appropriate only where repeating an operation is safe. For a real order or payment, use a stable idempotency key and verify the external result after an uncertain timeout.

Middleware

AgentEngine(middleware=[...]) accepts hooks before and after model calls, before and after tool calls, on turn completion and on error. Middleware runs in registration order. Returning None from before_tool_call blocks a call.

Shipped middleware covers audit, token tracking, permissions, rate limits, loop guarding and context compression. API reference gives hook signatures. Instantiate only what the task needs and test blocked as well as successful tool calls.


Skills and orchestration

Skills are reusable instructions

A skill pairs a prompt template with required tool names. It helps an agent follow a procedure; it does not grant authorization or install a tool implementation.

from fg_agents import SkillsManager

skills = SkillsManager()
skills.register(SkillsManager.from_markdown(
    skill_id="inventory_review",
    name="Inventory review",
    markdown_content="""# Inventory review
Check current stock before making a recommendation.
State the lead-time assumption and show the reorder calculation.
""",
    required_tools=["stock"],
))

Pass the manager to AgentEngine or Orchestrator, register the required tools, and include the skill ID in the agent definition's skills list. Missing skills are logged during resolution; validate your configured skill IDs before deploying.

When to use an orchestrator

Use Orchestrator when a coordinator must delegate work to specialist agents. It wraps the engine, manages sub-agents, and wires delegation tools. For a single assistant, Agent is simpler.

An orchestrator can register built-in tools automatically. Review the resulting registry and expose a deliberate tool list in each definition. Set auto_register_builtins=False when you need to supply that registry yourself.

Lifecycle

Construct the LLM client, tool registry and repository; create the orchestrator; call await orchestrator.initialize(); consume its run events; and call await orchestrator.shutdown() when the application stops.

Sub-agent definitions can specify deadlines and the parent can limit parallel agents. Bound each delegated task and inspect partial results if a deadline expires. More agents do not guarantee a better answer; measure whether delegation improves your specific workflow.

See API reference, the generated public reference, and the repository's examples.