AgentBuilder 构建器

`AgentBuilder` 是 RAF 最常用的构造模式——通过流畅的链式调用配置并构建一个完整配置的 Agent。它封装了 `ChatClientAgent` 的复杂构造逻辑(管道组装、默认 Provider 注入、参数校验)。

结构体定义

pub struct AgentBuilder<C> {
    agent_id: String,                                          // Agent ID
    chat_client: Option<C>,                                    // LLM 客户端
    instructions: String,                                      // 系统指令
    tools: Vec<Arc<dyn ITool>>,                                // 工具列表
    context_providers: Vec<Arc<dyn IContextProvider>>,         // Provider 链
    properties: HashMap<String, serde_json::Value>,            // 扩展属性
    description: String,                                       // 描述信息
    max_tool_rounds: usize,                                    // 最大工具轮数(默认 10)
    compression_strategy: Option<Arc<dyn ICompressionStrategy>>,  // 压缩策略
    token_counter: Option<Arc<dyn ITokenCounter>>,             // Token 计数器
}

完整使用示例

use std::sync::Arc;
use rust_agent_core::{WorkspaceScope, ScopePolicy, TokenBudgetStrategy, EstimateCounter};
use rust_agent_client::DeepSeekChatClient;
use rust_agent_framework::{
    AgentBuilder,
    tools::{ReadFile, WriteFile, ListFiles, RunCommand},
    WorkspaceContextProvider,
};

#[tokio::main]
async fn main() -> rust_agent_core::Result<()> {
    // 1. 创建 LLM 客户端
    let client = DeepSeekChatClient::from_key("sk-...", "deepseek-chat")?;

    // 2. 配置工作区
    let scope = Arc::new(WorkspaceScope::new("/project", "my-project")
        .with_policy(ScopePolicy::ApproveOutside));

    // 3. 创建上下文提供器
    let workspace = WorkspaceContextProvider::new(Arc::clone(&scope))
        .add_tool(ReadFile { scope: None })
        .add_tool(WriteFile { scope: None })
        .add_tool(ListFiles { scope: None })
        .add_tool(RunCommand { scope: None, timeout_secs: None });

    // 4. 构建 Agent
    let agent = AgentBuilder::new("workspace-agent")
        .chat_client(client)                          // 必需
        .instructions("你是一个项目助手,能够读写文件和执行命令。") // 系统指令
        .with_description("工作区管理助手")              // 描述
        .add_context_provider(workspace)               // 添加 Provider(在默认 HistoryProvider 之后)
        .max_tool_rounds(8)                            // 工具调用循环上限
        .with_compression_strategy(                    // 压缩策略
            Arc::new(TokenBudgetStrategy::new().with_eviction_threshold(0.6))
        )
        .with_token_counter(                           // Token 计数器(压缩必需)
            Arc::new(EstimateCounter::new())
        )
        .with_properties([                             // 扩展属性
            ("environment".into(), serde_json::json!("production")),
        ])
        .build()?;                                     // 构建,返回 Arc<dyn IAgent>

    Ok(())
}

方法详解

`new(id: impl Into<String>) -> Self`

创建构建器实例,自动初始化:

- `context_providers`:包含默认的 `InMemoryHistoryProvider` - `max_tool_rounds`:默认 10 - 其他字段使用默认值

`chat_client(client: C) -> Self`

**必须调用**。设置 LLM 客户端。`C` 必须实现 `IChatClient + 'static`。

AgentBuilder::new("agent")
    .chat_client(DeepSeekChatClient::from_key("sk-...", "deepseek-chat")?)
//  .chat_client(OpenAiChatClient::from_key(base_url, "sk-...", "gpt-4o")?)
    .build()?;

如果忘记调用,`build()` 返回 `Err(ConfigError("chat_client is required"))`。

`instructions(text: impl Into<String>) -> Self`

设置系统指令文本。在 `run()` 时被组装为 `system` 角色的消息。

.instructions("你是一个专业的代码审查助手。你的职责包括:\n1. 检查代码风格\n2. 发现潜在 bug\n3. 提出改进建议")

也可以通过 `AgentRunOptions::with_instructions()` 在单次运行时覆盖:

let stream = agent.run(
    messages,
    Some(session),
    Some(AgentRunOptions::new().with_instructions("本次对话使用简洁风格回复")),
).await?;

`with_tool(tool: impl ITool + 'static) -> Self`

注册单个工具。可多次调用注册多个工具。

.with_tool(ReadFile { scope: None })
.with_tool(WriteFile { scope: None })
.with_tool(RunCommand { scope: None, timeout_secs: None })

当注册了工具时,`build()` 自动用 `FunctionInvokingChatClient` 包裹 `IChatClient`,实现工具调用循环。

`add_context_provider(provider: impl IContextProvider + 'static) -> Self`

追加一个 ContextProvider 到链中。Provider 按注册顺序依次执行。

// 链:InMemoryHistoryProvider → WorkspaceContextProvider → SkillsProvider
.add_context_provider(workspace_provider)
.add_context_provider(skills_provider)

