ARTICLE DETAIL

资讯详情

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

从代码问答到任务执行:AI编码助手羲和的工程化实践

从代码问答到任务执行:AI编码助手羲和的工程化实践 “羲和”这个名字听起来就带着一股执拗劲儿。作为一个天天跟代码打交道、又被各种 AI 工具“教育”过的人我太清楚市面上那些所谓的 AI 编程助手有多分裂了——问它一段代码是什么意思它能给你讲得头头是道真要让它动手改个 bug、加个功能又常常答非所问甚至直接把项目结构搞乱。所以当我自己动手设计这个名叫羲和XiheAgent的 AI 编码助手时核心目标就一个让 AI 从“能回答问题”进化到“能真正执行任务”。这篇文章不聊那些虚的架构图也不做概念科普我就从实际设计和实现的角度把羲和从代码问答到任务执行的完整链路拆开来讲。里面涉及代码检索、上下文管理、Agent 工具调用、安全沙箱这些实打实的环节也有我踩过的坑和调优心得。如果你是做 AI 应用开发的工程师或者正在纠结怎么给自己的项目接一个“靠谱”的编码助手这篇内容应该能帮你省下不少摸索时间。1. 整体设计思路为什么代码问答和任务执行必须分两层很多人以为 AI 编码助手就是一个聊天框接上大模型 API 就完事了。真这么干过的人都知道模型对单个文件的理解可能还不错但一旦涉及整个项目的依赖关系、多文件联动甚至要自动修改代码纯靠“问一句答一句”根本撑不起来。羲和的设计里我一开始就把能力拆成了两层理解层代码问答和执行层任务执行这两层各自解决不同的问题也各自有独立的实现逻辑。1.1 理解层解决的核心痛点模型怎么“看懂”项目代码问答的本质是把模型变成一个“读过你整个项目”的专家。但这里有个天然的矛盾大模型的上下文窗口再大也不可能把一套中型项目的几十万行代码全部塞进去。于是问题就变成了——怎么让模型在有限上下文里精确找到回答用户问题所需要的代码片段。我的思路是参考 RAG检索增强生成的框架但不照搬通用文档检索那套做法。通用 RAG 把文本切块存向量库就行代码不行。代码有语法结构、有依赖关系、有函数调用链你把一个函数体从整个类里切出来单独存检索的时候很容易丢失上下文。所以羲和的理解层做了一套“代码结构感知”的索引方案后面我会详细讲具体怎么做。另一件容易被忽略的事是代码问答不只是“找到相关代码”这么简单。用户问“这个项目的登录流程是怎么实现的”模型需要的不只是登录函数本身还包括路由配置、中间件、数据库里用户表的字段定义、前端调用接口的路径。这要求系统不仅要检索代码文本还要能沿着调用关系做“推理式检索”把相关的上下游代码一并找出来。我试过只做向量相似度检索的方案效果差强人意后来改成“结构索引 向量召回 调用链扩展”三步走回答质量才真正上了一个台阶。1.2 执行层解决的真正难题让模型“敢动手”且“不闯祸”任务执行的难度比问答高一个量级。问答阶段模型说错了顶多就是回答不准确执行阶段模型改错一行代码、删错一个文件后果是实打实的。所以执行层的核心不是“能不能调工具”而是“怎么让模型在可控范围内完成任务”。我给羲和定的执行原则是“最小动作 可回滚”。所谓最小动作就是模型每一步操作都尽量小——改一个函数就只改这个函数不顺手牵羊去动无关的 import新增一个文件就只加这个文件不悄悄改掉公共配置。可回滚是底线任何修改在执行前都会自动创建 git 快照或者文件备份执行完毕后再做差异对比确认改动符合预期。这里有个很实际的经验Agent 设计上千万不要让模型一步到位输出“最终 diff”。那种一步到位的方式表面上效率高实际上一旦出错你根本不知道是模型的推理错了还是执行环境出了问题。羲和采用的方式是“计划-执行-验证”三步循环模型先输出一个行动计划系统逐项执行每执行完一步就自动跑编译或测试做验证只有验证通过才继续下一步。这套循环跑起来之后任务执行的成功率从最初的不到一半提升到了可以实际投入使用的水平。2. 代码问答层实现从文件检索到上下文组装的关键细节代码问答是羲和的地基地基打不牢上层执行层就是空中楼阁。这一节我把理解层的关键实现细节拆开讲包括代码索引怎么做、检索怎么组织、上下文怎么组装每一步都会说明为什么这么做。2.1 代码索引怎么做先建语法树再做向量化最开始的版本我直接把代码按行切块塞进向量库用 embedding 模型做相似度检索。测试下来发现两个严重问题第一代码里大量的标识符变量名、函数名相似度很高向量检索经常召回一堆“看起来像但根本不是”的代码第二代码的语义很多在结构关系里而不在文本表面——比如一个函数调用了另一个定义在别处的函数纯文本切块根本抓不住这个关系。后来我换成了“先结构后向量”的方案用 tree-sitter 解析项目源码生成语法树然后基于语法树提取出每个函数、类、模块的签名和依赖关系存成一份结构索引。这份索引回答了两类问题一是“某段代码在哪里”二是“这段代码依赖了哪些其他代码”。向量检索当然还要用但它的定位变成了“语义召回的第一道筛子”召回结果必须经过结构索引的校验和补全才能进入最终上下文。具体操作上每个函数的索引信息我记录了这么几项文件路径、函数名、入参出参、调用的函数列表、被哪些函数调用、类名如果有、以及函数体被截断后的摘要。这里有个细节很多人会忽略函数体摘要不能只取前几百个字符应该同时保留函数的中段和结尾——很多关键逻辑比如错误处理、返回值处理恰恰在函数末尾。2.2 检索策略相似度召回与调用链扩展的配合检索层面我跑通了这样一条流水线。用户提出一个问题后系统先用 embedding 做一次全局相似度召回拿回最相关的 20 到 30 个代码片段。这 20 到 30 个片段里可能直接命中了用户想找的函数也可能只是文字上相近但实际无关。接下来就需要结构索引出场对召回结果里的每个函数沿着“调用它的函数”和“它调用的函数”两个方向各展开一层把相关代码一并拉入候选集。为什么要展开两层以上就不太合适了呢因为调用链太长时上下文会迅速膨胀模型反而分不清哪些代码是直接相关的。我试过展开三层回答质量没有显著提升但 token 消耗涨了接近一倍。最终选定了“召回 一层扩展”作为标准配置。当然如果你的项目里函数依赖极深可以通过参数动态调整展开深度但默认一层的性价比在大多数项目里是最高的。候选集产生之后还有一道重排序的工序。这一步我用了两路信号一路是结构相关度判断候选代码和用户问题涉及的模块是否在同一调用链上另一路是关键词覆盖度用户问题里的业务词汇是否出现在代码注释或标识符中。两路信号加权合并之后取前 8 到 10 个片段作为最终携带进上下文的代码。实测下来这道重排序比单纯依赖 embedding 的相似度排序在“准确命中用户意图”上提升了大概三成的效果。2.3 上下文组装把代码片段拼成模型能读懂的“项目简报”光有代码片段还不够模型还要知道这些代码之间的关系否则它看到 10 个零散的函数体依然不知道谁调谁、谁属于哪个模块。所以上下文组装是一个不可跳过的环节。我会把最终选定的代码片段拼接成这样一份结构化的“项目简报”先是项目概览——这是一个什么语言的项目用了什么框架核心目录结构长什么样。然后是代码片段清单每段代码前标注文件路径和它在项目中的角色。最后是关系说明——哪几个函数构成了主调用链哪个类是核心数据模型。这些信息拼装完成后才作为系统提示词的一部分喂给模型。这里有一个值得反复试验的细节代码片段的完整性。一开始我为了省 token对过长的函数做了截断处理只保留签名和开头几十行。结果模型的回答频繁出现“臆测”——因为它没看到函数末尾的真实返回逻辑。之后我改成“宁可少带几个文件也要保证选中的文件代码完整”。上下文总量不超限的情况下完整代码带来的准确性提升远比多塞几个半截文件要划算。2.4 多语言项目的处理统一结构索引别让语言差异干扰语义现在的项目很少是纯单语言的。一个 Web 项目后端是 Python前端是 TypeScript配置文件可能是 YAMLSQL 脚本单独放一层。我最初给每种语言都建了独立的检索通道后来发现维护成本太高效果也没有明显提升。最后统一成一套方案tree-sitter 支持的语言都走结构索引不支持的配置类文件比如 YAML、JSON退化成按段落切块做常规向量化处理。实际使用中跨语言检索最容易出的问题是在前后端接口对齐的场景。比如用户问“前端登录请求到底传了什么参数”系统需要同时命中前端的 API 调用代码和后端的请求处理函数还要让模型看到它们之间的对应关系。我的做法是在结构索引里额外维护一个“接口调用表”将 HTTP 路径作为关联键把前端的请求构造点和后端的路由处理函数关联起来。有了这层关联跨语言问答的准确率明显上来了。3. 从问答到执行Agent 架构与工具调用的工程化落地如果说代码问答层解决的是“看懂”的问题那么执行层要解决的就是“做对”和“做安全”。这一节我从 Agent 的工作流程、工具集设计、执行安全三方面讲重点谈为什么某些设计选择“反直觉”但更可靠。3.1 Agent 核心循环计划-执行-验证每一步都要留痕我在 1.2 节提过三步循环这里展开讲实现。整个 Agent 循环可以简化成这样一个状态机模型产出一个计划计划状态系统按计划调用工具执行状态每次执行后自动运行验证验证状态验证失败则把错误信息反馈给模型让它修订计划修订状态直到通过或达到最大尝试次数。这个循环最关键的地方是“留痕”。每一步计划、每一次工具调用的输入输出、每一个验证结果都要记录成结构化日志。这不仅是做审计用的更重要的是拿这些日志当模型的“记忆”。当一个执行任务步骤很多时模型很容易忘记自己前面已经做过什么。我会在每轮循环启动前把前面的操作摘要连同当前状态一起拼进提示词相当于给模型一个“操作备忘录”。没有这个备忘录的 Agent执行到第五步第六步时经常开始重复劳动甚至做出和前面操作互相矛盾的行为。第二个经验是计划要“小而具体”不要“宏大而抽象”。模型第一次输出的计划往往是“修改用户模块并补充测试”这种粒度这没法执行。我要求模型每次只给出下一步最多三个原子动作比如“定位文件 src/auth.py 中第 120 行的 verify 函数”“打印该函数前后 30 行代码”。把大计划拆成原子动作好处是每一步都容易验证对错模型在任何一步出错时修正成本都很低。3.2 工具集设计只给 Agent 够用的“手”不给多余的“刀”工具调用是 Agent 和真实世界交互的接口。这个接口设计得宽泛Agent 灵活但危险设计得狭窄安全但笨拙。羲和目前暴露给 Agent 的工具集经历了三轮精简最终稳定在这么几个核心工具上代码检索工具按符号名、文件路径、调用关系定位代码这和问答层的检索是同一套底层能力文件读写工具支持读取文件指定范围、基于行号或上下文锚点追加/修改代码命令行执行工具在项目目录内执行测试、构建命令比如 pytest、npm run build但限制了执行权限和网络访问版本操作工具自动创建分支或快照支持回滚到任意操作点我特别想说一下“为什么不给 Agent 任意执行命令的权限”。有一次测试我临时给 Agent 开了完整的 Shell 权限它为了获取项目依赖列表直接执行了 pip 全局安装。虽然那次没造成实质破坏但暴露了一个问题模型对“当前环境是隔离的还是真实的”并没有稳定的判断力。所以最终版本里所有命令行工具都在 docker 沙箱里运行而且只允许白名单命令。任务执行完生成的 diff仍然统一回到宿主项目里人工确认。这套机制的麻烦之处在于沙箱和宿主环境的一致性维护好处是——目前为止执行层没有出现过一次破坏性事故。3.3 执行安全diff 审查、权限分级与自动回滚安全设计不是上线后才补的而是从第一版就要有的骨架。羲和的安全体系分三层第一层是权限分级。我把可执行的操作分成了“只读操作”和“写操作”。只读操作查代码、读文件、跑测试Agent 可以自主执行写操作改文件、执行安装命令、创建新文件需要经过一个“风险评分”判断。低风险的写操作比如修改单个函数体且改动行数小于 20 行可以直接执行但会记录日志中高风险操作比如修改配置文件、删除文件会暂停执行等待人工确认。第二层是 diff 审查。任务执行完成后系统自动生成一份完整的 diff 报告按文件分组展示每一处改动并标注出模型自己声明的改动原因。这份报告连同执行日志一起作为人工审查的核心材料。我自己的使用习惯是即使完全信任 Agent 的执行结果也会在合并前花两分钟扫一遍 diff 报告。很多问题——比如老代码里暗含的一个特殊边界条件——模型注意不到但人眼在 diff 里扫一眼就能发现。第三层是自动回滚。每次任务执行前系统自动创建一个 git 备份分支执行过程中每完成一个原子动作就记录一个回滚点。万一执行到一半发现方向错了可以一键回到任意回滚点而不是只能整个任务推倒重来。这个粒度非常重要尤其是长时间执行的任务——如果只有任务级回滚中间正确完成的部分也得跟着作废。4. 实操实录模型选型、提示词模板与效果调优过程理论说得再漂亮落到模型选型和提示词这些具体环节才是真正让人头疼的部分。这一节记录我在实际搭建羲和过程中反复试验后沉淀下来的选型逻辑和调优方法尽量给出可以直接参考的参数和模板。4.1 模型选型通用对话模型做问答代码专用模型做执行市面上的大模型很多但不存在一个模型在所有维度都最优。羲和实际运行中用了两个模型配对问答层用通用能力较强的对话模型兼顾理解和表达执行层用代码能力更扎实的模型专注 diff 生成和工具调用决策。为什么这么分代码问答需要的是“理解 解释”对模型的推理深度要求高同时希望它的语言表达足够自然用户看着不费劲任务执行需要的是“精准定位 低错误率”代码生成能力、长上下文保持能力更重要甚至宁可牺牲一点表达流畅度。同一个模型很难在这两个维度上都做到顶尖。分开以后每一层都能根据自己的需求选性价比最高的模型成本上反而更划算。关于上下文长度我建议至少选 32K 以上的模型但不要迷信长上下文。羲和的最大上下文峰值大概 24K token其中系统提示词和“操作备忘录”占了一半。我试过把整份检索报告全塞给模型发现超过 20K token 后模型对早期内容的“记忆”显著变差。这不是模型窗口不够而是注意力瓶颈。所以哪怕窗口再大我也会做一层压缩合并重复代码片段、剪掉与任务无关的注释、将长函数体替换成摘要保证喂给模型的内容精炼但关键部分完整。4.2 提示词模板系统提示词决定模型的“职业素养”提示词工程在这个阶段没有秘密可言核心就是把你想让模型遵守的规则写清楚。我分享两个实际在用的模板要点一个管问答一个管执行。问答层的系统提示词核心约束有三条第一回答必须基于提供的代码片段如果片段中没有明确信息必须说明“代码中未直接体现”禁止臆测第二回答中涉及代码位置时必须给出文件路径和具体行号第三如果用户问题是调试类问题先描述问题可能原因再给出排查步骤最后才是修改建议。这三条约束的作用是让模型保持“工程师思维”而不是“教科书思维”。执行层的系统提示词我会额外加入几条操作规则只能使用提供的工具不得使用未声明的命令每次修改前先调用检索工具确认当前代码状态修改后必须说明验证方式遇到不确定的情况停下并询问用户不要擅作主张。其中“不确定就停下”这条特别重要。模型在执行任务时有一种“为了完成任务强行继续”的倾向哪怕它已经意识到信息不足。把“允许停下询问”写进规则后执行过程的无效操作明显减少。4.3 效果调优用真实项目跑回归别用几个 demo 案例自欺欺人调优阶段最大的坑是拿自己精心设计的 demo 案例当评测集。demo 案例模型早就见过类似的跑得再顺也不能说明问题。我后来搭了一套评测集选了几个真实的中型开源项目每个项目准备 30 个左右的任务涵盖代码解释、bug 定位、单点修改、跨文件功能实现这几类。每次调优后就用这套评测集跑一遍回归统计任务完成率、首次成功率、平均执行步骤数、人工干预次数这几个指标。这里分享一个具体的数据变化第一版执行层首次成功率只有 38%主要原因就是模型经常定位错文件加入结构索引做前置检索后首次成功率爬到了 57%再引入“操作备忘录”解决长任务失忆问题后成功率稳定在 68% 左右。剩下的失败案例里相当一部分是模型对项目业务语义的理解不足——它知道代码是怎么写的但不清楚这个项目的业务规则为什么这么定。这种情况我会把相关的业务上下文接口文档、README 里的需求说明也纳入检索范围成功率又可以往上走一小截。调优过程中我也得到一个教训不要频繁调整提示词的措辞。有一次我为了提升一个案例的表现连续加班微调提示词结果那个案例是过了但其他案例的通过率掉了一截。后来我改成“批量改动 全量回归”的模式每次只固定一个优化方向积累 5 到 10 个改动后统一跑一次回归。这样既保证了调优的节奏也避免了“按下葫芦浮起瓢”。4.4 成本控制token 消耗集中在哪就从哪里省钱最后一个实操话题是成本。AI 编码助手的成本大头在模型推理问答层相对好控制执行层因为多次循环迭代token 消耗往往是问答层的 10 倍以上。我在控制成本上做了三件事一是在检索层做严格筛选坚决不让低相关片段进入上下文。每省掉一个无关文件就是省掉几百到几千 token。二是在执行循环里引入“压缩摘要”——每轮循环结束后把上一轮的详细工具输出替换成一句话摘要避免上下文无限膨胀。三是问答层和任务层分开计费偶尔用一次的任务执行用高质量高价格的模型没问题高频的代码问答则可以用延迟稍高但更便宜的模型整体成本能压到原来的六成左右。当然成本优化不能牺牲正确率。我的原则是涉及代码修改的关键路径优先保证正确率token 贵一点没关系涉及辅助性问答比如解释概念、梳理流程的环节才考虑用低成本模型。把正确的场景分给正确的模型才是真正的省钱。5. 踩坑指南代码问答与任务执行中的典型问题排查最后这部分我把我实际遇到的问题按“高频 高破坏性”的标准筛了一遍挑出最有代表性的 8 个每个都给出排查思路和解决方案。这些问题如果你也在做类似的东西大概率会遇到。5.1 代码检索类问题问题一向量检索召回了相似但无关的代码。这个最常见尤其是项目里存在大量重复模式比如一堆结构相似的 API 接口函数时极易发生。排查步骤是先看召回结果是不是集中在少数几个文件——如果是说明 embedding 被标识符相似度带偏了。解决方法是加强结构索引让“调用链扩展”成为候选集主要来源向量召回只作为兜底。问题二跨语言调用链断裂。前端调后端接口、Python 调 C 扩展这类场景结构索引在语言边界处经常对不上。排查时先确认 tree-sitter 是否正确解析了两种语言的文件再看接口调用表里有没有对应的路径映射。如果确认是手动维护的映射表有遗漏就把新增接口注册做成自动化脚本减少人工维护的滞后。5.2 上下文与模型表现类问题问题三模型“忘记”了前面的操作步骤。长任务执行到后半段模型开始重复已经做过的修改或者对当前文件状态产生错误判断。这个问题的根源不是模型变笨了而是关键的状态信息没有被显式传递。解决方案就是我前面提到的“操作备忘录”每轮循环都把最新的文件状态摘要和已完成操作列表放在提示词的显眼位置。实测这个改动对长任务成功率的提升非常显著。问题四模型给出的修改与项目既有风格不一致。比如项目里全用类型注解模型生成的代码却没有。这类问题模型自己是意识不到的需要在系统提示词里显式说明项目风格约束。我的做法是在项目接入羲和时自动扫描项目的代码风格特征是否用类型注解、缩进规范、命名习惯生成一份“风格备忘”注入提示词。这个功能看起来朴素但对代码评审体验的改善是立竿见影的。5.3 执行与安全类问题问题五沙箱内测试通过宿主环境却跑不起来。沙箱里的依赖版本、环境变量和宿主不一致是执行层最隐蔽的坑。排查时先对比沙箱和宿主的依赖锁定文件再看是不是有环境变量或系统级依赖差异。解决思路是把依赖安装做成沙箱启动时的强制步骤并定期同步宿主锁定文件到沙箱镜像。问题六并发执行任务互相干扰。多个用户同时触发任务执行时如果共用一个工作目录文件状态很容易互相污染。我在这个问题上交过学费后来强制改成“每个任务独立工作副本”的模式任务之间物理隔离才彻底解决。代价是磁盘占用上升但稳定性优先。5.4 测评与调优类问题问题七评测集跑得好真实场景却拉胯。很多时候是因为评测集里的问题描述比真实用户更“规范”真实用户的问题往往模糊、信息不全。解决方法是定期从真实使用日志中抽取问题筛选后加入评测集让评测集保持对真实分布的覆盖。问题八回归测试发现某次改动让多个案例同时失败但改回去又有别的案例失败。这通常说明某个中间方案本身设计得过于耦合而不是单个改动的问题。我的处理方式是用二分法锁定是哪个改动引入的回归然后直接重构那段逻辑而不是在补丁上打补丁。编码助手项目本身也需要用工程方法来治理这句话我实践下来深有体会。我个人在实际操作中的感受是AI 编码助手的开发功夫不在“接入模型”这一步而在接入之后那些看不见的工程细节里——检索的组织方式、上下文的取舍、执行安全的边界、调试时的排查节奏每一样都是积少成多的打磨。羲和目前还在持续演进比如把多模态图表识别接入问答层、让执行层支持更复杂的多文件重构任务这些都是后续可以继续展开的方向。如果你也在做类似的项目希望这篇内容能给你一些参照。
返回列表