AI Agent 设计模式总结:7 月实践提炼的 5 种可复用模式的系统归纳

发布时间:2026/7/27 10:50:16
AI Agent 设计模式总结:7 月实践提炼的 5 种可复用模式的系统归纳 AI Agent 设计模式总结7 月实践提炼的 5 种可复用模式的系统归纳一、Agent 的三层架构和 5 种模式的地图在讲具体模式之前先说说 Agent 的总体架构。一个 AI Agent 系统可以分为三层规划层决定做什么、执行层具体去做、反馈层检查做得怎么样。5 种模式分布在这三层之中二、规划层模式意图路由与 Chain-of-Thought模式一意图路由Intent Router这是最基础也最重要的模式。用户输入帮我查一下 git log和这段代码有什么 bug需要调用的工具完全不同。意图路由就是根据用户输入的特征决定走哪条处理路径。// // 模式一意图路由 — 根据输入特征分发到不同的 handler // use std::collections::HashMap; /// Agent 指令的类型 #[derive(Debug, PartialEq)] enum Intent { /// 代码相关查 bug、解释代码、优化建议 CodeReview, /// Git 操作log、diff、commit、push GitOperation, /// 文件操作读、写、搜索 FileOperation, /// 通用问答闲聊、知识查询 GeneralChat, } /// 意图路由器 — 用关键词匹配做快速分类 /// 生产环境可以用 embedding 做更精准的语义匹配 struct IntentRouter { // 每个意图关联一组触发关键词 rules: HashMapIntent, Vecstatic str, } impl IntentRouter { fn new() - Self { let mut rules HashMap::new(); // 注册各意图的关键词 rules.insert(Intent::CodeReview, vec![ bug, 错误, 修复, 优化, 代码, 重构, review ]); rules.insert(Intent::GitOperation, vec![ git, commit, push, log, diff, branch, merge ]); rules.insert(Intent::FileOperation, vec![ 文件, 读, 写, 查找, 搜索, 打开, 保存 ]); IntentRouter { rules } } /// 根据用户输入匹配意图 /// 返回最佳的意图类型 fn route(self, user_input: str) - Intent { let input_lower user_input.to_lowercase(); // 遍历所有规则找到匹配关键词最多的意图 let mut best_match Intent::GeneralChat; let mut max_score 0usize; for (intent, keywords) in self.rules { let score keywords.iter() .filter(|kw| input_lower.contains(*kw)) .count(); if score max_score { max_score score; best_match intent.clone(); // 实际项目中用 Copy trait } } best_match } /// 根据匹配到的意图生成对应的 system prompt fn build_system_prompt(self, intent: Intent) - String { match intent { Intent::CodeReview 你是一个代码审查助手专注于发现 bug 和提供优化建议。.to_string(), Intent::GitOperation 你是 Git 操作专家帮助用户管理版本控制。.to_string(), Intent::FileOperation 你是文件系统助手帮助用户管理本地文件。.to_string(), Intent::GeneralChat 你是通用 AI 助手友好地回答各种问题。.to_string(), } } }意图路由的复杂度可控——最早可以只用关键词匹配后面逐步升级到 embedding 向量检索。关键是在最开始就把路由作为一个独立组件抽象出来不要把它写在 main 逻辑里。模式二Chain-of-Thought 的工程化实现Chain-of-Thought思维链听起来像 Prompt Engineering但它其实可以工程化。核心思想是让 Agent 在调用工具之前先用一段结构化文本描述它的推理过程。// // 模式二Chain-of-Thought — 结构化的推理过程 // /// 思维链中的一步 #[derive(Debug)] struct ThoughtStep { /// 这一步在做什么观察、分析、决策 step_type: String, /// 这一步的具体内容 content: String, } /// 一次完整的 Agent 推理轨迹 #[derive(Debug)] struct ReasoningTrace { steps: VecThoughtStep, /// 最终决定采取的行动 action: OptionAgentAction, } /// Agent 可以执行的具体动作 #[derive(Debug)] enum AgentAction { /// 调用某个工具传入参数 InvokeTool { tool: String, args: String }, /// 直接回答用户不需要调用工具 Respond(String), /// 需要更多信息反问用户 AskUser(String), } /// 构建 CoT prompt引导模型按固定格式输出推理过程 fn build_cot_prompt(user_query: str, available_tools: [str]) - String { format!( r#你是一个有工具调用能力的 AI Agent。 用户提问{user_query} 可用工具{tools} 请按以下格式回复 思考[描述你当前在分析什么] 分析[基于思考得出的结论] 行动[选择一个工具并给出参数或直接回复] 规则 1. 如果问题可以通过工具解决使用对应的工具 2. 如果问题不需要工具直接回复 3. 如果需要用户提供更多信息反问用户#, user_query user_query, tools available_tools.join(, ) ) }这个模式的要点在于不是把一步一步思考写在 prompt 里而是把推理过程的格式固定下来让 Agent 的每次决策都有迹可循。当 Agent 做错时你去翻它的思考记录通常就能找到问题出在哪一步。三、工具注册与发现这是我在 Rust 里用得最顺手的模式——利用编译时 inventory 做工具自动注册。// // 模式三编译时工具注册 // inventory 在编译期收集所有注册的工具 // use async_trait::async_trait; /// 工具接口 — 每个工具实现这个 trait #[async_trait] pub trait Tool: Send Sync { /// 工具名称用于路由匹配 fn name(self) - str; /// 工具描述会注入到 Agent 的 system prompt fn description(self) - str; /// 工具的参数定义JSON 格式告诉 Agent 怎么传参 fn parameters(self) - str; /// 执行工具传入 JSON 参数返回执行结果 async fn execute(self, args: str) - ResultString, String; } // inventory 在编译期收集所有实现了 Tool 的类型 inventory::collect!(Boxdyn Tool); /// GitLog 工具 — 获取 git 提交历史 struct GitLogTool; #[async_trait] impl Tool for GitLogTool { fn name(self) - str { git_log } fn description(self) - str { 获取最近的 git 提交记录 } fn parameters(self) - str { r#{n: {type: number, description: 获取条数默认10}}# } async fn execute(self, args: str) - ResultString, String { let params: serde_json::Value serde_json::from_str(args) .map_err(|e| format!(参数解析失败: {}, e))?; let n params.get(n).and_then(|v| v.as_u64()).unwrap_or(10); // 实际执行 git log 命令 let output std::process::Command::new(git) .args([log, --oneline, format!(-{}, n)]) .output() .map_err(|e| format!(git 命令执行失败: {}, e))?; Ok(String::from_utf8_lossy(output.stdout).to_string()) } } // 在模块加载时注册工具 inventory::submit!(Box::new(GitLogTool) as Boxdyn Tool);编译时注册比运行时注册好的地方在于你不会漏掉一个工具。所有inventory::submit!的调用在编译时被收集到一个全局列表里Agent 启动时直接读取这个列表即可。四、可靠性保障重试退避与自我校验模式四重试与退避Agent 的外部调用API、命令执行、文件读写都可能失败。重试策略不是失败就再试一次而是要加入退避、抖动和最大次数限制。// // 模式四指数退避重试 // use std::time::Duration; use rand::Rng; /// 重试配置 struct RetryConfig { /// 最大重试次数 max_retries: u32, /// 基础等待时间毫秒 base_delay_ms: u64, /// 最大等待时间毫秒防止无限增长 max_delay_ms: u64, /// 是否添加随机抖动避免惊群效应 jitter: bool, } /// 带指数退避的重试执行器 async fn retry_with_backoffF, Fut, T, E( config: RetryConfig, mut operation: F, ) - ResultT, E where F: FnMut() - Fut, Fut: std::future::FutureOutput ResultT, E, { let mut attempt 0; loop { match operation().await { Ok(result) return Ok(result), Err(e) { attempt 1; if attempt config.max_retries { return Err(e); // 达到最大重试次数返回最后的错误 } // 指数退避delay base * 2^attempt不超过 max let delay config.base_delay_ms * 2u64.pow(attempt); let delay delay.min(config.max_delay_ms); // 可选随机抖动在 delay 的 75%~125% 范围内随机 let final_delay if config.jitter { let mut rng rand::thread_rng(); let jitter_factor rng.gen_range(0.75..1.25); ((delay as f64) * jitter_factor) as u64 } else { delay }; eprintln!( 第 {}/{} 次重试失败{}ms 后重试..., attempt, config.max_retries, final_delay ); tokio::time::sleep(Duration::from_millis(final_delay)).await; } } } }这个模式的要点是不要裸写重试把它封装成可复用的工具。同时区分可重试的错误网络超时、临时 429和不可重试的错误401 鉴权失败、参数错误。模式五自我校验Agent 生产环境最怕的是自信地给出错误答案。自我校验就是让 Agent 在输出结果前再做一次 quality check。这个模式的关键在于校验标准要简单明确。比如返回的 git log 是否包含至少一条记录、API 返回的 JSON 是否能正确解析。校验器不需要比 Agent 聪明但它必须比 Agent保守——宁愿 reject 一个正确的结果让 Agent 再确认一遍也不要放过一个错误的结果。五、总结7 月的 Agent 设计实践让我意识到Agent 不是聪明模型 一些函数调用而是一套设计良好的工程模式。模型负责语义理解模式负责可靠的决策和纠错。5 种模式的实用排序按 7 月使用频率和效果工具注册与发现模式三——每次 Agent 扩展能力都依赖它意图路由模式一——决定了 Agent 是否听懂用户重试与退避模式四——生产环境的保底Chain-of-Thought模式二——提高复杂任务的成功率自我校验模式五——长期来看最重要但 7 月只做了原型8 月的 Agent 目标是把自我校验做到生产级别让 Agent 输出任何涉及文件修改或命令执行的结果前必须通过校验器的检查。