ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI工程从零到落地:手搓代码审查Agent的完整实践

AI工程从零到落地:手搓代码审查Agent的完整实践 最近总是有人问我AI工程到底从哪下手。市面上那么多框架、平台、低代码工具看起来什么都能干可真到自己上手做一个能用的东西时又总觉得哪里不对——好像什么都知道一点动手却完全没有思路。所以我想写一篇真正“从零开始”的AI工程实践手记把我自己搭一个AI Agent完整项目时的思考、踩坑、选型和最终落地方案都整理出来。不是那种“三分钟搭一个AI应用”的营销文也不是贴一堆官方文档的翻译稿而是把我作为一个工程师从接受需求到最终交付的完整过程记录下来包括那些只有实际动手才会明白的坑。这篇内容适合谁适合已经会写代码、但是对AI工程化还处于“看过很多教程但没独立做过项目”阶段的人。也适合那些想给公司内部搭建AI工具但不知道从哪开始规划的团队。我会用我自己完成的一个真实项目作为主线——用CodeBuddy实现一个具备Harness Engineering能力的代码审查Agent把AI工程从设计到落地的每个环节拆开揉碎讲清楚。这里面既有Prompt工程的核心技巧也有Agent架构设计的决策过程还有多AI协作、工作流编排、测试评估这些通常要到生产环境才会遇到的问题。1. AI工程的整体设计与方案选型1.1 为什么强烈建议你从零手搓而不是套低代码平台我第一次接到这个需求时第一反应也是“要不要用现成的Agent平台”。毕竟市面上的选择太多了有些甚至宣称不需要写一行代码。但当我仔细想清楚需求后我意识到这个项目必须从底层开始搭原因其实很简单——我需要完全掌控AI的每一个行为边界。我的需求是做一个代码审查Agent它需要读取程序员提交的代码变更理解业务上下文调用各种分析工具最后输出一份包含逻辑缺陷、安全隐患、性能瓶颈的审查报告。如果我用低代码平台做首先面临的就是平台锁定问题。底层模型要升级时平台的适配速度是谁也保证不了的。更重要的是低代码平台对Prompt的可控性往往很差它们会有一层自己的抽象而这层抽象会把你的Prompt做各种隐式处理——等你发现输出不受控的时候各种奇奇怪怪的中间层已经让你的调试过程变成一场噩梦了。从零搭看起来“工程量大”但实际上整个系统的复杂度完全在自己的掌控范围内。我做了一个决定核心Agent逻辑我自己写模型调用层我自己封装工具调用协议我自己定义Prompt模板全部纳入代码仓库做版本管理。这其实是AI工程的第一个核心认知——Prompt就是代码它需要和普通代码一样被管理、被测试、被回滚。如果这个前提不成立后面所有关于稳定性和质量的话题都无从谈起。1.2 核心架构设计与关键依赖选型架构上我采用的是经典的“应用层编排 模型层调用 工具层执行”三层结构。这个结构本身不复杂但每一层的选型都藏着选择。模型层我选择了主流的GPT系列接口——原因无他工具链成熟、上下文管理方便而且函数调用Function Calling的正确率是当前市面上最高的这对于Agent的可靠性至关重要。如果你要做Agent应用模型的结构化输出能力直接决定了你整个系统的稳定性这不是“谁家智能分高”的问题而是“谁家的JSON不会三天两头出错”的问题。工具层我用了LangChain的Tool规范和Pydantic做参数校验。坦白说LangChain本身有各种被人诟病的地方但它的Tool抽象确实做得不错尤其是和Pydantic联合使用时的自动格式校验能帮我挡掉一大批“模型返回了格式正确但逻辑错误的参数”这类隐蔽Bug。如果你不想用LangChain完全可以自己定义一套Tool协议但前提是你得自己处理各种边界情况——我建议第一个项目还是站在巨人的肩膀上把精力分配到真正重要的问题上。编排层我用的是自研的一套状态机没有使用LangGraph或者其他现成的编排引擎。原因是我对系统的控制欲比较强而且代码审查这个场景的状态迁移是确定性的一个自研状态机构造足够清晰依赖也少。这里有一个重要心得对于状态迁移非常明确的场景自研比用编排框架效率更高但如果你的Agent有多条动态规划路径老老实实用LangGraph这类成熟方案吧你自己写状态管理会写到怀疑人生。2. Prompt工程与Agent核心机制详解2.1 让Prompt稳定输出的三条铁律做AI工程最核心的技能绝对不是“写一段看起来很牛的Prompt”而是“设计一套在任何情况下都能稳定输出的Prompt体系”。我的代码审查Agent第一版上线时模型回答质量时好时坏。坏的时候它会把“代码风格建议”和“逻辑漏洞”混在一起或者明明没有发现安全问题时偏要强行找一些问题来“证明自己有用”——这就是最典型的AI幻觉补偿现象。后来我把Prompt工程的核心收敛到三条铁律上。第一角色与任务边界必须唯一且不可交叉。我在System Prompt里明确规定你是代码审查助手只负责发现代码中的缺陷并提出修改建议不做代码补全不做代码优化建议之外的任何教学式输出。这个“唯一性”能极大减少模型的发散行为。第二输出格式规范必须与功能绑定而不是与“风格”绑定。我在Prompt里放了一段JSON Schema的描述明确要求对每个发现的问题输出问题类型、严重级别、影响函数、具体描述、修改建议。当模型知道“我必须填完这些字段”时它的输出质量和只给了“请你审查这段代码”的情况完全是两个层级。第三上下文管理要有预算。我截取代码时遵循“具体函数完整保留、无关代码只留函数签名”的原则。很多AI工程新人犯的最大错误是试图把所有源码一次性塞给模型以为信息越多越好。事实上过多的无关上下文会让模型丢失焦点还会吃掉昂贵的token预算。在接入代码检索工具之后我为Agent设计了一套“先检索后申请”的上下文管理协议——Agent必须先通过工具检索到相关函数确认需要后才会把完整代码读进来。2.2 Agent循环的四大核心环节一个Agent能稳定完成任务靠的是“规划Plan—记忆Memory—行动Action—反思Reflection”这四个环节的可靠循环。规划环节里Agent根据用户需求拆解任务产出待办清单记忆环节维护两类上下文一是会话内的短期记忆二是跨会话的长期经验沉淀行动环节就是调用外部工具执行具体操作反思环节则是让模型根据执行结果修正自己的下一步规划。我在这套机制里遇到的最典型问题是规划路径不稳定。同一段代码在不同的审查轮次里模型可能选择完全不同的分析顺序——有时先看安全再看性能有时先看性能再看安全。对于审查类任务这种不稳定的分析顺序会让报告质量出现明显差异。我的解决方案是在Prompt中给Agent一个有向的分析管道先做数据流追踪再做安全漏洞扫描再做性能热点分析最后做代码规范检查。每个阶段都必须完成后才允许进入下一阶段。看起来很机械但正是这种“机械”保证了输出的一致性。记忆方面我做了两层。短期记忆就是常规的对话历史但我会做显式的消息裁剪只保留最近3轮完整交互。长期记忆会更有意思——我把每次代码审查发现的高频问题类型做了聚合统计沉淀成一个“高频问题知识库”在后续审查时以附加参考的形式注入Prompt。实测下来这种方式比单纯调大模型参数对“精准打击”特定团队代码习惯的效果更好。3. 实操案例用CodeBuddy实现Harness Engineering的完整流程3.1 Harness Engineering到底在讲什么Harness Engineering约束工程这个概念最近在AI工程圈子里讨论得很多。简单说它强调的不是“让AI更自由地发挥”而是“让AI在明确定义的边界内高效工作”。你想象一下骑马——没有缰绳的野马跑得再快也不敢骑经过训练的良驹才可以托付重任。Agent工程完全同理如果一个AI Agent没有受控的行为边界、没有明确的工具使用协议、没有强制性的输出规范它表现得再聪明你也不敢让它接触生产数据。我做的这个代码审查Agent本质上就是一个Harness Engineering的完整案例。它的“缰绳”体现在三个层面第一它能调用的工具是预定义的只能执行git diff解析、代码检索、依赖扫描、规则引擎匹配这四类操作其他行为一律拒绝第二它的执行路径是约束好的必须按我设定的分析管道走第三它的输出格式是强制的必须产出结构化报告无法直接输出自由文本。3.2 CodeBuddy如何成为我的工程主力坦白说写完整个Agent框架有大量的代码生成和调试工作。这时候CodeBuddy派上了大用场。CodeBuddy具备几个和Harness Engineering结合特别紧密的能力代码库索引Codebase Indexing、多文件代码编辑Multi-File Editing、命令执行可视化Command Execution、以及AI问答。这几个能力组合起来让我“一个人完成了一个小团队的活”。我举一个真实的工作流我需要为代码审查Agent实现一个“跨函数数据流追踪”模块。这是一件非常繁琐的事情因为要在代码库中查找某个变量从一个函数传递到另一个函数的路径。我自己的流程是这样的先用CodeBuddy的代码库索引去定位所有相关的函数定义和调用点然后让它生成一个数据流追踪的核心逻辑接着用命令执行能力跑单元测试看到哪些用例失败后再让AI根据失败信息反向修改代码。整个过程迭代了差不多20轮但每一轮都有明确的目标和反馈信号。这里必须强调CodeBuddy不是我用来“自动写完全部代码”的魔法棒而是我的“结对编程搭档”。我把任务拆得很细下达的指令都是可执行的、范围明确的——这本身就是Prompt工程在编码辅助场景下的应用。比如说我不会说“帮我优化这段代码”我会说“找到analyze_data_flow函数中导致tainted_var跟踪丢失的条件分支修复并补充对应的单元测试”。这样明确具体的指令让AI辅助编程工具的产出质量产生了质的飞跃。3.3 核心模块实现细节与参数选择这里我把代码审查Agent的几个核心模块的关键实现要点列出来方便大家参考。Git Diff解析模块这个模块的作用是把git diff的输出解析成结构化数据。我用了unidiff库来做这个解析它能把diff解析成包含文件路径、Hunk位置、变更行号的规范结构。初始方案是直接用正则解析但真实场景的diff远比想象中复杂——合并冲突标记、二进制文件、rename检测都会让正则崩溃。这里有个参数值得关注上下文行数设置为3行也就是说每个变更块我们向模型展示前3行和后3行未更改代码。3行这个数值来源于我的实测——太少模型看不到函数语义太多token浪费明显。代码检索工具为了让Agent能按需获取更多上下文我封装了一个基于AST抽象语法树的代码符号索引工具。它的作用是把Python代码库中的所有函数、类、方法建立索引支持按函数名精确查询、按正则模糊搜索。另一个重要的设计是这个工具的返回内容做了“签名优先”的处理——只返回函数签名和docstring不返回函数体除非Agent明确要求“获取完整代码”。这能有效防止Agent一次性读取过多无关代码控制token消耗。规则引擎集成除了靠大模型“理解”代码我还加了一个经典的静态分析规则引擎用的是bandit——一个专攻Python安全问题的开源工具。让大模型做安全审查总会有漏网之鱼但静态分析工具的好处是“规则明确、不会漏报”。我把bandit的扫描结果结构化后注入Prompt作为Agent审查安全问题的“参考情报”实测下来这比让模型自己找安全问题的命中率高了很多。3.4 多AI协作与工作流编排实战做完了单一Agent我开始琢磨多Agent协作。因为代码审查这件事本身包含多个专业领域——逻辑正确性、安全性、性能优化、代码风格一个Agent把四件事都做完结果一定是在每个维度上都浅尝辄止。最终我采用了“一主多从”的主控/工作流模式对应到harness-qa-RG.biz这类团队的生产需求它体现为以下角色分工主控AgentCoordinator负责任务拆解、结果汇总、报告生成研究员AgentResearcher负责本地代码检索和外部知识查找代码专家AgentCode Expert负责深度逻辑推理和缺陷定位审查员AgentReviewer负责对前序Agent的输出做质量把控这套协作机制最核心的部分不是“让多个Agent聊天”而是用工作流来约束它们的表达方式。我用一个状态机定义了Agent之间的消息传递协议主控下发任务时必须包含任务ID、输入参数、预期输出格式子Agent返回结果时必须包含结果状态成功/失败/需补充信息、核心结论、支撑证据。有了这套协议Agent之间的信息传递永远是结构化的而不是自然语言导致的不确定性。两个Agent协作时我用了一个“争议仲裁”机制。当代码专家和审查员对某个问题是否属于“严重缺陷”有分歧时主控Agent会根据两个证据链的长度、来源可信度以及是否匹配预设的规则库来做客观裁决。这比让两个模型直接辩论更高效也更可控。4. 上下文窗口管理、工具调用与状态机设计4.1 上下文窗口的预算分配策略上下文窗口是你每个请求要付的钱更是模型输出质量的关键约束。我刚做第一版时把整个代码变更的所有文件全部塞进上下文结果模型输出经常“忘”了文件A里的问题。原因很简单——上下文被文件B的大量无关代码淹没了。我的解决思路是给上下文窗口做预算分配。对于代码审查场景我给上下文分配了这样一个比例每次请求时系统Prompt占10%工具调用结果占30%代码变更内容占50%历史消息占10%。当代码变更太大超过50%的预算时我会启用“分块审查模式”——把大变更拆成多个小变更分别审查最后在主控Agent里做结果汇总。这个方法配合CodeBuddy的上下文压缩能力能让Agent稳定处理的代码量提升3倍以上。4.2 工具调用返回值的结构设计工具调用是Agent连接真实世界的桥梁。但是工具返回什么、以什么形式返回这里面的设计大有讲究。我踩过最大的坑是让Agent直接使用工具返回的原始文本。当时我接了一个代码检索工具返回的是纯文本格式的代码段模型需要自己从里面找函数名、参数列表和返回类型。结果就是模型频繁“看错”函数名甚至把注释当成代码整个输出质量惨不忍睹。后来我把所有工具的返回值都强制为结构化的JSON格式。例如代码检索工具的返回值结构是这样的包含检索状态、匹配的函数列表、每个函数的签名、docstring、文件位置、以及可选的完整代码。结构化之后模型的解析负担大幅度降低输出准确率直线上升。这个经验我建议每个做AI工程的同学都记下来——工具层的关键目标不是“把原始结果丢给模型”而是“替模型做掉所有可以用确定性代码解决的解析工作”。4.3 Agent状态机的实践设计Agent的执行路径不能是纯自由发挥必须有一套状态机来托底。我设计的代码审查Agent状态机包含这些核心状态任务接收、任务分解、变更获取、数据流分析、安全分析、性能分析、规范检查、报告生成、结果反思。每个状态下Agent可以调用的工具集合是不同的——数据流分析状态下它只能调用代码检索工具安全分析状态下才能调用bandit扫描这样就杜绝了“Agent在错误场景下执行错误操作”的问题。状态迁移的条件大部分是确定的——比如“安全分析完成且bandit返回成功”就迁移到“性能分析”“性能分析完成”就迁移到“规范检查”。唯一的动态点是任务分解阶段Agent会根据变更文件的规模决定是否触发分块审查模式。这个设计让Agent有了结构性的保障即使模型的推理能力在某些极端场景下不太行状态机也能保证流程不乱。5. 评估体系搭建与问题排查实录5.1 建立Agent的“考试卷”——评测集设计做一个AI Agent如果只靠一两轮测试就说“完成了”那是自欺欺人。我花了不少精力搭了一套评测集收集了12个真实的代码变更案例每个案例都经过人工标注包含标准答案——应该发现哪些问题、问题严重级别是什么、对应的修复建议应该包含哪些要点。评测指标分三层。第一层是结构合规率检查Agent的输出能不能被解析、是否包含所有必需字段第二层是问题命中率以人工标注的问题为基准计算Agent发现的问题中有多少是真实命中第三层是误报率Agent提出但人工认为不是问题的算误报。这三个指标的加权得分是我判断Agent迭代是否成功的唯一依据。第一版Agent跑完评测数据不幸中带点幸运结构合规率92%问题命中率61%误报率27%。结论很明确Agent确实能发现不少问题但它“会说废话”的毛病比较明显。后来我在反思环节增加了一步“自我质疑”——在每个发现的问题后要求Agent两两对比是否可能属于同一根因重复的只保留贡献度最高的那个。这个简单操作让误报率从27%降到14%。5.2 高频踩坑实录与排查方法整理几个排查中发现的经典问题——“症状、原因、对策”一键三连症状一Agent在某些代码上表现完美某些代码上完全失智。排查发现是测试代码中包含了各种不常见的编码格式比如带BOM头的文件、混合换行符的文件。Git Diff解析模块在解析这些文件时产生了偏移错误。对策用chardet做编码检测对所有工具输入做“编码归一化”预处理。症状二Agent超时高发经常在同一个工具上来回调用。原因是循环检测机制不够——Agent在无法获取足够信息时会反复调用同一个检索工具每次换个关键词。对策设置工具调用“同工具最高次数3次”的熔断机制3次之后强制进入反思状态并提示模型更换检索策略。症状三模型输出的JSON偶尔出现“截断”。长文本输出时某些模型会在中途截断导致JSON解析失败。这个问题我用两招解决第一提示模型优先输出结论和关键证据细节放后面第二解析失败时自动触发一个“续写修复”调用把截断的部分拼接回去。实测下来修复成功率能到80%。5.3 关于Harness Engineering的终极避坑心得所有能把AI工程做到生产级别的团队基本上都在做同一件事让AI的自由度限制在一个足够小、足够明确的动作空间里。小到什么时候你会觉得它是“可控的”。再分享一个我个人的工程习惯。每次给Agent加一个新工具或新能力时我会先做“单一技术验证”——只测这个能力本身不混合已有功能。确认稳定后再逐步引入到主流程里。这个流程听起来很慢但能帮你省下大量后期排障的时间。AI工程做的越久越会意识到问题几乎都不是出在单个环节上而是出在多个环节交互时产生的不可控组合。6. 从第一版到可交付一个完整链路的回顾整个流程走完从接到需求到可交付我大概用了两周的业余时间。第一周搭框架、设计Prompt和工具层第二周做测试迭代和评估优化。最后交付的代码审查Agent能够稳定地在代码变更中识别出逻辑缺陷、安全隐患和性能瓶颈输出一份结构清晰的审查报告并且附上每个问题对应的代码位置和修改建议。我可以很负责任地说如果没有CodeBuddy这类AI辅助编码工具这个时间至少要翻一倍的。但如果你问我AI辅助编码工具是否直接“替我完成了”这个项目答案是没这回事。真正重要的还是你对架构的理解、对Prompt的精细设计和对系统的评测能力——这些思想层面的东西谁也替代不了。另外有一点感触很深就是AI工程和传统软件工程在思维上的最大差异。传统软件工程的核心是“消灭不确定性”——一切都要定义清楚、类型明确、流程固定。AI工程则是“把不确定性关进笼子里”——模型的输出天然是概率性的你要做的不是奢望它每次都正确而是设计一套机制让它在出错的时候不会造成不可挽回的后果。这层思维转换过来了AI工程才算真正入了门。如果你正在打算做自己的第一个AI项目我的建议是选一个足够小的场景小到你可以在一两周内跑通完整闭环然后认真把它做深做透。不要贪多求大一次只做一件事最好。代码审查Agent这个场景能在一篇文章的篇幅内把AI工程的主干环节全部串起来本身就是个很好的起点。你的第一个项目不必是这个但请务必从头到尾亲手走完一遍——设计、搭建、测试、迭代一个环节都不要少。
返回列表