Skip to content

核心概念

Recursive 有五个正交的核心概念,每个概念都可以独立替换,互不影响。

1. Message(消息)

位置src/message.rs

唯一的数据原语。Message 是一条聊天消息,可选包含工具调用列表。所有状态都通过 Vec<Message>(对话记录)传递。

rust
pub struct Message {
    pub role: Role,          // System | User | Assistant | Tool
    pub content: String,
    pub tool_calls: Vec<ToolCall>,
    pub tool_call_id: Option<String>,
}

2. LlmProvider(LLM 提供商)

位置src/llm/

模型后端的 trait。实现一次,到处使用。

rust
#[async_trait]
pub trait LlmProvider: Send + Sync {
    async fn complete(
        &self,
        messages: &[Message],
        tools: Option<&[ToolDef]>,
    ) -> Result<Message>;
}

内置实现:

  • OpenAiProvider — OpenAI 兼容 HTTP(支持 OpenAI、DeepSeek、Ollama 等)
  • AnthropicProvider — Anthropic 原生 API
  • MockProvider — 用于测试的脚本化响应

3. Tool + ToolRegistry(工具 + 工具注册表)

位置src/tools/

Tool 是模型可以调用的任何操作(读取文件、运行 shell 命令、获取 URL 等)。

rust
#[async_trait]
pub trait Tool: Send + Sync {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn parameters(&self) -> serde_json::Value;    // JSON Schema
    async fn call(&self, args: serde_json::Value) -> ToolResult;
}

ToolRegistry 将工具名称映射到实现,负责分发调用。

内置工具

工具描述
read_file读取文件内容(沙箱限制在工作区内)
write_file写入或创建文件
apply_patch应用 V4A 格式的补丁
list_dir列出目录内容
run_shell执行 shell 命令(带超时)
search_files跨文件正则搜索
web_fetchHTTP GET 并提取 HTML 文本(可选)
remember / recall持久化键值内存

4. AgentRuntime(Agent 运行时)

位置src/runtime.rs

核心循环。接收目标字符串,交替调用模型和工具,直到满足终止条件:

目标 → [LLM] → 工具调用? → [工具] → [LLM] → 工具调用? → ... → 最终答案

终止条件FinishReason):

原因含义
NoMoreToolCalls模型停止调用工具(正常完成)
ProviderStop(s)LLM 返回停止信号
BudgetExceeded达到 max_steps 限制
Stuck { .. }同一工具调用循环重复
TranscriptLimit { .. }对话记录过大
PlanPendingAgent 等待计划审批
Cancelled外部取消
PermissionDenialLimit权限拒绝次数过多

运行期间的错误通过 runtime.run()Err(...) 返回。

循环故意不可扩展——新能力应该以工具或 Provider 的形式添加,而不是在循环内部分支。

5. AgentEvent(步骤事件)

位置src/event.rs

每一步执行后发出的观察者通道。通过 EventSink 订阅可以驱动 UI、日志、回放或测试,无需修改循环。

rust
#[non_exhaustive]
pub enum AgentEvent {
    AssistantText { text: String, step: usize },
    ToolCall { name: String, id: String, arguments: String, step: usize },
    ToolResult { id: String, name: String, output: String, step: usize },
    Usage { input_tokens: u32, output_tokens: u32, step: usize },
    Compacted { removed: usize, kept: usize, summary_chars: usize, step: usize },
    TurnFinished { reason: String, steps: usize },
    // ...
}

正交性示意

┌─────────────────────────────────────────┐
│              AgentRuntime               │
│  ┌─────────┐         ┌───────────────┐  │
│  │   LLM   │◄────────│ ToolRegistry  │  │
│  │Provider │         │  (Tool × N)   │  │
│  └────┬────┘         └───────────────┘  │
│       │  AgentEvent 流                  │
└───────┼─────────────────────────────────┘

   ┌────────────┐  ┌──────────┐  ┌──────────┐
   │    TUI     │  │ HTTP API │  │   日志   │
   └────────────┘  └──────────┘  └──────────┘

Released under the MIT License.