tiny-agent 把 “调用模型 → 执行工具 → 回灌结果” 的循环建模成一个显式状态机,并将模型、沙箱、存储、可观测与拦截全部抽象为 trait。框架本身不绑定任何后端:用哪家模型、状态存到哪里、工具跑在何种隔离环境,皆由调用方决定。
它面向需要把 agent 嵌进真实服务的场景——进程会重启、用户会取消、工具有副作用、运行过程要审计——这些工程负担都被收敛进清晰的边界里,而不是散落在业务代码中。
[]
= "0.1"
工作原理
一次运行就是状态之间的转移。每个状态都携带可序列化的 AgentState,这是 checkpoint 与恢复能成立的基础。
submit ─▶ Reasoning ─┬─▶ ToolExecuting ─▶ Reasoning 执行工具、回灌结果,继续推理
├─▶ WaitingForUser 工具请求用户输入,停车等待
└─▶ Success 模型给出最终回复(终态)
cancel ─▶ Interrupted ─▶ resume 任意安全点取消并落 checkpoint,可续跑
error ─▶ Fail 超过重试 / 迭代上限(终态)
特性
- 状态机式的 agent loop —
Ready → Reasoning → ToolExecuting → …全程显式建模,每个状态都可序列化。 - Checkpoint 与恢复 — 中断点写快照,
resume从断点续跑,终态自动清理。 - 协作式取消 — 传入
CancellationToken即可在安全点停车;未应答的工具调用会被补齐占位结果,transcript 不留孤儿tool_use。 - 后端无关 — Provider、Sandbox、三类存储、Trajectory、Middleware 全是 trait,可任意替换。
- 内置 Provider — Anthropic 与 OpenAI 兼容端点(DeepSeek、OpenAI 及各类兼容服务),均为流式。
- 沙箱抽象 — 触达外部世界的工具统一经
Sandbox::execute,自带HostSandbox,换隔离方案不改工具代码。 - 类型安全的工具 — 参数 struct 经
schemars自动生成 JSON Schema;只读工具之间自动并发调度。 - Skills — 按
SKILL.md约定从目录发现技能,模型按需加载,不必把说明一次性塞满上下文。 - 中间件 — 洋葱模型的生命周期钩子,可改写请求/消息/工具结果,或在工具执行前短路。
- 可观测 — Trajectory 事件流记录运行全过程,用于实时推送、日志、回放与评测。
- 子 Agent —
spawn_agent把子任务委托给独立配置的子 agent。 - 健壮性 — 区分可重试与永久错误,指数退避 + jitter,并限制最大连续失败与迭代次数。
快速开始
AgentRunTime 需要五样必填依赖:一个 Provider、一个 model 名、两类存储(checkpoint 与 transcript)、一个 sandbox。Sandbox 框架自带 HostSandbox;存储 trait 由你按后端实现(内存、文件、SQLite、Redis……)。
下面用内存存储跑通一轮完整的 agent loop,完整版见 src/main.rs:
use ;
use async_trait;
use ;
use CancellationToken;
// 最小内存存储:同时实现 checkpoint 与 transcript 两个 trait。
// Agent 没有 Clone 但可序列化,所以 checkpoint 按 JSON 字符串存。
async
# 默认指向 DeepSeek,可用 LLM_BASE_URL / LLM_MODEL 覆盖
LLM_API_KEY=sk-xxx
HostSandbox会在本机直接执行 bash 命令,仅适合本地试跑;隔离场景请自行实现Sandbox。
两种构造方式
上面的 AgentRunTimeBuilder 是链式写法。等价地,你也可以先组一个 AgentRuntimeConfig,再用 AgentRunTime::from_config 构造——当配置来自外部(反序列化、依赖注入、需要在多个 runtime / 子 agent 间复用同一份配置)时更顺手。
use ;
// 必填项走构造函数:provider / model / checkpoint / transcript / sandbox
let mut config = new
.with_tools
.with_system_prompt
.with_middleware
.with_trajectory;
// 其余参数是公开字段,按需直接设置
config.temperature = Some;
config.max_iter = 20;
config.skill_dirs.push;
let runtime = from_config?;
AgentRunTimeBuilder 内部也只是收集这些字段后调 from_config,两者完全等价,按喜好选用。
核心概念
Agent 状态机
| 状态 | 含义 |
|---|---|
Ready |
准备进入下一轮推理 |
Reasoning |
正在调用模型 |
ToolExecuting |
正在执行模型请求的工具 |
WaitingForUser |
工具要求用户交互(如追问),已停车 |
Success / Fail |
终态,运行结束清理 checkpoint |
Interrupted |
被取消,已写快照,可 resume |
驱动入口:
| 方法 | 作用 |
|---|---|
create_session() |
新建会话 |
submit(session_id, text) |
追加一条用户消息,返回 Ready |
run(agent, token) |
把状态机跑到停车点 |
resume(session_id, token) |
从中断的 checkpoint 续跑 |
run_session(session_id, token) |
一站式:有断点就恢复,否则从头跑 |
可插拔的 trait
| Trait | 职责 | 自带实现 |
|---|---|---|
LlmProvider |
流式 chat completion | AnthropicProvider、OpenAiCompatibleProvider |
Sandbox |
工具执行环境(按会话 open / execute / save / fork) | HostSandbox |
CheckpointStorage |
可恢复状态快照的存取 | 自行实现 |
TranscriptStorage |
喂给模型的消息历史 | 自行实现 |
TrajectorySink |
只读事件流 | NoopSink |
Middleware |
生命周期钩子(拦截 / 改写 / 短路) | 自行实现 |
工具
用参数 struct(实现 Deserialize + JsonSchema)注册,Schema 自动生成:
tools.register;
内置工具:read_file、write_file(write / replace / insert / append)、bash、askFollowupQuestion。
此外 register_spawn_agent 提供子 agent,register_read_skill 提供 skill 加载(配置 skill 目录时自动注册)。
Skills
Skill 让模型按需加载领域知识或操作手册,约定沿用 Agent Skills:一个目录配一个 SKILL.md,YAML frontmatter 写 name 与 description,正文是给模型看的说明,同目录其余文件作为可选资源。
构建时用 skill_dir / skill_dirs 指定发现目录,发现规则:
dir/SKILL.md存在 →dir本身是一个 skill;root/*/SKILL.md存在 → 每个子目录各是一个 skill;- skill id 取目录名,
name/description取自 frontmatter。
let runtime = new
// ...必填项
.skill_dir
.build?;
配置了 skill 目录后,框架自动注册 readSkill 工具:模型先看到所有 skill 的 name + description 索引,需要时再调用 readSkill 拉取某个 skill 的正文或某个资源文件——按需加载,避免把全部说明一次性塞进上下文。
底层的 SkillManager 也可独立使用(discover_dirs / list / read),用于在运行时之外检视或预加载技能。
子 Agent(spawn_agent)
spawn_agent 是一个工具:让父 agent 把一段独立子任务委托给一个单独配置的子 agent,子 agent 跑完后把结果回传给父 agent。
子 agent 的形态由你传入的一份 AgentRuntimeConfig 决定——它可以用不同的 model、system prompt、工具集、中间件,与父 agent 互不影响。注册时把这份子配置交给 register_spawn_agent:
use ;
// 1. 子 agent 的配置:可与父 agent 完全不同的一套
let mut child_tools = new;
register_default_tools;
let child_config = new
.with_tools
.with_system_prompt;
// 2. 把子配置注册成父 agent 的一个工具
let mut tools = new;
register_default_tools;
register_spawn_agent;
// 3. tools 交给父 runtime,模型即可调用 spawn_agent { task: "..." } 派生子 agent
几个关键点:
- 子 agent 拥有独立的会话与 transcript(互不污染上下文),但共享父 agent 当前的 sandbox——子任务里
write_file、bash的副作用落在同一个工作环境,父 agent 后续可见。注册时配置里的sandbox会在调用时被父 sandbox 覆盖,所以那一项填什么都行。 - 工具入参只有一个
task字段;返回child_session_id与子 agent 的最终结果(含失败 / 等待用户 / 中断等状态)。 - 子配置同样可挂自己的 skills、中间件、trajectory,实现「专用子 agent」。
中间件
钩子按洋葱模型组合:before_* 正序、after_* 逆序。可改请求、改消息、给工具结果脱敏,或在工具执行前直接短路。
可观测:Trajectory
运行时在每个关键节点同步 emit 一条事件(用户消息、推理开始、assistant 消息、工具调用与结果、中断、失败……)。事件序列化为带 type tag 的 JSON,按发生顺序到达,适合实时推送给前端,或落盘做日志、回放与评测。不设 sink 时为 NoopSink。
配置
AgentRunTimeBuilder 上的可调参数:
| 方法 | 默认 | 说明 |
|---|---|---|
max_iter |
10 | 单次运行的最大推理轮数,超限以 MaxIter 失败 |
max_consecutive_fail_count |
3 | 最大连续失败次数 |
retry_backoff |
200ms | 可重试失败的退避基数(指数 + jitter),ZERO 关闭 |
tool_timeout |
120s | 单个工具调用超时,ZERO 关闭 |
temperature / max_tokens |
无 | 透传给 provider |
system_prompt |
无 | 系统提示 |
skill_dir / skill_dirs |
无 | skill 发现目录 |
middleware |
空链 | 可多次调用追加 |
trajectory |
NoopSink |
事件 sink |
兼容性
需要 Rust 1.85 及以上(edition 2024)。运行时基于 Tokio。
许可证
采用 MIT 或 Apache-2.0 双许可,任选其一。
除非你明确声明,你有意提交并被本仓库收录的贡献,均按上述双许可授权,不附加额外条款。