AI编程助手进阶:从代码问答到自动化任务执行的XiheAgent实战
发布时间:2026/9/29 23:23:18 作者:尧图编辑部 阅读量:1,286

很多 AI 编程助手现在都卡在同一个坎上能聊天但干活只能靠复制粘贴。用户问完代码解释、让 AI 补全一段函数然后把代码手动粘回编辑器再手动跑测试再手动处理报错。这不叫助手这顶多叫高级搜索框。我在设计羲和XiheAgent的时候目标很明确让 AI 不仅能回答这个代码在做什么还能直接去改代码、跑测试、查日志、修问题形成一个从自然语言到实际变更的闭环。这听起来像是把 Copilot 和 AutoGPT 揉在一起但真做起来会发现难点根本不在模型有多聪明而在工程链路怎么串。我从会话层、规划层、执行层分别拆解加入任务调度和告警机制让它适配真实的开发环境。这篇文章把我从零搭建 XiheAgent 的完整思路、踩坑经历和迭代建议写成一份实操复盘给也想做同类 AI 编码助手的朋友一个可参考的路线图。1. 从能聊代码到能干活我为什么做 XiheAgent1.1 现有 AI 编程助手的通病市面上的 AI 编码助手80% 的交互形态都是一个对话框加一串流式回复。用户问这段查询为什么会慢它能给你讲出一堆索引、执行计划、连接池的潜在问题——然后呢然后用户得自己打开编辑器找到那个慢查询的代码自己判断哪里需要改。如果项目里有五处类似的查询AI 压根不知道你说的是哪一处。我测试过一些主流工具后发现几个共性问题上下文依赖聊天窗口的单次对话换一个会话它就失忆了跨文件的项目级问题经常答非所问。代码修改建议是纯文本不会自动生成 diff更不会帮你执行、验证。任务型请求帮我给所有接口加上超时重试没有拆解能力它只会把实现细节一次性吐出来剩下全交给人类。没有日志、告警、任务状态这类事后反馈出了问题用户还得自己去终端翻。这些问题的本质是当前的 AI 编程助手停留在知识问答层面而开发者真正需要的是任务执行能力。问答只需要理解与生成执行必须感知环境、操作环境、验证结果。XiheAgent 做的事情就是补上后面三块。1.2 目标拆解把一句话需求变成已完成的修改我给 XiheAgent 定的核心需求很简单用户说一句给登录接口加上失败重试三次后就锁账号它应该能完成以下操作在代码仓库里检索登录接口的实现位置找到相关文件。理解现有代码的结构确定重试逻辑应该插在哪里、锁账号的状态应该存哪里。生成代码修改建议并以 diff 形式展示给用户确认。用户确认后自动写入文件、运行相关测试、检查是否报错。如果测试失败拉取日志定位失败原因尝试自修复最多重试 N 次。所有操作留痕生成一份执行报告必要时推送到企微告警。这个目标拆解直接决定了系统架构。它需要四个核心模块代码语义理解与会话管理回答准不准、任务规划怎么把需求拆成步骤、工具执行真正操作文件和环境、异常处理与告警出错了怎么办。后面每一章我都按这四个模块展开讲。2. 会话层不再是聊天框代码问答背后的上下文工程2.1 代码索引与检索让模型先找到对的文件代码问答的第一个难题不是模型不会答而是模型不知道该看哪个文件。一个大型项目的仓库动辄几千个文件把全部代码塞进提示词既不现实也没必要。XiheAgent 的做法是建一套轻量代码索引采用检索 排序的机制用树形结构维护项目目录记录每个文件的路径、语言类型、函数列表、类名、关键变量的出现位置。对函数名、类名、注释做向量化嵌入建立向量索引支持按语义相似度召回。在提问预处理阶段先从 Query 里抽取候选实体词接口名、文件名、变量名、异常类型等用这些词做一次精确匹配同时把整个 Query 丢进向量检索做一次语义召回最后合并两个结果集按相关度排序。实际工程里我一开始只做了向量召回效果非常差。原因是开发者提问经常带缩写或内部黑话比如把 dmp 的 ctl 改一下向量检索压根不知道 dmp 是 data_management_platform 的缩写。后来我在索引层加了同义词表和项目别名表由用户或团队维护效果才有明显提升。提示代码检索的召回质量直接决定后续所有步骤的准确性。我建议优先把实体精确匹配放在语义召回之前因为代码问题往往强依赖准确的文件路径或函数名模糊匹配容易带回一堆无关代码。2.2 会话状态管理跨轮次保持任务的短期记忆问答助手最常见的尴尬是你刚让它找到某个文件下一句追问那这个函数里第二个参数呢它已经不知道这个函数指的是什么。XiheAgent 在会话层维护了一个状态对象核心字段长这样dataclass class SessionState: session_id: str current_files: list[str] current_symbols: list[str] pending_diff: str | None task_plan: list[TaskStep] | None tool_history: list[ToolInvocation] last_user_query: str每一轮用户提问都会先走一遍上下文刷新逻辑从 SessionState 里取出 current_files把它们的内容摘要拼进系统提示词如果用户提到了这个那个刚才的则优先在 current_files 和 current_symbols 里做指代消解。这个设计让 XiheAgent 连续对话能力上升了一个档次——它不再是每次都从零理解问题。这里有个容易忽略的细节文件内容摘要不能直接截断也不能全量塞进提示词。我的做法是按需分块加载。比如某个文件有 2000 行我只把当前符号定义附近的前后 80 行以及整个文件的签名列表加载进来。这样既节省 token又降低模型被无关代码干扰的概率。2.3 我踩过的上下文坑token 预算分配刚开始做的时候我给提示词塞了太多东西项目完整架构说明、代码风格指南、全部检索结果、历史对话……结果模型回答越来越离谱经常把旧对话里的错误假设当成事实还出现幻觉引用不存在的类名。后来我做了 token 预算分配经验值得分享内容项预算占比说明系统角色与全局指令5%只放最基本的行为约束不塞项目细节会话最新目标10%当前任务的一句话描述当前文件/关键符号片段45%只放与本次操作强相关的代码片段检索结果与上下文依据25%从索引召回的相关文件摘要和相似代码历史对话摘要15%用模型对旧对话生成压缩摘要不直接拼原文按照这个比例我用 8K 到 16K 的上下文窗口就能跑通大多数场景。如果评审意见里要求参考某个文件我再单独定位加载那个文件而不是扩大默认上下文。3. 从问题到步骤任务规划层的拆解逻辑3.1 意图识别区分告诉我和帮我改会话问题只能用于问答层任务执行靠的是规划层。设计的第一个关键动作就是意图分类。XiheAgent 把用户输入划分为三类咨询意图用户只想了解代码逻辑、排查思路不需要修改文件。此时走问答链路返回解释和建议不产生任何代码变更。修改意图用户明确要求修改代码、新增功能、修复问题。此时走任务规划链路。混合意图用户既想知道为什么又希望修复。比如为什么会报空指针帮我修下。此时先给出简要原因分析再进入执行链路。分类我用的是一个轻量分类模型加规则兜底。规则部分包括关键词触发比如出现帮我改修复添加重构等词直接判定为修改意图模型部分则对语义更复杂的请求做判断。这个分类做的不够精准会出大乱子——用户就问了句这个 bug 是怎么产生的如果 Agent 认为是修改意图直接开始改代码那产品就没法用了。3.2 任务分解与依赖编排一旦判定为修改意图规划器就要把自然语言变成可执行的任务链。拿给登录接口加失败重试来说规划器输出的计划大概是这样{ goal: 为登录接口增加失败重试与账号锁定, steps: [ {id: 1, action: locate, target: login endpoint, expect: 找到实现文件与函数}, {id: 2, action: read, target: 相关函数实现, expect: 了解现状与依赖}, {id: 3, action: modify, target: 增加重试逻辑, expect: 生成代码修改diff}, {id: 4, action: run, target: 登录相关单元测试, expect: 测试通过}, {id: 5, action: report, target: 生成执行摘要, expect: 输出变更说明} ], dependencies: [[1, 2], [2, 3], [3, 4]] }任务依赖编排是个容易想简单但必须精细的地方。步骤之间不是简单的顺序执行比如跑测试和检查代码风格是可以并行的修改 A 文件和修改 B 文件可能需要归并成一次 diff 提交。我参考了数据流编程里 DAG 的思路把每个步骤的输入输出声明好由执行引擎来判断哪些步骤可以并发、哪些必须阻塞。3.3 遇到模糊需求怎么做追问澄清拆解过程中规划器经常遇到信息缺口。比如用户说给所有接口加上限流这个描述有两个关键缺口一是什么是所有接口二是限流策略参数。早期版本我会让模型自己猜结果它给 Web 层、RPC 层、定时任务全都加了一遍还默认了一个非常激进限流阈值差点把生产环境搞挂。后来我固定了一套澄清策略任何关键参数缺失必须走澄清分支不允许猜测。实现方式是规划器在输出计划前先检查必填项清单发现缺项就生成追问问题列表等用户答复后再补全计划。比如上面那个需求Agent 会问限流范围只限对外 HTTP 接口还是也包括内部调用限流维度按 IP、按用户还是按接口高峰期阈值和降级策略是什么这样做确实会多一轮交互但总好过执行到一半才发现理解错了。我的原则是宁可多问一句不能瞎猜一次。这跟人一样需求都没对齐代码写得再快也是白写。4. 工具调用层让 Agent 真的动起手来4.1 工具注册表与权限控制任务执行层是 XiheAgent 区别于普通 AI 助手的关键。它不是一个模型而是一组工具的调度中枢。我设计了一个工具注册表每个工具实现统一的接口class BaseTool: name: str description: str parameters: list[ToolParameter] permission_level: str # read_only / local_write / remote_write / destructive def validate_args(self, args: dict) - bool: ... async def execute(self, args: dict) - ToolResult: ...工具列表初期包括读取文件、写入文件、执行 Shell 命令、运行测试、搜索代码、查询日志、git 操作、调用项目内脚本。每个工具都声明了权限级别比如读取文件是 read_only执行 Shell 命令是 local_writegit reset --hard这类危险操作直接标记为 destructive默认不允许执行。权限校验发生在两个地方一是任务规划阶段检查整个计划是否包含超权限工具二是每个工具执行前再次校验当前用户会话的操作权限。我还在注册表里维护了可执行白名单像rm、mv、git push这类命令默认不在白名单内需要用户在设置里显式开启。4.2 命令执行与沙箱隔离工具层最怕的是 Agent 乱执行命令。如果运行环境是本地开发机一条rm -rf可能让人几个月的心血白费。我给 XiheAgent 准备了两种运行模式本地受控模式所有命令通过一个受控 Shell 包装器执行包装器会检查命令是否命中黑名单、是否超出工作目录范围输出截断到最近 N 行如果命令超过预设超时时间默认 30 秒会被强制终止。容器隔离模式把 Agent 的写操作和命令执行放到 Docker 容器里代码仓库以只读卷挂载进去Agent 修改文件产生的是 diff 文件宿主机器只保存 diff 和应用后的副本由用户确认后真正落盘。容器隔离模式的改动成本比较大但对高风险操作是必要的。我现在是把两种模式并存常规的代码修改走本地受控模式追求效率重命名、批量替换、回滚、清理类任务走容器隔离模式避免不可控的破坏。沙箱隔离有一个需要注意的细节即使命令跑在容器里文件系统也可能挂载了宿主机的目录。我在容器启动时严格限制挂载点只把允许操作的项目目录挂进去其他目录一律隔离。这个检查不能省因为 Docker 配置一旦疏忽隔离就跟没有一样。4.3 结果回传与自我修正工具执行完不是终点结果要回传给规划器做校验。每次工具执行返回一个结构化结果包含退出码、标准输出、标准错误、执行时长、变更文件列表。规划器拿到结果后会先判断是否满足步骤里声明的 expect验收条件。以修改登录接口重试逻辑为例写文件的工具返回成功但规划器期望通过登录模块的测试于是把测试工具压进执行队列。如果测试失败规划器不会简单重试上一次操作而是先拉取测试输出和日志把失败信息附加到当前上下文再重新规划修复步骤。这个过程我称之为自主修复循环执行步骤失败 → 捕获错误上下文。根据错误类型判断是否可自动修复编译错误、断言失败、超时通常可修复权限问题、依赖缺失需要人工介入。可修复则修订执行计划回到失败步骤的前置依赖重新走。达到最大重试次数我设的是 3 次仍未解决时停止操作把完整日志交给用户。这个循环看着简单但做不好的原因往往是错误上下文太窄。早期我只把 stderr 的最后几行交给模型它经常误判原因。后来我把失败时的相关文件版本、最近一次 diff、测试用例名称都打包进去修复准确率才明显提升。5. 任务执行中的异常处理失败重试、超时与告警联动5.1 失败分类临时失败与永久失败任务执行必然遇到失败但失败跟失败不一样。我把执行异常分为两类临时失败网络抖动、服务暂时不可用、并发锁冲突、测试环境资源不足。这类失败通常带瞬时特征重试能解决。永久失败代码语法错误、接口参数不匹配、权限被拒、需求本身有问题。这类失败重试一万次都是白搭必须停下等待人类介入。分类依据不靠猜我维护了一份异常特征库。比如退出码 137被 kill、超时、连接重置归为临时失败退出码 126/127命令不存在、编译错误、git 冲突归为永久失败。如果模型判断不了就让系统默认按永久失败处理——宁可多问人也不能让 Agent 陷入无限重试死循环。5.2 重试策略与退避算法对临时失败我用了指数退避加重试上限的策略。基础延迟 1 秒每次翻倍最多重试 3 次。但如果简单套这个公式一个耗时很久的编译任务失败后重试会叠加系统负载所以我加了重试代价评估预计耗时低于 30 秒的任务直接按退避重试高于 30 秒的任务必须先输出警告由用户决定是否重跑。还有一个非常容易踩的坑重试幂等性。写文件、发消息这类工具如果上次执行其实已经成功了但因为网络问题没把结果回传重新执行就会造成污染。我的做法是给关键命令加执行 ID参数命令执行前先检查这个 ID 是否已经产生过结果有则直接返回旧结果不重复执行。5.3 与任务调度系统的配合dolphinscheduler 接入XiheAgent 单独使用只能处理前台交互式任务比如用户坐在电脑前让它改代码。但真实开发环境里很多任务是不需要人盯着的比如跑全量回归测试、定时刷新测试环境、批量数据修复脚本。对这类任务我把 XiheAgent 接入了任务调度平台用 Apaches DolphinScheduler 来编排定时触发和依赖执行。接入方式不复杂我给 Agent 暴露了一个异步任务接口把用户的任务计划打包成一个调度任务提交给 DolphinSchedulerDolphinScheduler 负责按时触发、管理重跑、记录日志。执行完成后Agent 再把结果回写到调度实例的日志里。这样做的好处是XiheAgent 负责做什么和怎么做DolphinScheduler 负责什么时候做和怎么等到做完。我选 DolphinScheduler 而不是自己写 Cron是因为任务编排的依赖关系、失败重跑、补数策略这些功能现成的比自研可靠没必要重复造轮子。实际接入时踩了一个坑DolphinScheduler 的 shell 任务默认环境变量和 Agent 运行时不一致导致 Agent 进程启动后找不到依赖。解决方法是把 Agent 的启动参数和环境变量写进调度任务的配置里而不是依赖 shell 的默认环境。5.4 企业微信告警人机协同闭环任务执行到这里还差最后一块拼图人类何时介入。尤其是失败重试也解决不了的任务必须第一时间通知到人。我做了企业微信群机器人告警触发条件有三个永久失败发生。自动修复重试达到上限。任务执行结果与预期偏差过大例如测试通过率低于阈值。告警消息带的内容不是一句任务失败就完了而是包含失败任务名、失败类型、重试次数、相关日志片段、触发失败的工具调用链、可能的处理建议。这样接到告警的同事可以不用重新看一遍完整日志就能判断问题是什么、要不要介入。我自己实际使用下来的感受是告警文案写得越细人机协同的效率越高。如果告警里只写着执行失败收到消息的人还得自己打开系统查日志协同效率等于零。6. 实测复盘三类翻车现场与我的修复方案系统跑通之后我在自己的项目上连续实测了两周专门记录翻车场景。这里挑三个最有价值的分享给大家。6.1 改错文件检索召回与上下文窗口的失衡第一次实测我给 Agent 发指令把用户列表接口改成用 Redis 缓存。结果它定位到了三个相似度都很高的文件最后修改了其中一个最像但实际不对的实现——它把管理后台的用户列表改了而不是开放 API 的用户列表。两个函数签名完全一样但业务含义完全不同。翻车原因向量检索只按语义相似度排序没有涵盖接口暴露范围这个维度。后来我在检索阶段加了属性过滤召回结果必须先按路由注册信息过滤一遍再送进排序器。比如检索到三个实现后通过比对接口路由、控制器声明、注释里的业务描述来加权。这个修复让定位准确率从 70% 左右提升到 90% 以上。6.2 工具调用失控误删和误改的三道防线第二类翻车更惊险。有一次 Agent 在执行清理过期的构建缓存文件时把我手写的一批配置脚本当成了缓存给删了。日志显示它先执行了find . -name *.tmp返回了一堆文件列表但那个目录恰好有个脚本文件的后缀也是.tmp直接被扫进去了。这让我彻底意识到任何删除操作都不能让 Agent 直接执行。我立刻给执行层加了三道防线删除类命令强制走预演模式先输出将被删除的文件清单必须由用户确认后才真正执行。文件操作规则引擎记录项目里高价值文件特征比如配置、测试、部署脚本命中规则的文件不允许被删除。版本快照执行任何批量写操作前自动生成一次 git stash 或快照备份保证可回滚。现在这三道防线保证了再也没出现过误删事故。我真心建议大家别嫌这一步麻烦哪怕多花一次确认时间也远比恢复数据要省事得多。6.3 长任务执行中断断点续跑的实现还有一类问题是长任务执行到一半中断比如一个需要跑半小时的全量重构跑到第 20 分钟网络断了Agent 进程被杀。重新运行时它完全忘了之前的进度又从第一步开始。这种问题非常影响可用性。我增加了执行检查点机制任务执行引擎每完成一个步骤就把步骤结果和所有中间产物diff 文件、测试输出、临时文件路径写入本地持久化存储。重启后如果检测到同 ID 的任务存在未完成检查点规划器会直接从下一个未执行步骤继续而不是重新规划。这里有个技术坑不是所有步骤都能安全断点续跑。像写入文件这种幂等步骤可以续跑但启动外部服务这种有状态步骤续跑前必须先做状态确认或清理。我的策略是给每个步骤声明restartable属性只有标记为可安全继续的步骤才允许断点续跑否则老老实实从头来。7. 迭代方向给想复现的人一些建议7.1 最小闭环优先如果有人也想做一个类似 XiheAgent 的东西我最核心的建议是先跑通一个 10 分钟能完成的小任务闭环再谈扩展。比如从根据 JIRA 单号修改一个 Bug 并跑通对应测试开始而不是一上来就做全仓库重构、跨服务联动。压力越小越容易把链路里的状态管理、容错、日志做扎实。基础设施层面有一些能力建议尽早搭好会话持久化、工具执行日志、权限校验框架、告警通道。这些东西单独看都很基础但当你把 Agent 放到真实项目里用它们会决定你能不能在出问题时快速定位、能不能放心让它自动操作。7.2 下一步从单 Agent 到多 Agent 协作我目前正在探索的方向是把 XiheAgent 从单 Agent 演化为多 Agent 协作模式。不再是一个全能 Agent 包揽所有步骤而是拆成分析 Agent、编码 Agent、测试 Agent、运维 Agent它们之间通过任务消息队列通信。这样做的好处是每个 Agent 的上下文可以更干净——编码 Agent 不需要知道运维 Agent 的监控细节风险也更可控——高权限操作只给了运维 Agent编码 Agent 即使出格也拿不到删除权限。坏处是通信和共享状态变得复杂一个任务的状态要设计成可跨 Agent 传递的结构化数据。我还在踩坑中后续有结论了再单独写一篇分享。如果你打算复刻这个方向记住一个底线AI 编码助手的价值永远在解放重复劳动但人类必须保留最终决定权。任务怎么拆、工具怎么控、风险怎么兜底只要守住这个底线哪怕模型能力再弱一点工具本身也足够可靠。