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: HashMap<Intent, Vec<&'static 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: Vec<ThoughtStep>, /// 最终决定采取的行动 action: Option<AgentAction>, } /// 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) -> Result<String, String>; } // inventory 在编译期收集所有实现了 Tool 的类型 inventory::collect!(Box<dyn 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) -> Result<String, 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 Box<dyn 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_backoff<F, Fut, T, E>( config: &RetryConfig, mut operation: F, ) -> Result<T, E> where F: FnMut() -> Fut, Fut: std::future::Future<Output = Result<T, 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 输出任何涉及文件修改或命令执行的结果前,必须通过校验器的检查。