`add_context_provider_shared(provider: Arc<dyn IContextProvider>) -> Self`

添加一个共享的 Provider(`Arc`),便于在 Agent 外部保留引用。

let history = Arc::new(InMemoryHistoryProvider::new());
let agent = AgentBuilder::new("agent")
    .chat_client(client)
    .add_context_provider_shared(Arc::clone(&history))
    .build()?;
// history 可以在 Agent 外部使用

`with_history_provider(provider: impl IContextProvider + 'static) -> Self`

替换内置的 `InMemoryHistoryProvider` 为自定义实现。

.with_history_provider(RedisHistoryProvider::new(redis_client))

实现逻辑:在 Provider 链中定位 `InMemoryHistoryProvider`(按名称)并替换。如果不存在,则追加到链尾。

`max_tool_rounds(rounds: usize) -> Self`

设置工具调用循环的最大轮次。当 LLM 连续调用工具超过此时限,`FunctionInvokingChatClient` 返回 `FinishReason::MaxRounds` 并终止循环。

.max_tool_rounds(3)  // 最多 3 轮工具调用后强制终止

默认值为 10。

`with_description(desc: impl Into<String>) -> Self`

设置 Agent 的描述信息。存储在 `AgentMetadata.description` 中,供前端和发现机制使用。

.with_description("代码审查助手 — 检查代码风格、潜在 bug 和性能问题")

`with_properties(iter) -> Self`

设置扩展属性键值对。存储在 `AgentMetadata` 中(通过 `ResponseMetadata.properties` 传递)。

.with_properties([
    ("team".into(), json!("backend")),
    ("version".into(), json!("1.0.0")),
])

`with_compression_strategy(strategy: Arc<dyn ICompressionStrategy>) -> Self`

设置上下文窗口压缩策略。需要同时配置 `token_counter` 才生效。

.with_compression_strategy(Arc::new(SlidingWindowStrategy::new(50)))
.with_compression_strategy(Arc::new(TokenBudgetStrategy::new()))
.with_compression_strategy(Arc::new(
    CompressionPipeline::new()
        .add_strategy(Box::new(SlidingWindowStrategy::new(100)))
        .add_strategy(Box::new(TokenBudgetStrategy::new()))
))

`with_token_counter(counter: Arc<dyn ITokenCounter>) -> Self`

设置 Token 计数器。配合压缩策略使用。内置实现:

// 估算计数器(无需额外依赖)
.with_token_counter(Arc::new(EstimateCounter::new()))

// 精确计数器(需启用 tiktoken feature)
#[cfg(feature = "tiktoken")]
.with_token_counter(Arc::new(TiktokenCounter::for_model("gpt-4")))

`build() -> Result<Arc<dyn IAgent>>`

构建最终的 Agent,执行以下操作:

1. 校验 `chat_client` 已设置 2. 如果有工具,创建 `FunctionInvokingChatClient` 管道 3. 创建 `ChatClientAgent` 并配置所有参数 4. 返回 `Arc<dyn IAgent>` —— trait object,类型擦除

let agent: Arc<dyn IAgent> = AgentBuilder::new("my-agent")
    .chat_client(client)
    .build()?;

ContextProvider 链的默认行为

默认 Provider

pub fn new(id: impl Into<String>) -> Self {
    Self {
        // ...
        context_providers: vec![
            Arc::new(InMemoryHistoryProvider::new()) as Arc<dyn IContextProvider>
        ],
        // ...
    }
}

`InMemoryHistoryProvider` 自动将 Session 中的历史消息注入到消息列表,实现多轮对话。

Provider 链执行顺序

Provider 按 `Vec` 中的索引顺序执行:

[0] InMemoryHistoryProvider   ← 注入历史消息
[1] WorkspaceContextProvider  ← 注入工作区指令和工具
[2] AgentSkillsProvider       ← 注入 Skills 指令
[3] CompressionProvider       ← 压缩消息列表(replace_messages = true)

后续 Provider 可设置 `ContextResult.replace_messages = true` 来**替换**之前累积的消息——这天然支持压缩策略。

构建器泛型参数

`AgentBuilder<C>` 的泛型参数 `C` 由 `chat_client()` 方法推断:

// C = DeepSeekChatClient
let builder: AgentBuilder<DeepSeekChatClient> = AgentBuilder::new("agent")
    .chat_client(deepseek_client);

// C = OpenAiChatClient
let builder: AgentBuilder<OpenAiChatClient> = AgentBuilder::new("agent")
    .chat_client(openai_client);

`build()` 要求 `C: IChatClient + 'static`。

错误处理

| 错误场景 | 返回 | |----------|------| | 未设置 `chat_client` | `Err(ConfigError("chat_client is required"))` | | `ChatClientBuilder::build()` 失败 | 管道组装错误 |

下一步

掌握 `AgentBuilder` 后,继续阅读 **[Run 生命周期](./run-lifecycle.md)**,理解 `run()` 方法的三阶段内部机制。