ARTICLE DETAIL

资讯详情

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

从零实现AI编码助手XiheAgent:任务执行与架构实战复盘

从零实现AI编码助手XiheAgent:任务执行与架构实战复盘 “能聊代码”和“能动手改代码”之间隔着一整条工程链路。我整理羲和XiheAgent这份设计复盘的时候最大的感触就是代码问答只是入口任务执行才是真正让 AI 编码助手产生生产力的地方。市面上的助手大多停在了“给你讲一段逻辑”的层面但开发者的真实诉求往往是“帮我把这个 bug 修完”“把这两个模块重构一下”——这些动作光靠对话给不出答案必须让模型具备操作文件、执行命令、验证结果、按需回滚的能力。这篇文章是我自己从零实现 XiheAgent 的过程记录里面包含架构拆解、关键模块的取舍思路、踩过的坑以及任务失败后的告警、重试这类容易被忽略的工程细节。目标是给正在做类似 AI 代码工具的人一份可以直接参考的实战笔记也适合想理解 Agent 类应用背后设计逻辑的读者。1. 先想清楚问答型助手与执行型 Agent 之间的那道坎1.1 为什么“只能答不能做”才是真实瓶颈早期版本的 XiheAgent 本质就是一个套了模型接口的代码问答框。用户粘贴代码片段、提出问题我返回解释或者修复建议。听起来没问题但真实使用一段时间后会发现一个尴尬现象用户依旧需要自己定位文件、打开编辑器、逐行对照建议去修改很多被反复提到的“低级错误”依然在下一次提交中出现。为什么因为模型给出的建议没有被执行、没有被验证甚至没有被真正读进仓库的上下文里。我在最初设计时犯过一个错误把模型当作“全知全能的代码专家”认为只要给它足够的上下文它就能输出正确答案。但实际使用中答案的可靠性很大程度依赖于检索是否准确、上下文是否完整、执行结果是否回传。纯问答模式等于把最后的判断和执行责任全部甩回给用户省下的只是“搜索一下”的时间而不是“动手改”的时间。这也是 XiheAgent 转向“任务执行”的核心动因——不是为了炫技而是因为开发者真正需要的是结果不是建议。从产品角度看问答是需求收集和方案试探执行才是价值交付。能把“把parse_config函数里的错误处理补全”这句话变成一次真实代码编辑并跑通测试价值远高于十轮对话里的十个正确建议。1.2 从“给建议”到“动手改”职责边界重新定义当你决定让 Agent 动手执行任务第一件要面对的事就是职责边界。问答模式下说错了代价很低——用户瞟一眼不采用就行。执行模式下模型要写文件、跑命令、改配置说错了可能直接把一个模块改崩、把环境变量覆盖掉。所以设计上必须把“生成”和“执行”拆开每个动作都要有确认、有回滚、有验证。我在 XiheAgent 里做了一个很关键的划分问答模式ask与执行模式act并行。ask 模式走的是检索生成链路输出答案并给出引用来源act 模式走的是任务解析操作序列执行验证链路所有修改都会生成 diff 并留下快照。这两个模式可以在会话中自由切换但切换条件必须明确——是“给我解释”还是“帮我改”意图不清楚时宁可停下来问用户也不要擅自行动。这个边界看起来简单却解决了我遇到的最大一类事故模型误判用户想改代码把只读问题当成了修改任务结果改了不该改的文件。后来我加入了一条规则凡是包含“为什么”“解释”“区别”等关键词的问题默认走 ask 模式凡是包含“修改”“新增”“删除”“重构”等动词的问题默认走 act 模式。虽然听着基础但实测下来误判率大幅下降。2. 架构设计与核心机制2.1 主流程设计意图识别—任务解析—执行验证—反馈闭环XiheAgent 的完整链路看起来只有四步但每一步都大有文章。先说整体一条用户消息进来之后先经过意图分类器判断这是问答请求还是执行请求。执行请求会被送入任务解析器模型基于当前仓库状态生成一份结构化的任务计划JSON计划里包含要改哪个文件、用什么操作、期望的结果。接下来执行引擎按照计划逐一操作每个操作执行前记快照、执行后采集状态最后统一汇总结果供用户确认。我特意没有把任务解析和执行耦合在一起。让模型一次性“想到哪改到哪”的结果往往是灾难性的因为模型对文件路径的记忆会漂移对改完之后的连锁影响缺乏判断。拆成独立步骤后每个阶段都能引入人工确认点用户可以在执行前审阅计划、在执行中看进度、在执行后看 diff。实际体验下来用户对“可插手的执行过程”的信任度远高于“黑盒式一键完成”。反馈闭环是容易被忽略的一环。任务执行完不等于任务成功模型需要看到执行结果才能判断“是不是还要做下一步”。举个我实际遇到的例子模型执行pip install xxx后退出码为 0但没有检查包是否真的被安装。后来我在执行引擎里加了“结果回读”机制要求每个动作都必须附加一个验证表达式或验证脚本比如运行python -c import xxx来确认安装效果。反馈闭环让 Agent 不再只是“发布了命令”而是“确认了命令产生了预期效果”。2.2 工具注册表与执行沙箱的设计让 Agent 能干活的本质是给它注册一系列可调用的工具。我在 XiheAgent 里维护了一张“工具注册表”每个工具包含名称、描述、参数 schema、执行函数、权限级别、超时策略。模型本身并不直接执行代码它只负责“选择工具和填参数”真正落地动作的是注册表背后的执行函数。这个设计的好处有两个一是权限可控模型永远不能跳出已注册函数的能力边界二是易于扩展加一个工具只需要补齐一个函数不需要修改链路。工具表里常用的有这么几类文件读取、文件写入、目录遍历、命令执行、Git 操作、检索查询。每个工具都声明了自己的“危险性”比如命令执行就是高风险工具文件读取属于低风险工具。低风险工具调用不需要用户确认高风险工具必须等待用户授权。为了进一步降低风险命令执行工具默认跑在一个受限的沙箱容器里没有写宿主机的权限网络能力也被裁剪。沙箱不是论文里才有的概念动手做过的人都知道它是“允许模型做实验”和“不让模型搞坏环境”之间的保险丝。我最初的版本没有沙箱模型执行rm -rf build这类命令时如果工作目录认错了就可能误删整个项目。加了沙箱之后命令执行先落到容器内只有确认无误的产物才被拷贝回宿主目录这从根本上杜绝了破坏性操作。当然沙箱不是万能的还需要配合后面的黑名单和确认机制。2.3 上下文管理让模型记住项目而不是记住会话One of the biggest pain points in building a coding agent is context management. 普通聊天工具的上下文是一段对话历史而编码助手的上下文必须包含项目结构、文件内容、版本状态、用户偏好等多维信息。我在设计时把上下文拆成了三层项目级记忆、会话级记忆、操作级记忆。项目级记忆保存的是这个仓库的基础信息文件树、依赖清单、构建命令、代码风格约定。会话级记忆保存的是本次交互中用户提到的需求和方案倾向。操作级记忆保存的是最近几步的工具调用结果与文件变更。三层记忆各自独立存储在每次请求时按需拼接再送给模型。这里有个取舍要注意上下文不是越多越好。把整个仓库塞给模型大概率超出令牌窗口而且噪声会掩盖关键信息。我的做法是对项目级记忆做分层——只有当前任务涉及到的模块文件会被完整展开其余文件只保留头部注释或索引摘要。这样既保住了全局视野又不会让模型“淹没”在无关代码里。3. 代码问答模块的落地细节3.1 仓库检索向量相似度之外还要有结构信号问答模块是 XiheAgent 的起点但真正让它“答得像样”的不是模型参数而是检索质量。最开始我直接用朴素的向量相似度把问题和代码片段做匹配结果经常答非所问。后来我发现问题出在代码检索跟文档检索不一样——代码里有很多语义是藏在结构里的比如函数之间的调用关系、类的继承层次光靠语义向量很难有效表达。后来我在检索层加入了 AST 索引用 Tree-sitter 对仓库里的代码做解析提取出函数名、类名、导入关系、调用链把这些结构信息作为元数据跟向量索引做混合检索。用户问“这个项目的入口文件在哪”时结构化信息会优先命中入口相关节点用户问“这段代码为什么慢”时语义向量会优先定位到热点函数。两者加权合并之后检索质量明显上升。另外容易忽略的是代码块的粒度。整文件切片经常导致一个函数被切在多个 chunk 里检索结果出现“答非所问”按函数和类粒度切片才是可靠的方案。我用 AST 先把文件拆成函数级代码块再把过大的函数按注释或空行二次切分每个 chunk 控制在几十行以内。这个切片对后续的上下文拼接帮助也很大。3.2 答案生成与引用溯源让模型“说人话”还能“给依据”问答模块生成答案时有一件事必须做到——引用溯源。模型如果只给一个泛泛的结论用户很难判断要不要信。所以在 XiheAgent 里每个答案都会附带相关的文件路径和行号范围用户鼠标悬停就能跳转查看原始代码。这背后的实现其实不复杂检索阶段记录命中 chunk 的来源生成阶段要求模型在答案里显式标记引用标记。我为此在提示词里加了一段固定说明“你是一个代码阅读助手回答中必须标注与你结论直接相关的文件路径和行号。”然后配合关键词约束让模型学会输出类似“参考src/utils/parser.py:42-57”这样的引用格式。实测下来带有引用的答案采纳率会高出不少——因为用户能自己验证而这种验证本身也在建立信任感。还有一个小技巧问答模式下尽量让模型“边读边想”而不是一次性给结论。我会在答案结构上要求三段式先一句话概括结论再展开解释背后的原因和代码路径最后附上修改建议或后续可读文件列表。这种结构用户读起来负担小也更容易定位自己真正关心的问题。3.3 问答与执行的边界控制刚才说过 ask/act 分开但实际使用中用户的表达往往比较模糊。比如“这个函数的错误处理有问题”这种话既可以理解为“解释一下哪里有问题”也可以理解为“帮我修好它”。XiheAgent 的做法是低风险情况下倾向于走 ask 模式高风险操作必须显式询问用户的意图。我在交互层做了一个意图确认弹窗当分类置信度低于阈值时展示“你是想让我解释还是直接修改”的两个选项。这个设计一开始显得有点啰嗦但实际用下来大部分用户会觉得这个确认很有必要——他们自己很多时候都没想清楚到哪一步为止。而且这一步确认也为后续任务执行铺路确认了“修改”之后用户天然就会更关注 diff 内容参与感更强。边界控制里还有一个容易被忽略的细节已修改文件在新会话中的状态同步。我踩过一个坑用户在 ask 模式下问了一个文件的问题然后切到 act 模式去改另一个文件改完回来再问同一个文件模型给出的答案却是基于修改前的旧内容。后来我让上下文管理器统一从 Git HEAD 和当前工作区读取实时状态任何问答都会先检查文件是否被改动过改动过的文件自动触发重新建立索引避免上下文陈旧。4. 任务执行引擎的实现重点4.1 从自然语言到结构化任务计划解析与确认任务执行的第一环节是把用户的自然语言请求转换成一份可以被机器执行的任务计划。我在实现里走的不是让模型直接“调函数”而是先让模型输出一份 JSON 计划计划里包含操作清单、涉及文件、预期结果和风险提示。比如用户说“给auth_service.py增加登录次数的限制”模型会生成类似这样的计划{ goal: 给 auth_service.py 增加登录次数限制, operations: [ { action: read_file, target: src/services/auth_service.py, comment: 读取当前登录流程代码 }, { action: edit_file, target: src/services/auth_service.py, strategy: 在 login 函数入口增加失败计数检查, expected: 连续失败5次后锁定10分钟 } ], verification: python -m pytest tests/test_login_limit.py }这份计划生成之后不会立刻执行而是先渲染给用户看。我在界面上用结构化的卡片展示每个操作用户可以直接删除不想要的步骤也可以调整顺序。这个“计划确认”步骤是跟沙箱同样重要的安全网——它把模型犯错的成本从“执行完了才发现错了”提前到了“动手之前就发现方向错了”。实际使用中计划确认还承担了“拆解复杂任务”的功能。用户经常提的是非常粗颗粒的需求比如“帮我优化整个模块的性能”模型必须自己把它拆成若干子任务。拆得好不好直接决定了后续执行质量。我见过不少同类工具在这里翻车模型把“优化性能”拆成了“重写所有函数”结果改动量巨大很难审查。后来我在提示词里强调“最小改动原则”——优先做局部优化尽量避免大面积重写并把这一点写进了任务规划约束。4.2 文件编辑、diff 展示与回滚机制任务执行的核心操作集中在文件编辑上。但在动手写编辑器之前我认真算过一笔账一旦模型开始批量改文件回不去就是最恶劣的体验。用户宁愿模型什么都没做也不愿意看到自己辛辛苦苦写的代码被改得一塌糊涂还不能还原。所以在所有文件修改动作的前置阶段XiheAgent 会自动创建一份“操作前快照”记录文件的原始内容。编辑完成后引擎会自动生成一份 diff展示在界面上用户一眼就能看到改了什么。用户确认无误后快照会保留一段时间如果后续任务失败或用户不满意可以随时通过“回滚到快照”一键还原。回滚机制听起来朴素但细节很重要。我早期做过一个粗放的版本只在每个任务开始前存一次快照结果一旦任务分多步执行中途某一步失败回滚到原点会把所有成功的步骤也丢掉。后来改成了“操作级快照”——每个文件改前都存一份每次编辑都对应一组变更记录回滚时可以精确到某一次操作而不是整个任务。这个粒度差异在实际使用中就是“少加班半天”和“白干一天”的区别。还有一点diff 展示不能只给模型看更得给用户看。我参考了主流代码评审工具的做法把 diff 按文件分组用户可以直接在改动块上点赞、评论或撤销。很多用户第一次看到这个界面时很惊讶说“感觉真的在跟一个懂代码的同事协作”这也说明执行型工具的信任建立靠的就是每一个动作的透明可见。4.3 命令执行超时、隔离与输出解析除了改文件很多任务天然需要执行命令跑测试、跑构建、安装依赖、启动服务。命令执行是整个任务执行模块里风险最高的一部分我给它做了三层防御超时控制、目录隔离、输出约束。超时控制很好理解任何一个命令在后台跑太久都会被判定为异常。我按命令类型分别设了默认超时常规命令 60 秒测试命令 180 秒构建命令 300 秒。超时之后进程会被强制终止并给模型回传一个“超时中断”的失败信号让它知道不能再等下去需要换方案。输出解析是最容易低估的环节。模型需要通过命令输出来判断下一步行动但命令输出经常非常嘈杂几百行编译日志、一堆无关的警告、甚至墙上输出。我写了一个输出整理器会提取退出码、错误关键行、测试统计结果压缩成不超过几百字符的摘要再交给模型。这样模型不会被长输出淹没也能更聚焦地判断“现在该做什么”。顺便说一句grep -E FAILED|ERROR这类过滤命令在输出整理器里只是起点真正的关键是保留上下文相关性。至于目录隔离前面提到的沙箱就是核心载体。命令默认在工作区子目录或者容器内执行执行脚本和临时文件都不会污染宿主机环境。这层隔离让模型可以放心大胆做实验用户也不需要整天提心吊胆。4.4 失败重试与企微告警把任务执行变成可观测的流程任务执行不可能每次一帆风顺所以 XiheAgent 里内置了一套失败重试和告警机制。这部分的灵感确实来源于任务调度框架比如 DolphinScheduler 这类系统的设计但落到编码助手场景时需要重新取舍。先说重试策略。模型在任务执行过程中如果遇到失败不会立刻放弃而是会尝试一次“诊断—调整—再执行”的循环。比如测试跑了 20 秒失败了引擎会把失败日志交给模型让它判断是代码 bug 还是测试环境问题然后调整方案再执行。我设置最大重试次数为 3 次每次重试会保留上一次失败原因作为上下文输入避免模型重复踩同一个坑。告警机制则是为了处理“长时间任务”和“异步场景”。当任务执行时间超过预计值或者某个关键步骤失败了XiheAgent 会通过企业微信机器人推送一条结构化告警到指定群组。告警内容包括任务 ID、失败步骤、失败原因、时间戳、日志摘要。这样开发者不需要一直盯着屏幕等待任务跑完可以去做别的事情任务失败时第一时间收到通知。我实现企微告警的方式其实非常简单就是调用机器人 Webhook拼一个 Markdown 消息体再把关键信息填进去。唯一的心得是告警模板一定要包含“可操作信息”——比如文件路径、执行命令、日志位置光说“任务失败”没有用要让收到告警的人能直接开始排查。这也是我实测下来大家反馈最好的一点。4.5 安全与权限白名单、黑名单、人工确认权限管理是执行型 Agent 里我必须花最多篇幅讲的内容。很多同类工具不敢放开执行能力就是怕模型乱来。我的结论是与其因噎废食关掉执行功能不如把权限切成细粒度让模型有边界地放开手脚。我在 XiheAgent 里实现了三级权限白名单目录、黑名单命令、人工确认点。白名单目录定义了模型可以读写的范围通常就是当前项目根目录和临时目录其他路径一律不可访问。黑名单命令包括了rm -rf /、mkfs、dd这类破坏性操作只要工具表里出现这些命令直接拦截并报错。人工确认点则覆盖了高风险动作比如删除文件、修改全局配置、安装系统级依赖。安全设计里还有一条隐藏逻辑任何“文件删除”都必须走两步确认——第一步是确认计划里的删除意图第二步是执行前再次弹窗确认。我这么做是因为见过太多模型以为删了某个文件没问题结果第二天用户发现整个项目构建失败。用户可以嫌烦但不能没有这个确认安全永远优先于体验。5. 踩坑记录与排查技巧实录5.1 “假成功”退出码为 0 但任务没生效任务执行最初“翻车”最多的问题是命令返回了成功但实际行为完全不对。典型的案例是模型执行npm install后以为依赖装好了结果装的版本不对或者安装在错误目录下。退出码为 0模型就默认任务成功但用户的代码里 import 依然报错。排查思路很简单不要只看退出码要额外加“验证步骤”。我在任务计划里强制要求每个操作都附带验证条件比如“安装完成后运行node -e \require(xxx)\”来确认包可用。如果验证失败即使退出码是 0整个操作也会被标红成失败模型就会进入重试诊断流程。这类问题在设计上很难用“更聪明的模型”解决因为模型无法感知它没被安排验证的执行结果。但把“验证步骤”变成任务计划的一部分后假成功比例下降了很多。如果你也在做类似 Agent强烈建议从第一天就把验证逻辑写进任务计划模板而不是事后补救。5.2 上下文溢出与“越聊越笨”长会话里上下文窗口逐渐被陈旧内容占满模型越来越笨。这个现象我形容为“越聊越笨”——前期问题回答得挺准聊到后面连最基础的路径都记不住甚至会把前面自己修改过的代码忘掉。排查发现根因是上下文管理策略太粗糙。我之前是把对话历史原封不动往模型输入里加越加越长直到把有效信息挤出去。后来我对历史记录做了摘要压缩——把早期对话用模型提炼成一条摘要缓存只在需要时把摘要重新展开同时在任务执行阶段把“操作步骤”和“操作结果”分开存储不把长篇日志塞进模型上下文。如果因为篇幅问题不能做完整摘要系统还有一个实用的省流技巧每次模型调用前把“当前项目的 git diff 摘要”加进上下文比让模型逐行读历史更管用。项目状态比对话历史更能反映“现在到底改了什么”这个替换逻辑解决了我遇到的大量“上下文漂移”问题。5.3 并发冲突协同编辑时的“两个人互相覆盖”当 Agent 开始改文件时如果用户同时在编辑器里手动改同一个文件很容易出现互相覆盖。这个问题不是 Agent 独有的但 Agent 的自动修改更容易触发冲突。我第一次遇到时用户先手动改了utils/date.py然后 Agent 基于旧内容又改了一遍把用户的新改动覆盖了。处理方案比较直接所有文件修改前先检查文件哈希如果比 Agent 读取时的哈希新就停止写入并提示“文件已被外部修改是否重新基于最新内容执行”。这个检查成本很低但能避免大部分冲突。更进一步我还给 Agent 的文件操作加了一个“锁文件”机制——修改某文件前先创建.xihe-lock/file.lock改完移除。虽然这不能阻止用户不受控制地编辑但至少能在 Agent 内部避免多个任务并发修改同一文件。最省心的经验其实是引导用户Agent 改文件时建议用户先把本地改动提交或暂存。如果用户不愿意那至少你要给 Agent 装上哈希检查这层保险。5.4 危险操作拦截过度与授权疲劳安全机制做多了也会产生新问题——用户被各种确认弹窗烦到不行最后直接忽略授权反而让安全机制失效。我在测试阶段就遇到过测试者为了省时间一键全选所有确认框结果模型执行了一个改动很大的操作也没被发现。后来我调整了策略按“风险等级”动态决定确认点。低风险操作自动执行不再弹窗中风险操作只确认一次确认后本次会话内同类操作默认放行高风险操作继续保持每次确认。同时给用户提供“本次会话记住我的选择”的选项但只针对中风险操作生效高风险操作永远不记住。这个折中方案既保住了安全底线也免去了大量无关打扰。还有一个细节确认弹窗里的信息密度要控制。早期我的弹窗里塞满了命令参数、文件路径、环境变量用户根本看不完也看不懂。后来我把弹窗精简成三行操作类型、目标文件、风险说明。用户更容易理解也更容易做出正确判断。安全机制是为用户服务的而不是给用户添堵的。6. 扩展思路从单 Agent 到多 Agent 协作6.1 分工代码阅读、代码生成、测试验证各司其职做到后期我发现单 Agent 同时承担全部任务解析、代码生成、测试验证压力很大尤其是模型上下文和错误率会随着任务复杂度上升。所以我开始尝试多 Agent 协作架构——把不同能力拆给不同角色让它们像一个小团队一样配合。我目前的设计是三个角色Reader Agent 负责检索代码库、理解既有逻辑、输出结构化总结Writer Agent 负责基于任务计划和总结生成代码修改方案Tester Agent 负责写测试用例、执行测试、分析失败原因。三者之间通过一个简单的任务消息总线通信每个角色只处理自己职责范围内的事情。这样拆分的好处很直接每个 Agent 的提示词和目标都更聚焦模型不需要在“理解需求”“查代码”“写代码”“调试测试”之间来回切换输出质量更好出错时也能更精确定位到哪个环节出了问题——是 Reader 读漏了上下文还是 Writer 改坏了逻辑还是 Tester 的用例写错了。排查成本比单 Agent 低很多。6.2 与 CI/CD 链路融合把 Agent 变成“提交前的最后一公里”另一个值得尝试的扩展方向是把 XiheAgent 接到 CI/CD 流程里做成“提交前的最后一公里”。用户在本地跑完修改后Agent 可以自动执行 lint、单元测试、构建验证发现问题就地修复再验证直到全部通过。这个流程和人工反复提交 CI、等结果、回来改 bug 相比节省的时间非常可观。我在实验版本里用 GitHub Actions 和 Jenkins 做过两个方向的集成一个是 Agent 主动触发 CI 流水线读取构建结果决定下一步修改另一个是 CI 失败时自动生成一个“失败分析 修复建议”任务发给 Agent。这两个场景做起来都不复杂核心是把“Agent 能调用的工具表”扩展到 CI 平台的 API 接口上但收益立竿见影。6.3 一个我真实体会到的关键点别让 Agent 脱离人的判断力最后分享一个反复出现的心得无论多智能的 Agent都不能替代人的判断力。任务执行能力越强越要设计好“人在回路”的位置——计划确认不是形式而是让用户了解 Agent 要做什么给用户纠偏的机会diff 展示不是摆设而是让用户真正看懂改动、建立心理预期。我见过太多同类产品强调“全自动”把用户晾在一边等结果一旦出了错用户连问题出在哪都不知道。XiheAgent 的设计理念恰恰相反所有关键决策点都保留人工介入入口Agent 是干活的手用户才是把握方向的脑子。这种协作关系比单纯追求“AI 一键搞定”要走得远得多。如果你正在设计自己的 AI 编码助手建议从第一版就把“可确认、可回滚、可观察”写进架构里别等规模大了再加。执行型工具的信任一旦崩塌再想重建就难了。
返回列表