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: 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 输出任何涉及文件修改或命令执行的结果前,必须通过校验器的检查。
更多推荐



所有评论(0)