Skip to main content

lellm_agent/runtime/
builder.rs

1//! AgentBuilder — Agent 链式构建器。
2//!
3//! 提供推荐的入口 API,一步构建 ToolUseLoop。
4//!
5//! # 示例
6//! ```ignore
7//! use lellm_agent::{AgentBuilder, ToolRegistration};
8//! use lellm_core::Prompt;
9//!
10//! // 简单文本
11//! let agent = AgentBuilder::new(model)
12//!     .system("你是一个有帮助的助手。")
13//!     .tool(search_tool)
14//!     .tool(weather_tool)
15//!     .max_iterations(20)
16//!     .build();
17//!
18//! // 分层构建 — 最大化前缀缓存
19//! let agent = AgentBuilder::new(model)
20//!     .system(
21//!         Prompt::builder()
22//!             .layer_cached("核心身份…")
23//!             .layer_cached("工具指南…")
24//!             .layer_dynamic("会话上下文…")
25//!             .build(),
26//!     )
27//!     .build();
28//! ```
29
30use std::sync::Arc;
31
32use lellm_core::{Prompt, ReasoningConfig, ToolChoice};
33use lellm_provider::ResolvedModel;
34
35use super::config::{ToolCachePolicy, ToolUseConfig, ToolUseDeps};
36use super::context::ContextBudget;
37use super::fallback::FallbackStrategy;
38use super::request_opts::RequestOptions;
39use super::retry::RetryPolicy;
40use super::runtime::ToolUseLoop;
41use super::tools::{CompositeCatalog, StaticCatalog, ToolCatalog, ToolExecutor, ToolRegistration};
42
43/// Agent 链式构建器 — 推荐的 Agent 创建方式。
44///
45/// 内部收集静态工具和动态目录,`build()` 时组装为 `ToolCatalog`,
46/// 再传给 `ToolUseLoop::new()`。所有 setter 返回 `self`(不借用),
47/// 支持流畅的链式调用。
48pub struct AgentBuilder {
49    model: ResolvedModel,
50    /// 收集通过 `.tool()` 注册的本地静态工具(最高优先级)
51    static_tools: Vec<ToolRegistration>,
52    /// 收集通过 `.catalog()` 注册的动态目录(按注册顺序,先绑定的优先级高于后绑定的)
53    catalogs: Vec<Arc<dyn ToolCatalog>>,
54    config: ToolUseConfig,
55    deps: ToolUseDeps,
56}
57
58impl AgentBuilder {
59    /// 创建构建器,绑定模型。
60    pub fn new(model: ResolvedModel) -> Self {
61        Self {
62            model,
63            static_tools: Vec::new(),
64            catalogs: Vec::new(),
65            config: ToolUseConfig::default(),
66            deps: ToolUseDeps::default(),
67        }
68    }
69
70    /// 注册工具。
71    pub fn tool(mut self, reg: ToolRegistration) -> Self {
72        self.static_tools.push(reg);
73        self
74    }
75
76    /// 批量注册工具。
77    pub fn tools(mut self, registrations: impl IntoIterator<Item = ToolRegistration>) -> Self {
78        self.static_tools.extend(registrations);
79        self
80    }
81
82    /// 绑定动态工具目录(MCP、插件系统等)。
83    ///
84    /// 可调用多次。按注册顺序,先绑定的优先级高于后绑定的。
85    /// 静态工具(`.tool()`)永远拥有最高优先级。
86    ///
87    /// # 示例
88    /// ```ignore
89    /// use std::sync::Arc;
90    /// use lellm_agent::{AgentBuilder, ToolCatalog};
91    /// use lellm_mcp::{McpCatalog, McpClient};
92    ///
93    /// let client = Arc::new(McpClient::with_transport(transport));
94    /// let catalog = McpCatalog::discover(client).await?;
95    /// let agent = AgentBuilder::new(model)
96    ///     .catalog(Arc::new(catalog))
97    ///     .build();
98    /// ```
99    pub fn catalog(mut self, catalog: Arc<dyn ToolCatalog>) -> Self {
100        self.catalogs.push(catalog);
101        self
102    }
103
104    /// 设置最大迭代轮次(默认 10)。
105    pub fn max_iterations(mut self, max: usize) -> Self {
106        self.config.max_iterations = max;
107        self
108    }
109
110    /// 设置每次 LLM 请求的最大输出 token 数(默认 4k)。
111    pub fn max_output_tokens(mut self, max: u32) -> Self {
112        self.config.max_output_tokens = max;
113        self
114    }
115
116    /// 设置整个 Agent Run 的最大输出 token 总数。
117    pub fn max_total_output_tokens(mut self, max: u32) -> Self {
118        self.config.max_total_output_tokens = Some(max);
119        self
120    }
121
122    /// 设置系统提示。
123    ///
124    /// 支持简单文本或分层 `Prompt`(通过 `From<String>` 自动转换)。
125    ///
126    /// # 示例
127    ///
128    /// ```ignore
129    /// // 简单文本
130    /// let agent = AgentBuilder::new(model)
131    ///     .system("你是一个有帮助的助手。")
132    ///     .build();
133    ///
134    /// // 分层构建 — 最大化前缀缓存
135    /// use lellm_core::Prompt;
136    /// let agent = AgentBuilder::new(model)
137    ///     .system(
138    ///         Prompt::builder()
139    ///             .layer_cached("核心身份…")
140    ///             .layer_cached("工具指南…")
141    ///             .layer_dynamic("会话上下文…")
142    ///             .build(),
143    ///     )
144    ///     .build();
145    /// ```
146    pub fn system(mut self, system: impl Into<Prompt>) -> Self {
147        self.config.system = Some(system.into());
148        self
149    }
150
151    /// 设置系统提示(纯文本)。
152    ///
153    /// 这是 `.system()` 的别名,保留用于向后兼容。
154    #[deprecated(since = "0.5.0", note = "Use `.system()` instead")]
155    pub fn system_prompt(mut self, prompt: String) -> Self {
156        self.config.system = Some(prompt.into());
157        self
158    }
159
160    /// 设置工具缓存策略(默认 `Auto`)。
161    ///
162    /// - `Auto`:为未设置 `cache_control` 的工具自动添加 `Breakpoint`
163    /// - `Preserve`:不修改用户设置的 `cache_control`
164    /// - `Disabled`:清除所有工具的 `cache_control`
165    pub fn tool_cache_policy(mut self, policy: ToolCachePolicy) -> Self {
166        self.config.tool_cache_policy = policy;
167        self
168    }
169
170    // ─── RequestOptions 快捷 setter ──────────────────────────
171
172    /// 设置完整的 RequestOptions(覆盖所有生成参数)。
173    pub fn request_options(mut self, opts: RequestOptions) -> Self {
174        self.config.request_options = opts;
175        self
176    }
177
178    /// 设置生成温度(0.0 ~ 2.0)。
179    pub fn temperature(mut self, t: f64) -> Self {
180        self.config.request_options.temperature = Some(t);
181        self
182    }
183
184    /// 设置 nucleus sampling 阈值(0.0 ~ 1.0)。
185    pub fn top_p(mut self, p: f64) -> Self {
186        self.config.request_options.top_p = Some(p);
187        self
188    }
189
190    /// 设置随机种子,保证可复现性。
191    pub fn seed(mut self, s: u64) -> Self {
192        self.config.request_options.seed = Some(s);
193        self
194    }
195
196    /// 设置工具选择策略(仅首轮生效)。
197    pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
198        self.config.request_options.tool_choice = Some(choice);
199        self
200    }
201
202    /// 设置停止序列。
203    pub fn stop_sequences(mut self, seqs: Vec<String>) -> Self {
204        self.config.request_options.stop_sequences = Some(seqs);
205        self
206    }
207
208    /// 设置预填充文本(引导模型输出方向)。
209    pub fn prefill(mut self, text: String) -> Self {
210        self.config.request_options.prefill = Some(text);
211        self
212    }
213
214    /// 设置推理配置(控制模型是否进行深度推理)。
215    pub fn reasoning(mut self, r: ReasoningConfig) -> Self {
216        self.config.request_options.reasoning = Some(r);
217        self
218    }
219
220    /// 设置是否流式输出推理过程。
221    pub fn stream_thinking(mut self, enable: bool) -> Self {
222        self.config.stream_thinking = enable;
223        self
224    }
225
226    /// 设置单轮推理 Token 上限。
227    pub fn reasoning_budget(mut self, max: u32) -> Self {
228        self.config.request_options.max_reasoning_tokens = Some(max);
229        self
230    }
231
232    /// 设置整个 Agent Run 的最大推理 Token 总数。
233    pub fn max_total_reasoning_tokens(mut self, max: u32) -> Self {
234        self.config.max_total_reasoning_tokens = Some(max);
235        self
236    }
237
238    /// 设置 Fallback 策略。
239    pub fn fallback(mut self, fallback: Arc<dyn FallbackStrategy>) -> Self {
240        self.deps.fallback = fallback;
241        self
242    }
243
244    /// 设置工具重试策略。
245    pub fn retry_policy(mut self, policy: RetryPolicy) -> Self {
246        self.config.retry_policy = policy;
247        self
248    }
249
250    /// 设置上下文预算(Token 上限 + 压缩策略)。
251    /// 若要关闭限制,设置 `max_tokens = usize::MAX`。
252    pub fn context_budget(mut self, budget: ContextBudget) -> Self {
253        self.config.context_budget = budget;
254        self
255    }
256
257    /// 构建 ToolUseLoop。
258    ///
259    /// 将静态工具和动态目录坍缩为最终的 `ToolCatalog`,
260    /// 传给 `ToolUseLoop::new()`。
261    pub fn build(self) -> ToolUseLoop {
262        // 构造优先级队列:本地静态工具永远拥有最高优先级
263        let mut sources: Vec<Arc<dyn ToolCatalog>> = Vec::new();
264
265        if !self.static_tools.is_empty() {
266            sources.push(Arc::new(StaticCatalog::from_tools(self.static_tools)));
267        }
268
269        sources.extend(self.catalogs);
270
271        // 坍缩成最终的单根 Catalog
272        let final_catalog: Arc<dyn ToolCatalog> = match sources.len() {
273            0 => Arc::new(StaticCatalog::empty()),
274            1 => sources.remove(0),
275            _ => Arc::new(CompositeCatalog::new(sources)),
276        };
277
278        let executor =
279            ToolExecutor::with_retry_policy(final_catalog, self.config.retry_policy.clone());
280        ToolUseLoop::new(self.model, executor, self.config, self.deps)
281    }
282}