Layered architecture
Module boundaries
| Module | Responsibility | Dependencies |
|---|---|---|
afk.agents | Agent definition, configuration, fail-safe | pydantic |
afk.core | Runner, step loop, state management, policies | afk.agents, afk.llms, afk.tools, afk.memory |
afk.llms | LLM runtime, provider adapters, retry/circuit breaking | Provider SDKs |
afk.tools | Tool registry, execution, validation, hooks | pydantic |
afk.memory | State persistence, checkpoints, compaction | Backend drivers |
afk.telemetry | Event pipeline, metrics, exporters | OTEL SDK (optional) |
afk.a2a | Agent-to-agent protocol, auth, external adapters | afk.core |
afk.evals | Eval runner, assertions, reporting | afk.core |
Key rule: Modules in the Adapters layer never import from each other.
afk.llms doesn’t know about afk.tools. Only afk.core wires them
together.Runtime sequence
What happens when you callrunner.run(agent, user_message="..."):
Contract boundaries
Every boundary between modules uses a typed contract: All contracts are Pydantic models. This means:- Validation at boundaries — malformed data causes clear errors
- Serializable — every contract can be logged, stored, or sent over the wire
- Versionable — contracts have stable shapes for backward compatibility
Error isolation
Failures in one adapter don’t crash the others:| Failure | Impact | Runner behavior |
|---|---|---|
| LLM call fails | No response for this step | Retry (if retryable) or fail the run |
| Tool execution fails | Tool result is an error object | Return error to LLM for self-correction |
| Memory backend fails | State not persisted | Run continues (degraded mode) |
| Telemetry fails | Events not exported | Run continues (events dropped silently) |
Design principles
- Contracts first. Define the interface (Pydantic model), then implement the behavior. Never skip the contract.
- No cross-adapter imports.
afk.llmsdoesn’t importafk.tools. Onlyafk.corewires modules together. - Classify failures. Every error is retryable, terminal, or non-fatal. The runner uses this classification to decide what to do.
- Least privilege. Adapters get only the data they need. LLM adapters don’t see tool results until the runner decides to include them.
Next steps
LLM Layer
Provider-portable LLM runtime in detail.
Developer Guide
Contribute to AFK — patterns and conventions.