Skip to content

Architecture

Steven Enamakel edited this page Aug 31, 2026 · 5 revisions

Architecture

TinyAgents is a small, provider-neutral agent harness for Rust, plus a durable typed state-graph runtime. It takes its shape from LangChain (models, tools, middleware, structured output, streaming, usage/cost) and LangGraph (START/END, nodes, conditional edges, channels/reducers, checkpoints, interrupts, subgraphs, time travel), rebuilt as ordinary, typed Rust.

This page shows how the crates layer together.

The crates

Crate Path Owns
Harness crates/tinyagents-harness/ provider-neutral model calls, typed tools, middleware, structured output, streaming, usage/cost, retry/limits, cache, memory, sub-agents, steering
Graph runtime crates/tinyagents-graph/ durable typed state graphs: START/END, nodes, edges, conditional routing, commands, Send fanout, reducers/channels, checkpoints, interrupts, subgraphs, child-task orchestration, streaming, observability, time travel
Registry crates/tinyagents-registry/ named capability catalog (models, tools, agents, graphs, routers, reducers) that .rag binds by name
Expressive .rag crates/tinyagents-language/ declarative, side-effect-free blueprints that compile into the same graph/harness types as hand-written Rust
Session crates/tinyagents-session/ durable SQLite-backed session history and a run ledger, separate from harness and graph durability
Tracing crates/tinyagents-tracing/ shared, feature-gated tracing macros used by the other crates

There is no facade crate: a consumer depends directly on the crates it needs. See Quick Start for the dependency snippet and feature flags, and Capabilities for a full index of what each crate exposes.

Dependency direction

tinyagents-tracing
        ^
        |
tinyagents-harness --------+
        ^                  |
        |                  v
tinyagents-language   tinyagents-session
        ^
        |
tinyagents-graph
        ^
        |
tinyagents-registry

tinyagents-tracing has no internal dependencies and compiles to no-op macros unless the tracing feature is enabled. tinyagents-harness depends only on tracing. tinyagents-language depends on harness (it compiles a .rag blueprint into harness/graph types). tinyagents-graph depends on harness, language, and tracing. tinyagents-registry depends on graph, harness, and language, since it is the layer that binds a .rag blueprint's capability references against real, registered Rust values. tinyagents-session depends only on harness (for its sqlite feature) and tracing; nothing in harness reads from session, and session is not required to run a harness or a graph at all — it is a separate persistence domain for history and search.

Both tinyagents-graph and tinyagents-registry also depend on a vendored, provider-neutral inference crate, tinyinference (vendor/tinyinference/crates/tinyinference), and tinyagents-harness depends on a vendored tool-schema crate, tinytools (vendor/tinytools/crates/tinytools).

Request flow

+-------------+        +-------------------------------------------+
| Application |------->| Capability Registry                       |
| Rust code   |        | models | tools | agents | graphs | routers |
+------+------+        +---------------------+---------------------+
       |                                     |
       |                                     v
       |              +-------------------------------------------+
       +------------->| Durable Graph Runtime                     |
                      | typed state | nodes | edges | checkpoints |
                      +---------------------+---------------------+
                                            |
                                            v
                      +-------------------------------------------+
                      | Agent Harness                             |
                      | prompts | tools | middleware | usage/cost |
                      +----+--------------------------+-----------+
                           |                          |
                           v                          v
                 +------------------+        +------------------+
                 | Model Providers  |        | Typed Tools      |
                 | OpenAI and       |        | local functions  |
                 | compatible hosts |        | external systems |
                 +------------------+        +------------------+

A graph node can call into the harness to run a model or a tool. The harness does not depend on the graph: it runs standalone as a single agent loop, or as one node's implementation inside a larger graph.

Optionally, a workflow can be authored as a .rag blueprint instead of Rust: the blueprint compiles (lexer, parser, compiler) into a Blueprint, which the registry binds by name into the same GraphBuilder/CompiledGraph and harness types a hand-written graph would use. See Expressive Language .rag.

Composition: agents calling agents, graphs embedding graphs

Two composition features let larger systems be built out of the same typed pieces, rather than a separate execution model:

  • Sub-agents. tinyagents_harness::subagent (SubAgent, SubAgentSession, SubAgentTool) wraps a whole harness agent as an ordinary Tool. A parent agent calling a sub-agent is just a tool call; the child run gets its own root_run_id/parent_run_id and its usage and cost roll up to the parent. See Harness.
  • Subgraphs. tinyagents_graph::subgraph (adapter_subgraph_node, shared_subgraph_node) embeds one compiled graph as a single node inside another. The embedded graph's checkpoints are namespaced under the embedding node id, so durability composes the same way state does. See Graph Runtime.

tinyagents_graph::recursion (RecursionPolicy, RecursionStack, RunTree) tracks parent/child run lineage for both of the above and enforces a depth limit, so a chain of sub-agents or nested subgraphs stays bounded and observable rather than growing without a cap. It is a safety and observability mechanism, not a distinct architectural layer.

Harness

The harness owns nondeterministic agent work:

  • model request construction and provider adapter calls
  • tool registration and execution
  • middleware hooks, structured output strategy, streaming deltas
  • provider error normalization, usage and cost records
  • retries, limits, cancellation, cache, memory/embeddings
  • sub-agents (tinyagents_harness::subagent): an agent exposed as a tool, run as a child at depth +1, capped by a run-limit policy and reported through sub-agent lifecycle events
  • deterministic model and tool test doubles (tinyagents_harness::testkit)

See Harness.

Graph runtime

The graph owns deterministic state movement:

  • typed state, reducers/channels, and updates
  • nodes, static and conditional edges, commands, Send fanout
  • checkpoints, interrupts, time travel, topology export, run status, observability
  • child-task orchestration (tinyagents_graph::orchestration): typed task controls (spawn/await/cancel/status/list/steer, and similar) exposed as harness tools, so an orchestrator agent manages concurrent child tasks by a stable task id
  • subgraphs (tinyagents_graph::subgraph): a node embeds another CompiledGraph

See Graph Runtime.

Registry

The registry owns named capabilities, letting Rust code and .rag blueprints refer to a model, tool, agent, graph, or router by a stable name instead of a process global. Entries describe capabilities well enough that a .rag blueprint's references can be validated and bound before it ever runs. See Registry.

Expressive language .rag

.rag is a declarative, side-effect-free blueprint format. It cannot embed arbitrary code; it can only reference capabilities by name, which the compiler binds and validates against a registry. Pipeline:

.rag source -> lexer -> parser -> compiler -> Blueprint
            -> bind_capabilities (registry) -> build_graph -> run to END

See Expressive Language .rag.

Design rule

Each crate keeps its contract narrow:

  • the harness calls providers and tools
  • the graph moves typed state through explicit topology
  • the registry binds names to capabilities
  • .rag describes workflow source, and lowers into the same graph/harness types a hand-written Rust caller would build directly

No layer bypasses another layer's policy, observability, or validation boundary: a .rag-authored blueprint, a sub-agent call, and a subgraph all pass through the same registry binding, run-limit, and event machinery as a top-level, hand-written run.

TinyAgents

Provider-neutral agent harness and durable state-graph runtime for Rust.

Getting started

Concepts

Modules

Providers

Contributing


Clone this wiki locally