Layered architecture
Module boundaries
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: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.