An agent is a model that can call tools and decide when to stop. Behind the scenes, every Cognis agent is a compiledDocumentation Index
Fetch the complete documentation index at: https://cognis.vasanth.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Graph<AgentState> with a small set of nodes — a model node that asks the LLM what to do, a tool-dispatch node that runs the calls, and edges that loop until the model has nothing left to do.
You can build that graph by hand, but you usually don’t need to. AgentBuilder carries the opinion that most agents look the same, and gives you a builder for the parts that change.
What an agent is, exactly
When to use an agent
- The model needs to make multiple decisions before answering.
- You want it to reach for tools (search, calculator, API lookups, your domain functions).
- You want a transcript the user can replay.
Client::invoke(messages) directly.
If you need branching that doesn’t fit the loop shape, drop down to a Graph<S>.
Quick example
agent.run(impl Into<Message>) returns an AgentResponse carrying:
content: String— the final assistant reply.messages: Vec<Message>— the full transcript, including tool calls and tool results.
How it works
When you callagent.run:
- Memory seeds the conversation. If you set one (
with_memory), the agent prepends those messages. - The model is invoked with current messages and tool definitions.
with_max_iterationscaps how many times the loop can ask. - If the reply has tool calls, the tool dispatcher runs them, threads the results back as
Message::toolentries, and loops. - If it doesn’t, the loop exits and the model’s last reply becomes
resp.content. - Middleware wraps each model call. Retry, fallback, rate limits, redaction, prompt caching, planning — all hook into this loop without rewriting it.
Customization knobs
AgentBuilder exposes everything you’d want to change about that loop:
| Method | What changes |
|---|---|
.with_llm(Client) | The model. Required. |
.with_tool(Arc<dyn Tool>) / .with_tools(...) | What the agent can call. |
.with_system_prompt(...) | Role / persona / rules. |
.with_memory(impl Memory) | What context survives between run calls. |
.with_max_iterations(n) | Hard cap on loop iterations. |
.with_max_tool_calls(n) | Hard cap on total tool invocations. |
.with_filesystem(Arc<dyn Backend>) | Mounts a virtual filesystem the model can read/write. |
.with_approver(Arc<dyn Approver>) | Human-in-the-loop tool gating. |
.stateful() / .stateless() | Persist or reset state across run. |
.with_graph(graph) | Replace the default ReAct graph entirely. |
- Tools — define tools, validators, the tool macro.
- System prompts and memory — Buffer, Window, TokenBuffer, SummaryBuffer, Entity, Knowledge-Graph, Vector, Hybrid.
- Multi-agent orchestration — Sequential, Supervisor, ParallelVote, RoundRobin.
- Middleware — the catalog and how to compose.
- Human-in-the-loop — interrupts and approvers.
When the loop isn’t enough
Sometimes you want behavior the loop can’t express — fan-out across heterogeneous workers, persistence-aware time travel, structured handoffs between agents with different state shapes. Then you have two paths:- Multi-agent orchestration.
MultiAgentOrchestrator::new(strategy).add(...).run(...)runs several agents under a strategy. - Hand-built graph.
Graph<MyState>::new()....compile()?then either drive it directly, orAgent::wrap(graph)to slot it into the agent surface.
AgentResponse as the output type, so callers don’t change.
See also
Quickstart
The fastest path to a working agent.
Tools
Define what the agent can do.
Multi-agent
When one agent isn’t enough.
Graphs
The substrate behind the loop.