1. 为什么做羲和:从"能聊天"到"能干活"的跨越
做开发工具这些年,我越来越清楚一件事:AI 编码助手如果只能聊天,价值就折掉了一半。市面上的 AI 编程助手,从最早基于代码补全的模型,到现在能理解整个仓库上下文的对话式产品,能力确实在快速进化。但真用起来你会发现一个尴尬的点——它把代码写得再漂亮,最后落地执行的还是你。复制代码、打开终端、跑测试、修报错、再提交,这一整条流水线 AI 全程围观却插不上手。
羲和(XiheAgent)想解决的就是这个断层。它的定位不是又一个"AI 问答盒子",而是把代码问答和任务执行打通的一条完整链路。你问它"这个调度任务为什么一直失败",它不仅能定位到问题代码,还能在你授权后直接把补丁提交到调度平台、触发一次重跑、帮你确认执行结果。从"给答案"到"把事办成",这是羲和与普通对话式编码助手最本质的区别。
这个项目适合谁?如果你维护着多套服务,每天要处理大量重复的查代码、改配置、调任务、看告警这类体力活,羲和的模式会非常对胃口。如果你只是想要一个能聊代码的 Copilot,现有工具已经够用,没必要搭一整条执行链路。做这个项目的核心判断是:AI 的判断能力只有和工程体系的执行能力对接起来,它才能从一个"参谋"变成"队伍里能干活的成员"。
2. 整体设计:三层架构与关键取舍
2.1 功能层:自然语言入口与意图路由
羲和的用户入口就是一个对话框,但后端的第一步并不是直接把问题丢给大模型,而是先做意图路由。我把它分成三类:问答类("解释一下这个函数的逻辑")、任务类("调度平台里 id 为 1024 的任务今天为什么失败了")、操作类("把修复后的脚本重新提交并触发执行")。
这三类对系统的能力要求完全不同。问答类只需要检索代码、组织答案,不触碰任何外部系统;任务类需要拉取调度平台的执行日志、结合代码上下文做根因分析;操作类最重,需要把自然语言转成具体的 API 调用,并且经过用户确认后才真正执行。把这三类混在一个 Prompt 里让模型自己猜,很容易出现答非所问、越权操作的情况。所以我在前端就做了一个轻量级分类器,用少量标注数据训练了两层意图判断:先判断是否涉及"执行",再判断涉及哪个目标系统。
2.2 解析层:代码理解与任务拆解
确定意图后,解析层负责把用户的自然语言和系统状态对齐。比如用户说"上次那个任务又挂了",这个"上次"和"又挂了"必须被解析成具体的对象:哪个工作流?哪个任务实例?失败原因是什么?这需要三个信息源的配合。
- 代码索引:对仓库做 tree-sitter 语法解析和 embedding 向量化,建立"文件—函数—调用关系"的索引,支撑代码检索和影响面分析。
- 状态快照:实时拉取 DolphinScheduler 的工作流定义、实例状态、日志摘要,让模型能基于真实运行数据回答问题,而不是凭空猜测。
- 历史上下文:保留同一个会话窗口内的对话记忆,用户说"上次那个"时,能关联到之前讨论过的具体任务。
任务拆解部分我踩过的坑是:一开始让模型直接输出"调用某个 API"的完整参数,结果经常出现参数幻觉,比如把 processDefinitionId 和 projectId 搞混。后来改成两步走——模型先输出结构化的"操作意图 JSON",再由代码层把 JSON 映射到真实的 API 调用参数,这样即使模型猜错了 ID,代码层也能通过名称匹配做二次校验纠偏。
2.3 执行层:沙箱、调度器与消息通道
执行层是整个系统风险最高、也最需要做防护的部分。我的设计是三段隔离:
- 代码执行沙箱:任何由 AI 生成或修改的脚本,不会直接跑在宿主机上。我用了容器方案,每次执行拉起一个临时容器,挂载只读的代码目录,网络默认隔离,执行完销毁。
- 调度器桥接服务:与 DolphinScheduler 的交互单独做成一个后端服务,封装创建、更新、发布、触发、查询状态等操作,提供一套带 token 鉴权的 HTTP 接口给羲和调用。
- 消息通道:企微告警、站内通知都走统一的消息服务,保证任务失败、执行成功这类关键事件能及时触达相关人。
这层设计最重要的原则是:执行链路必须有"人参与确认"的环节。涉及数据变更、任务发布这类操作,羲和会先把完整的执行计划列出来(改了什么、调用什么接口、影响哪些任务),等用户点击确认后才真正发出请求。宁可多一步确认,也不能让 AI 在无人监管的状态下动生产环境。
3. 代码问答能力:不只是"能聊",还要"聊得准"
3.1 代码索引的搭建与坑
代码问答的质量,很大程度上取决于检索能力,而不是模型能力。我一开始图省事,把整个仓库的文件全塞进上下文,指望长上下文模型自己找答案。结果仓库稍微大一点,Token 消耗爆炸,而且关键信息容易被淹没在无关代码里。后来改成"索引优先"的方案:用 tree-sitter 解析出每个文件的函数、类、导入关系,再对函数体的摘要做向量化,查询时先搜出 Top 5 相关函数,连同它们的调用链一起交给模型。
这个方案里有一个很关键的细节——索引要与实际执行任务联动。比如用户问"某个调度任务为什么失败",系统不是直接搜全仓库,而是先通过 DolphinScheduler 拿到该任务对应的脚本路径,再只对这个脚本及其依赖文件做检索分析。范围一缩小,准确率提升非常明显。实测下来,定位问题的准确率从大约六成提高到接近九成。
检索的质量依赖分块策略。太粗的块(整个文件)会混入大量无关信息,太细的块(单行)又丢失上下文。我最后用的是"函数级分块 + 按调用链拼接":每个块是一个函数的完整实现,检索时把被测函数的前后调用者一起打包给模型。这样模型既能看懂某个函数在做什么,也能理解它在整个调用路径里的位置。
3.2 代码问答里的上下文管理技巧
模型上下文窗口虽然一直在变大,但"塞得下"不代表"效果好"。代码问答场景里,我总结出三条上下文纪律:
- 先结论后推理:Prompt 结构上要求模型先输出结论(比如"失败原因大概率是数据库连接池耗尽"),再给出推理过程。因为工程人员在使用时,第一眼需要的是判断方向,细节可以慢慢看。
- 日志优先级高于代码:排查任务失败问题时,执行日志往往比代码本身更有价值。我在系统里做了一个日志预处理模块,自动对海量日志做摘要提取,只保留异常栈、关键错误码、耗时统计,再把这部分压缩后的信息连同相关代码一起交给模型。
- 会话内的多轮改写:用户后续追问"那该怎么改",模型需要结合前面提到的任务和日志来回答。我维护了一个会话状态表,每一轮都重新拼接"当前目标 + 关键上下文摘要 + 最新问题",而不是把全部历史消息一股脑丢进去。
4. 任务执行核心链路:对接 DolphinScheduler 的实践
4.1 为什么选 DolphinScheduler 作为执行底座
做任务执行这一步,面临一个选型问题:自己写调度器,还是接入现成的调度平台。我的选择是直接对接 DolphinScheduler,原因有三点。第一,它的 REST API 覆盖了从项目管理、工作流定义到任务实例查询的完整链路,省去了自己造轮子的成本;第二,它天然支持 Shell、SQL、Python、HTTP 等多种任务类型,正好满足 AI 生成产物多样化的执行需求;第三,社区活跃、文档齐全,遇到调度 bug 时能找到现成的解决方案。
当然,接入 DolphinScheduler 不是没有代价。它本身的部署和维护就有一定复杂度(依赖数据库、ZK 等组件),API 版本间也有一些兼容性问题。我的经验是把桥接服务做成独立模块,把 DolphinScheduler 的 API 调用全部收敛在这一层,避免上游业务代码被调度平台的细节污染。
4.2 任务提交与状态同步的核心链路
羲和触发一个任务执行的完整链路如下:
用户确认执行计划 → 羲和执行服务 → DS Bridge 服务 → DolphinScheduler REST API → 创建工作流/更新流程定义 → 发布上线 → 启动工作流实例 → 轮询实例状态 → 状态入库 → 按结果决定下一步(告警/通知/继续操作)这一步里最容易出问题的地方在于 DolphinScheduler 的"版本"概念。它的流程定义更新后,必须显式执行"发布"操作,新版本才会生效。我第一版对接时漏了这一步,结果出现一个很隐蔽的 bug:代码更新成功了,但实际跑的还是旧版本脚本。后来在桥接服务里加了一个强制流程——创建或更新流程定义后,立刻调用 release 接口,并且校验返回结果中的版本号与预期一致,才彻底解决这个问题。
任务实例的状态同步,我做过一版"短轮询"方案:每 10 秒查一次实例状态,直到终态。但调度任务里有很多长耗时任务(跑数、训练等),短轮询既浪费请求又容易触发 API 限流。现在改成两段式:前端先等 30 秒,用 DolphinScheduler 的查询接口拿一次状态;如果还在运行中,就转成后台异步监听,通过企微 webhook 回调通知最终结果。这样既保证了实时性,又不会一直占着连接。
4.3 执行产物的校验与回滚机制
AI 执行任务,最怕一个问题:跑是跑起来了,但结果不对。我加了三个校验环节。第一,语法级校验:AI 生成的 Shell/SQL 脚本,在提交给调度平台前先做静态检查,Shell 用bash -n,SQL 用 Explain 模式预执行,避免把明显有语法问题的脚本发到生产调度平台。第二,Mock 预演:对有时间窗口的任务,先在测试库里跑一遍逻辑,确认核心指标正常后再正式触发。第三,版本留痕:每次任务执行前自动记录当前脚本的 MD5,执行结果异常时能快速对比到底是"代码变了"还是"环境变了"。
回滚机制上,我依托 DolphinScheduler 的流程定义版本管理做了一个"一键回退"接口。当 AI 修改后的脚本在生产环境跑挂时,桥接服务能自动拉取上一个正常版本并重新发布。实测中这个功能拯救了不止一次线上事故,值得专门做成一个独立能力。
5. 失败告警:任务挂了,怎么让人第一时间知道
5.1 企业微信告警通道的搭建
调度任务不可能永远成功,告警能力是整套执行链路的"安全网"。我选择企业微信机器人作为主要告警通道,原因是它接入成本低、触达率高——团队里大家本来就在企微上,消息不容易被漏掉。
企微机器人的 webhook 接口调用很简单,但对消息格式有要求。我封装了一个微信推送服务,支持文本和 Markdown 两种消息类型。文本消息适合简单的"任务失败"通知,Markdown 消息适合带详细上下文的告警卡片。企微 webhook 发送时有个隐藏限制:频繁调用会触发频控,一般限制是每个机器人每分钟最多 20 条消息。我在这层加了本地队列做削峰,把突发的大量告警合并成一条汇总消息发出去,避免封禁。
5.2 告警模板与信息分级
告警不是把错误信息原样丢出来就完事。我设计了三个级别的告警模板:
- P0 级(立即通知):关键生产任务连续失败超过阈值,推送 Markdown 格式的完整告警卡片,包含失败时间、任务 ID、错误摘要、触发人、建议处理方向。
- P1 级(汇总通知):批量任务中有部分失败,每 15 分钟汇总一条,列出失败任务清单和占比,避免告警轰炸。
- P2 级(静默处理):非核心任务失败,只记录日志并在日报中体现,不打扰人。
告警内容里我特别加了一个"AI 诊断意见"字段。这个字段由羲和在检测到失败事件后,主动拉取执行日志和代码变更记录,生成一句话判断(比如"疑似最近一次脚本修改引入了空指针,建议回滚到上一版本")。这个能力在实战中很受团队欢迎——告警不只是通知你"出事了",还顺带告诉你"大概哪里出事了"。
5.3 告警防抖与升级机制
告警太多会让人麻木,太少又怕漏掉关键事件。我实现了一个简单的防抖器:同一任务同一类型的失败,在 30 分钟内只发送一条告警,后续状态变化(比如恢复成功)会再补一条"恢复通知"。这样既不会轰炸,又能让团队了解到事态的全过程。
升级机制也是必要的。当 P0 告警发出后 10 分钟内任务仍未恢复,并且没有人在企微群里点击"我接手处理",系统会自动升级通知到值班负责人,并附带完整的失败链路时间线。这个机制本质上是在"AI 自主执行"和"人肉兜底"之间做了一个平衡——AI 可以尝试自动修复,但人有权利也有渠道随时接管,不能让机器在故障场景下完全失控。
6. 落地过程中的常见问题与排查实录
6.1 任务状态不一致:AI 以为成功了,实际上失败了
这是我在使用中遇到的最头疼的问题。AI 通过 API 查询 DolphinScheduler 任务实例状态时,有时会拿到"SUCCESS"的状态,但实际任务是失败的。查下来原因是:DolphinScheduler 的流程实例状态是"最终态",但内部单个任务节点可能有 FAILED 却被后续的重试逻辑覆盖;或者流程虽然标记为成功,但某些分支节点被跳过,没有真正执行。
我的解决办法是:状态判断不能只依赖流程实例的 summary 状态,必须去查询实例下每个任务节点的明细状态,任何一个关键节点是 FAILED 就算整体失败。同时在中枢服务里维护一张"任务执行状态表",以自己实时记录的状态为准,DolphinScheduler 的返回只作为辅助参考。这样即使调度平台因为各种原因返回了误导性状态,告警逻辑也能基于真实记录做出正确触发。
6.2 Token 超限与上下文污染
任务执行失败后,拉取日志做分析时,很容易把一条几 MB 的日志全塞给模型,直接触发 Token 超限。更隐蔽的问题是:大量重复运行日志会"污染"模型的判断——模型看到的是几十次一模一样的INFO日志,真正的异常信息反而被淹没了。
后来我加了一个日志摘要层:对原始日志做行级分类(INFO/WARN/ERROR/异常栈),只保留 ERROR 和异常栈前后各 20 行,再对重复内容做去重和合并。这样一个几 MB 的日志文件通常能压缩到 1000 字以内,既省钱又提高了分析准确率。另一个技巧是给模型显式说明"这是压缩后的日志摘要,不是完整日志,缺失上下文可能影响判断",这能显著减少模型基于不完整信息做出错误推断的概率。
6.3 权限与安全的边界控制
AI 能执行任务后,权限边界就变成了首要问题。我踩过的一个教训是:桥接服务刚开始用的 token 具有全部项目管理权限,某次 AI 在上下文驱动下,把另一个业务线的调度任务也"顺手"更新了。虽然因为流程里有人工确认环节没有造成实际损失,但这暴露了权限粒度太粗的问题。
现在的方案是:羲和系统对接企业内部的单点登录,用户在对话框里触发执行操作时,羲和会校验该用户对目标项目的操作权限,只有同时满足"用户有权限 + 桥接服务 token 有权限"才放行。审计日志也会完整记录每次操作的用户、时间、指令、API 调用和返回结果,作为事后追溯的依据。从实际经验看,权限控制不是一个技术问题,而是一个组织问题——你需要在系统设计之初就和生产运维团队确认清楚,哪些操作允许自动化、哪些必须留给人来做。
6.4 DolphinScheduler 的高可用与 API 限流
调度平台本身的稳定性会直接拖垮整个自动化链路。DolphinScheduler 的 API 有并发连接数限制,大量任务同时触发时,会出现连接超时和数据拉取失败。我的方案是在桥接服务里做两层保护:一个简单的信号量控制并发请求数(最高 20 个并发),另一个是请求级重试机制——对 5xx 和超时错误做三次指数退避重试,对 4xx 错误直接丢弃不重试。
还有一个取巧的做法是:高频状态查询不直接打到 DolphinScheduler,而是缓存到 Redis,由桥接服务自己维护一个"调度事件表",只在任务状态发生跳变时触发后续动作。这极大地降低了对调度平台的请求压力。目前这套机制已经跑了几个月,DolphinScheduler 侧没有再出现过因查询压力导致的服务不可用。
7. 后续演进方向与个人体会
羲和目前已经稳定承担了团队日常的代码问答、故障排查和任务调度辅助工作。我个人体会最深的一点是:开发一个 AI 编码助手,技术难点从来不在模型本身,而在于你愿不愿意花精力把所有周边系统(代码仓库、调度平台、消息通道、权限体系)打磨得足够规整。AI 的能力边界很大程度由它所接触的系统边界决定——只给它一个对话框,它就只是一个聊天机器人;给它接上整个工程体系,它才真正成为团队的一员。
后续我打算做两个方向的扩展。一是增加更多执行入口,比如支持通过语音触发告警确认操作,在移动端就能完成紧急故障的处理闭环;二是把 AI 的执行经验沉淀成可复用的知识库——每次修复成功的案例都自动生成故障报告和修复模板,让下一次类似问题时,羲和能直接给出经过验证的解决方案,而不是每次都从零开始推理。
如果你也在做类似的方向,我的建议是:先把"最小闭环"跑通,不要一上来就想做一个综合大系统。从"让 AI 能查询任务状态"这样一个小能力开始,逐步扩展到修改、执行、告警,每加一个能力都要确保可回滚、可审计、可人工干预。自动化是手段,可靠才是目标,这条原则值得贯穿整个项目的生命周期。