Quick import map
| Task | Import | From |
|---|---|---|
| Define an agent | from afk.agents import Agent | afk.agents |
| Configure safety limits | from afk.agents import FailSafeConfig | afk.agents |
| Set up policy engine | from afk.agents import PolicyEngine, PolicyRule | afk.agents |
| Run an agent | from afk.core import Runner | afk.core |
| Configure the runner | from afk.core import RunnerConfig | afk.core |
| Debug a run | from afk.debugger import Debugger, DebuggerConfig | afk.debugger |
| Define a tool | from afk.tools import tool | afk.tools |
| Tool context access | from afk.tools import ToolContext | afk.tools |
| Tool hooks | from afk.tools import prehook, posthook, middleware | afk.tools |
| Build an LLM client | from afk.llms import LLMBuilder | afk.llms |
| Run evals | from afk.evals import run_suite | afk.evals |
| Define eval cases | from afk.evals.models import EvalCase, EvalBudget | afk.evals.models |
| A2A client | from afk.a2a import A2AClient | afk.a2a |
| MCP server | from afk.mcp import MCPServer | afk.mcp |
| Task queue | from afk.queues import TaskQueue, TaskItem | afk.queues |
Architecture overview
The framework is built on three core pillars that interact at runtime:-
Agent (
afk.agents.Agent) — The definition. It holds the static configuration: “Who am I?” (instructions, name), “What can I do?” (tools, subagents), and “How safe am I?” (fail-safe limits). Agents are stateless definitions that can be reused across many runs. -
Runner (
afk.core.Runner) — The engine. It orchestrates the dynamic execution: managing the event loop, maintaining conversation state in memory, executing tools, handling failures, and dispatching events to telemetry. Runners are stateful (holding connections to memory/telemetry) but re-entrant. -
Runtime (
afk.llms,afk.tools) — The capabilities. The underlying machinery that powers the agent: LLM clients handling provider I/O, and the tool registry managing safe execution of Python functions.
Execution model
TheRunner supports three execution modes for different use cases. All modes use the same underlying event loop and guarantee the same consistency models.
1. Synchronous (run_sync)
Blocks the calling thread until the run is complete. Best for scripts, cron jobs, and simple CLI tools.
- Concurrency: Internal tasks (like tool execution) still run on the asyncio event loop, but the entry point is blocking.
- Return: Returns the final
AgentResultonly after the run reaches a terminal state (completed,failed).
2. Asynchronous (await run)
Returns an awaitable coroutine. Best for backend API handlers (FastAPI, Django Async) and high-concurrency workloads.
- Concurrency: Non-blocking. Allows thousands of concurrent agent runs in a single process.
- Return: Returns
AgentResultupon completion.
3. Streaming (await run_stream)
Returns an async iterator of AgentStreamEvents. Best for real-time UIs (chatbots, IDE integrations).
- Feedback: Emits events immediately as they happen (token delta, tool start, tool end).
- Control: Allows the consumer to abort the run by cancelling the stream consumption.
Most common pattern
Constructor signatures
Agent
Runner
Key Runner methods
-
run_sync(agent, ...)->AgentResultBlocking execution. In memory-backend modes, this persists state to the DB at every step. In-memory mode is transient. Use for: Scripts, CLI tools, tests. -
await run(agent, ...)->AgentResultAsync execution. This is the standard entry point for scalable applications. It handles the full agent loop: LLM calls -> Tool execution -> Policy checks -> Recursion. Use for: Web servers, queue workers, scalable backend services. -
await run_stream(agent, ...)->AgentStreamHandleReturns a handle exposing anasync iteratorof events. The stream yields events as they happen. You MUST consume the stream to drive execution forward. Use for: Chat interfaces, real-time feedback. -
await list_background_tools(*, thread_id, run_id, include_resolved=False)->list[dict]Lists persisted deferred tool tickets for a run. Use for: Long-running tool monitoring and external worker reconciliation. -
await resolve_background_tool(*, thread_id, run_id, ticket_id, output=...)->NoneMarks a deferred ticket as completed and stores output for runner polling. Use for: External worker completion callbacks. -
await fail_background_tool(*, thread_id, run_id, ticket_id, error=...)->NoneMarks a deferred ticket as failed with error details. Use for: External worker failure propagation. -
await resume(agent, *, run_id, thread_id)->AgentResultRe-hydrates a run from the database. It loads the full execution snapshot (messages, unexecuted tool calls) and continues exactly where it left off. Use for: Human-in-the-loop workflows (resume after approval), recovering from process crashes. -
await compact_thread(*, thread_id)->NoneTriggers memory compaction for a specific thread. This applies the configuredRetentionPolicyto reduce token usage by summarizing history or discarding old messages. Use for: Long-running conversations where context window limits are a concern.
RunnerConfig
FailSafeConfig
@tool decorator
Core types
AgentResult
| Field | Type | Description |
|---|---|---|
final_text | str | The agent’s final response |
state | str | Terminal state: completed, failed, degraded, cancelled, interrupted |
run_id | str | Unique run identifier |
thread_id | str | Thread identifier for memory continuity |
tool_executions | list[ToolExecutionRecord] | All tool calls with name, success, output, latency, and agent provenance |
subagent_executions | list[SubagentExecutionRecord] | All subagent invocations |
usage_aggregate | UsageAggregate | Token counts and cost estimates |
state_snapshot | dict[str, JSONValue] | Final runtime counters/snapshot metadata |
AgentStreamEvent
| Field | Type | Description |
|---|---|---|
type | str | Event category (see Streaming) |
text_delta | str | None | Incremental text chunk |
tool_name | str | None | Tool name for tool events |
tool_call_id | str | None | Tool call identifier |
tool_success | bool | None | Whether tool succeeded |
tool_output | JSONValue | None | Tool output payload |
tool_error | str | None | Tool error message |
step | int | None | Current step index |
state | str | None | Current agent state |
result | AgentResult | Terminal result (for completed events) |
error | str | None | Error message (for error events) |
Agents module
Agent
Core agent configuration — model, instructions, tools, subagents.
FailSafeConfig
Step limits, cost budgets, timeout, failure policies.
PolicyEngine
Policy rules for gating tool calls and actions.
PolicyRule
Individual policy rule definition.
Core module
Runner
Agent execution engine — sync, async, stream.
RunnerConfig
Safety defaults, sanitization, interaction mode.
AgentResult
Run output — final_text, state, tool/subagent executions, usage.
Tools module
@tool decorator
Define typed tool functions with Pydantic models.
Hooks
Pre/post hooks and middleware for tool execution.
LLM module
LLMBuilder
Builder pattern for LLM client configuration.
LLMRequest / LLMResponse
Normalized contracts for LLM interaction.
API stability
AFK follows semantic versioning. Public exports fromafk.agents, afk.core, afk.tools, afk.llms, and afk.evals are considered stable and will not change without a major version bump.
Internal modules (prefixed with _ or under afk.core.runner._internal) are not part of the public API and may change at any time.