AI Agent 的工程化实践:从 prompt 调试到系统化评测的方法论

AI Agent 的工程化实践:从 prompt 调试到系统化评测的方法论
AI Agent 的工程化实践从 prompt 调试到系统化评测的方法论一、prompt 调参的地狱为什么试了 50 遍还是不行agent 的第一个版本很简单把 PR 的 diff 文本拼进 prompt让 GPT-4o 给 review 意见。第一个 prompt 大概长这样你是一个代码审查专家。请审查以下代码变更找出潜在问题。 {diff}结果是什么样的呢agent 会指出变量名可以改得更语义化对但漏掉了这段代码有 SQL 注入风险致命还会对着完全正确的代码凭空捏造 bug幻觉。我尝试了各种 prompt 技巧加 system prompt、给几个示例、要求结构化输出……效果时好时坏。改一个 prompt 词某些 case 变好了另一些 case 又变差了。这就是 prompt 调参地狱两周后我意识到没有评测集的 prompt 调参就是在盲飞。你必须先有一个固定的测试集每次改 prompt 后跑一遍全量测试才能知道到底有没有改进。二、构建评测集最难也是最必要的一步我花了整整一周手工构建了一个 200 条代码片段的评测集。每条包含代码 diff模拟真实 PR 的变更正确 review由我和一位 senior 工程师共同标注关键分类标签安全漏洞 / 性能问题 / 代码规范 / 逻辑错误 / 无问题// // 评测集的数据结构 // use serde::{Deserialize, Serialize}; /// 一条评测用例 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EvalCase { /// 用例 ID方便追踪哪条没过 pub id: String, /// PR diff 内容 pub diff: String, /// 预期 review 结果人工标注的正确答案 pub expected: VecReviewIssue, /// 这条用例的难度easy / medium / hard pub difficulty: Difficulty, /// 问题类别标签 pub category: IssueCategory, } /// Agent 输出的 review 意见 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ReviewIssue { /// 严重程度 pub severity: Severity, /// 问题描述 pub description: String, /// 涉及的文件和行号 pub location: OptionSourceLocation, /// 修复建议 pub suggestion: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Difficulty { Easy, Medium, Hard } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum IssueCategory { Security, Performance, CodeStyle, LogicError, NoIssue, }光有数据集还不够还需要定义评测指标// // 评测指标计算 // pub struct EvalMetrics { /// 召回率真实问题中被 agent 找出来的比例 pub recall: f64, /// 精确率agent 说的问题中真正是问题的比例 pub precision: f64, /// F1 分数recall 和 precision 的调和平均 pub f1: f64, /// 幻觉率agent 凭空捏造问题的比例 pub hallucination_rate: f64, } impl EvalMetrics { pub fn compute(predictions: [ReviewIssue], ground_truth: [ReviewIssue]) - Self { let true_positives /* 计算真正例 */; let false_positives /* 计算假正例幻觉 */; let false_negatives /* 计算假反例漏报 */; let precision true_positives as f64 / (true_positives false_positives) as f64; let recall true_positives as f64 / (true_positives false_negatives) as f64; let f1 2.0 * precision * recall / (precision recall); EvalMetrics { recall, precision, f1, hallucination_rate: false_positives as f64 / predictions.len() as f64, } } }三、有了评测集后prompt 优化变成科学实验评测集 build 好之后prompt 优化就不再是盲飞了。我遵循了一个标准流程举个例子通过分析评测结果我发现 agent 在性能问题类别上的 recall 只有 35%。分析失败用例后发现很多性能问题是关于N1 查询模式的agent 需要在更广的上下文中才能发现。于是我在 prompt 里加入了一条规则当看到循环中包含数据库或 API 调用时必须检查是否存在 N1 查询问题并标记为 Performance / High 严重度。加上这一条后性能问题的 recall 从 35% 跳到了 68%。// // Prompt 版本管理和 A/B 测试的简单实现 // pub struct PromptVersion { pub version: String, pub system_prompt: String, pub user_prompt_template: String, pub metrics: OptionEvalMetrics, } impl PromptVersion { /// A/B 对比判断新版本在所有维度上是否都优于旧版本 pub fn is_strictly_better_than(self, other: PromptVersion) - bool { match (self.metrics, other.metrics) { (Some(a), Some(b)) { a.f1 b.f1 a.recall b.recall a.precision b.precision } _ false, } } }四、从 prompt 调优到多阶段 pipeline随着评测指标逐步提升我发现单次 LLM 调用的天花板到了——precision 到 85% 就上不去了。于是我把 agent 拆成了多阶段 pipeline第一阶段分类器判断这条 PR 是否需要 review跳过纯文档或配置变更。第二阶段问题检测扫描代码 diff列出所有潜在问题。第三阶段严重度判断对每个问题做 severity 分级。第四阶段去幻觉用代码静态分析工具Clippy、cargo check验证 agent 发现的问题是否真的存在。加了第四阶段后幻觉率从 12% 降到了 3.5%。但第四阶段也有代价每次审查多了 200-400ms 的 clippy 执行开销。后来我们做了缓存——同一个 commit hash 的代码不再重复跑 clippy增量耗时降到 50ms 以内。多说一点Claude 3.5 做代码 review 的幻觉率3.5%比 GPT-45.1%低不少选模型本身就是一个重要的优化手段。五、总结从 prompt 调参地狱到系统化评测我总结的方法论是没有评测集就不要调 prompt。评测集是唯一的锚点没有它你永远不知道是在进步还是退步。评测集必须人工标注。不能让 LLM 自己给自己出题改卷那叫自欺欺人。单次 LLM 调用的天花板很低。要提升到 85% 的准确率必须用多阶段 pipeline 代码分析工具作为安全网。Prompt 版本管理要像管理代码一样严肃。每个版本记录 prompt 内容 评测指标确保可以回溯。这套方法论不只适用于代码 review agent任何需要用 LLM 做判断的场景都适用。