我一直在琢磨一个问题AI编码助手被问得最多的一句话是什么不是这段代码什么意思而是你能不能直接帮我把这事干了。代码问答做得再好用户最后还是要回到终端、回到控制台手动敲命令、配调度、盯告警。项目代号羲和XiheAgent就是奔着这个缺口去的——从代码问答走到任务执行让AI不只是解释代码而是去编排任务、触发调度、处理失败告警。这篇文章我会围绕整个落地过程聊清楚架构怎么分层、DolphinScheduler是怎么接进来的、企业微信告警闭环怎么打通最后把实测踩过的坑一并交代希望对想自建AI编码助手的团队有点参考价值。1. 从会聊代码到会干活羲和项目的出发点1.1 编码助手普遍停在问答层的困局现在市面上的AI编码助手大部分能力集中在代码生成、代码解释、单元测试建议、报错分析这个范围内。做得好的上下文理解很扎实能顺着你的项目结构一路聊下去但有一个共性短板——对话闭环了工程闭环没有。举个很常见的场景凌晨两点半值班同事发现DolphinScheduler上一个数据同步任务失败了他在AI助手里问这个任务为什么失败AI助手分析完日志告诉他是源表分区没更新重跑一下就行。然后呢然后他还是要自己去打开DolphinScheduler的Web界面找到那个失败的工作流手动点击恢复再回到企微群里转发一条消息说已重跑大家留意结果。问题就出在这里AI能把是什么讲得明明白白但怎么做那一步始终停在工具之外。问答做得再丝滑也只是把人的判断力增强了一点并没有把人的操作负担真正卸下来。1.2 羲和想解决的是最后一公里的执行闭环羲和项目立项时我们定了一个很朴素的目标让用户在自然语言里表达意图AI负责拆解、调用工具、跟踪结果、回报状态。用户对着一套DolphinScheduler说的是帮我把昨天失败的同步任务重跑一遍跑完在企微群里通知大家系统要能做到识别出操作对象是哪一个工作流实例调用DolphinScheduler的REST API完成重跑动作轮询任务状态直到终态根据结果向企业微信机器人推送成功或失败的消息。这就不只是一个编码助手了而是一个带着手和脚的Agent系统。编码问答只是其中一个对话能力真正的核心是任务执行引擎——让AI的产出从文字建议变成实际动作。我在设计前期对比过几类方案直接调Shell命令、封装内部运维平台API、对接开源调度系统。最后定下来的原则是执行层必须收敛在标准化的工具协议里不能放开让模型自由发挥。这个原则贯穿了整个项目后面每一段设计都跟它有关系。2. 羲和的整体架构对话、规划、执行三者如何咬合2.1 三层能力划分与消息流转羲和整体上分三层对话理解层、Agent编排层、工具执行层。对话理解层负责把用户的口语化表达转成结构化意图Agent编排层是大脑负责决定调用哪些工具、按什么顺序调、怎么处理中间结果工具执行层是手脚每一个工具都对应一个真实的系统能力比如DolphinScheduler查询工作流实例企微机器人发送消息读取指定路径日志文件。三层之间的消息流转我画过一张时序图这里不贴图用文字描述用户提问对话层将其转为意图参数JSON交给编排层编排层把它变成工具调用计划逐项下发到执行层执行层每次返回统一的工具结果包编排层判断是否继续下一个工具或直接生成回复最后回复经对话层包装返回给用户。这条链路里最容易被忽略的是中间状态的一致性。用户问了一个问题编排层决定先查任务状态、再查日志、再决定重跑那整个链路的中间结果必须能被追踪。我们在每条请求里都带了一个trace_id在Redis里维护当前执行进度页面端刷新时能看到正在查询任务状态、已定位失败原因、准备触发重跑这种实时进度体感上就不是在跟一个黑盒对话而是在跟一个看得见操作过程的助手对话。2.2 工具协议Agent和外部系统的共同语言工具协议是整个架构的地基。每个工具都按统一的JSON Schema描述包括工具名、描述、入参、出参、权限级别。模型侧看到的是一份工具清单执行侧拿到的是一个固定格式的调用请求。{ tool_name: dolphinscheduler_rerun_workflow, description: 重新运行指定工作流实例, parameters: { project_code: {type: string, required: true}, workflow_instance_id: {type: integer, required: true} }, permission_level: high, timeout_seconds: 30 }为什么必须用JsonSchema这套因为LLM的返回是自由文本直接拿自由文本去拼Shell或者拼HTTP请求太危险了。协议化之后模型输出先过一个严格校验器字段缺失、类型错误、枚举越界都会被打回重新生成。实测下来这一步能把因为模型输出格式漂移导致的执行失败率从15%以上压到1%以内。2.3 任务执行前的安全闸门任务执行的能力有了安全性就是第一道红线。羲和做了四道闸门第一道是命令白名单。工具能力一个萝卜一个坑所有外部调用都必须走预定义的工具不存在把模型输出直接接进终端这种路。第二道是参数校验。例如重跑某个工作流之前后端要先查这个工作流是否存在于当前项目下、是否在可执行的时间窗口内状态是不是终态避免拿着一个已经跑完的任务去重跑。第三道是风险分级。只读操作查询、列表自动执行写操作重跑、停止、下线默认需要用户在对话里二次确认客户端弹一个确认卡片。第四道是操作审计。每次工具调用都落审计日志包含谁发起的、哪个会话、什么时候、调了什么、结果如何后面出问题能追溯到完整链条。有朋友问过二次确认会不会打断流畅度我的看法是流畅度是给查代码用的安全是给生产环境用的这两者不能混为一谈。确实查个日志还要确认会很烦所以规则是只读免确认、写操作必确认。上线运行了一个多月没有一例误操作这个代价完全值得。3. 接入DolphinScheduler让AI真正去编排调度任务3.1 为什么第一个执行场景选了DolphinScheduler不是随便选的。我们内部的数据任务调度基本都在DolphinScheduler上它有两大优势让Agent接入特别顺一是REST API覆盖全面创建流程、查询实例、启动运行、停止重跑都有官方接口不需要去逆向页面接口二是工作流状态机清晰SUBMITTED_SUCCESS、RUNNING_EXECUTION、FAILURE、SUCCESS这些状态都是确定性的Agent判断逻辑不用玩模糊匹配。对比过AirflowAPI能力同样很强但当时我们团队对DolphinScheduler更熟悉而且它自带的告警通知和项目管理模块跟企微的联动成本更低。选一个自己最熟的场景做切入点能把精力集中在Agent本身的编排能力上而不是花大把时间去搞懂调度系统的细节。3.2 调度工具的协议设计与接入流程DolphinScheduler的接入不是把几个HTTP请求封装一下就完事关键在于把Agent能理解的动作映射成系统的安全操作。我给DolphinScheduler侧定义了四组工具查询类查询项目列表、查询工作流定义、查询工作流实例状态、查询任务日志操作类启动工作流、重跑工作流实例、停止运行中的实例、下线工作流创建类创建工作流定义、更新调度配置审计类读取执行历史、读取操作记录。每个工具在Agent的工具清单里都有清晰的description。模型通过描述决定什么时候该调哪个工具描述写得越准确模型的工具选择就越稳。例如查询工作流实例状态的描述是当用户询问任务是否跑完、是否失败或查看运行进度时使用入参project_code和workflow_instance_id必填。接入的流程层面我们封装了一个dolphinscheduler_client核心逻辑大概长这样def rerun_workflow_instance(project_code: str, workflow_instance_id: int) - dict: # 第一步确认实例存在且处于可重跑的终态 instance ds_client.query_instance_by_id(project_code, workflow_instance_id) if instance[state] not in [FAILURE, STOP, SUCCESS]: return {ok: False, message: f当前状态{instance[state]}不可重跑} # 第二步调用重跑接口 resp ds_client.rerun_instance(project_code, workflow_instance_id) # 第三步启动异步轮询把状态变化推回Agent上下文 return {ok: True, instance_id: workflow_instance_id, status: RE_RUN_TRIGGERED}DolphinScheduler新版API带token鉴权调用前先申请token再带在请求头里超时时间设长一点因为工作流启动不是秒级返回的轮询间隔建议在10到30秒之间写得太短会给调度系统带来无谓压力。3.3 从帮我重跑昨天的数据任务到流程下发的完整链路完整链路实测下来是这么走的用户发来一句帮我看一下昨天凌晨的数据同步任务好像失败了如果失败就重跑跑完在群里说一声。对话理解层先抽取意图目标对象是昨天凌晨的数据同步任务动作候选是查询状态 - 条件重跑 - 企微通知。编排层拿到这份意图后先调查询工作流实例状态发现该实例确实处于FAILURE终态于是进入写操作流程在对话里回给用户一个确认卡片检测到任务X在2025-06-10 02:00:00的实例运行失败是否重跑用户点击确认后编排层调用重跑工具随后进入一个异步状态机不断查询新实例状态直到状态变为SUCCESS或FAILURE。跑通这个链路最大的感受是Agent的价值不在于某一个工具调用多精准而在于把查状态、判断、执行、跟踪、通知这一长串动作编排得像一个人做的一样顺。为了让这条链路可复用我们把查询后条件执行这种多步模式做成了模板用户下次说看xx任务失败就重试时编排层会把已有的计划模板调出来而不是每次从头规划。4. 失败告警闭环企业微信通知是怎么打通的4.1 告警不只是一条消息那么简单把任务执行跟企业微信告警打通乍一看就是个webhook推送的事实际做起来远比想象中复杂。这里有两层需求。第一层是任务本身的失败告警DolphinScheduler工作流失败时自动向指定企微群推送一条结构化告警包含任务名、实例ID、失败时间、失败原因摘要。第二层是Agent执行动作的告警用户说跑完在群里说一声这个说一声本身也是一个工具调用要向企微机器人发送一条执行结果消息。我选了企微的群机器人webhook它对内网服务很友好不需要额外申请应用只要在群里添加一个自定义机器人拿到webhook地址POST一个JSON就能发消息。消息格式支持text和markdown我们统一用markdown字段排版清晰失败原因能用代码块圈起来大幅提升可读性。4.2 企微机器人消息模板与幂等控制消息模板设计成两套一套给任务失败告警一套给Agent执行结果通知。失败告警模板## 数据同步任务执行失败 - **任务名称**ods_order_daily_sync - **实例ID**147258369 - **失败时间**2025-06-10 02:04:12 - **失败节点**ods_order_daily_sync-shell-task - **日志摘要**ERROR: table ods_order_daily_20250609 not exists - **下一步建议**可对羲和说重跑这个任务这个模板里藏着一个小细节下一步建议里的提示不是硬编码的而是Agent在检测到FAILURE后根据失败原因自动生成的。如果模型从日志里判断是源表不存在就回复重跑前先检查源表是否就绪如果判断是偶发网络超时才回复可以重跑。这比固定文案请人工查看处理有温度得多也真的能帮值班的人减少一次点开日志的动作。幂等控制是告警里最容易翻车的地方。任务状态轮询和webhook重试如果没做好幂等同一个失败会重复推送好几条。我们给每条告警生成一个message_hash以任务实例ID 状态 首次发现时间为key在Redis里设置5分钟的自动过期窗口。窗口内同一key的告警直接丢弃。这样即使重试机制触发群里也不会被同一条告警刷屏。调用代码简化一下长这样import requests, hashlib, redis, time def send_alert(webhook_url: str, msg_md: str, dedup_key: str, ttl300): key alert:dedup: hashlib.md5(dedup_key.encode()).hexdigest() if redis_client.set(key, 1, exttl, nxTrue): requests.post(webhook_url, json{msgtype: markdown, markdown: {content: msg_md}}) return True return FalsenxTrue是关键SetNx天然具备只有第一次写成功的语义比先查再写要原子得多。4.3 把执行结果喂回给Agent形成真正的闭环告警发完不是终点。我在羲和里还做了一层反向联动企微群的告警消息本身可以成为触发Agent新一轮操作的信号。具体做法是在企微群里配置了一个消息监听机器人当有人在群里 羲和 并说这个失败是什么原因系统会把前面那封告警消息的上下文任务名、实例ID、日志摘要捞出来跟当前提问拼接成一条带上下文的对话请求发给模型。模型不需要用户重新提供任务名直接基于告警里的实例ID去查日志、分析原因、给出答复。这层设计在实操中特别省事。值班同学最痛的就是复制任务ID、打开系统、翻日志这一套流程现在只需要 一下助手上下文自动带过去。我后来在复盘时想通了一件事Agent的能力不只是接口调用还包括对消息流的事件感知。谁能让告警、日志、执行动作收敛在同一条上下文链路里谁就真正把运维闭环做出来了。5. 实测过程中踩过的坑和取舍心得5.1 上下文与token管理工具返回太长怎么办AI编码助手最经典的翻车现场不是模型不懂而是工具返回内容太长把上下文塞爆了。第一次联调时我们让Agent去查一个失败任务的完整日志DolphinScheduler接口把几百KB日志原样返回模型瞬间懵了开始胡编乱造而且那次对话的token消耗直接爆表。后来定了一个硬规则默认情况下工具返回需要经过摘要剪裁器。日志类结果只保留前50行和后50行中间用……省略N行……替代任务实例列表只保留最近10条异常堆栈只保留前20层。只有在用户明确要求把完整日志发我时整理层才走全文通道经由文件传输而不是塞进对话上下文。另外一个相关的心得是不要让一个工具调用的结果直接在对话里展开。工具返回先进一个记忆缓冲池模型只看摘要用户看到的是整理过的卡片。这一步让token开销降了约40%而且回答质量反而更好——因为模型不用在噪声里找重点了。5.2 LLM的工具调用幻觉与双重校验模型在工具调用上会产生一种很隐蔽的幻觉参数是对的、工具名是对的但意图理解偏了。比如用户说把那个失败的任务停掉模型可能调了下线工作流定义而不是停止当前实例两者在DolphinScheduler里是完全不同的操作后果也完全不同。我做了双重校验来兜底。第一重是前面提到的JsonSchema校验保证字段形态正确。第二重是业务语义校验器每个工具定义里带一个condition_rules例如停止工作流实例要求实例状态为RUNNING或PAUSED否则返回冲突提示下线工作流定义要求该流程没有运行中的实例否则提示先停止。校验器不通过时工具层会返回一个可读的冲突原因模型看到原因后重新规划而不是把错误结果硬抛给用户。这个设计思路其实就是把领域的业务规则从模型脑子里搬到代码里。模型负责理解用户意图代码负责保证操作合法性各司其职幻觉的影响就被有效隔离了。5.3 并发与幂等任务重复执行是灾难多用户同时问一个任务时事故隐患很大。两个值班同事同时让助理重跑同一个失败实例如果没有锁DolphinScheduler里就可能出现两个重复运行实例数据链路写两次直接污染结果。解决办法是在工具执行层加实例级分布式锁。以workflow_instance_id为粒度执行重跑动作前先抢锁抢不到就返回该任务正在处理中请稍候。锁超时根据任务预计运行时间动态设置默认给5分钟超过自动释放。配合数据库里的操作记录唯一索引同一个实例同一类型操作最多只能有一条成功记录。我还把幂等键加了进去。前端确认卡片每次生成一个session_action_id后端在执行前先查这个id是否已经执行过执行过就直接返回上次结果。这样即使网络抖动导致前端重试也不会产生重复操作。5.4 对话体验上的几个细节这块容易被技术细节掩盖但恰恰是最影响感知的。实测下来有三个点特别值得改进一是流式返回一定要做。Agent执行任务时如果好几十秒没有任何文字反馈用户会以为系统卡死了。我们在客户端用SSE流式推送执行进度正在查询任务状态、检测到失败原因、正在等待用户确认、已触发重跑这些节点依次滚出来体验提升是几倍的。二是操作结果要可视化。不能只发一行任务已重跑要带上新实例ID、当前状态、预计运行时长最好再带一个链接用户点进去直接到DolphinScheduler对应的实例页。链接跳转是老姿势但真的有用因为它把Agent的结论跟真实系统的凭据对齐了。三是主动提醒优于被动回答。任务从FAILURE转到SUCCESS时用户并没有再提问但系统会在企微群推一条上次重跑的任务已成功完成。这种主动事件推送让用户对Agent的信任感增长很快——他开始觉得这不只是个问答工具而是一个真的在盯活的助手。6. 后续演进思路与适用边界6.1 从单Agent到多Agent协作的规划羲和现在的形态是单Agent统一编排好处是实现直接、排查方便但到了复杂场景就开始吃力。设想一个场景用户说帮我检查所有近一周失败的数据任务按影响范围分组列出并自动修复其中因源表缺失失败的任务。这涉及多个DolphinScheduler项目、多个数据源连接器、多个失败原因分支单Agent在一个上下文里全部做完判断上下文很快就撑不住了。我计划下一步引入场景化子Agent一个调度Agent专门负责DolphinScheduler交互一个数据质量Agent负责检查数据血缘和源表状态一个通知Agent负责所有消息发送。主Agent只负责拆解目标和汇总结果子Agent各自维护自己的上下文记忆。这也会让工具的权限管理更干净——每个子Agent只能访问自己领域内的工具主Agent的权限反而收窄了。6.2 什么样的场景适合接任务执行这个项目的经验迁移到别的领域时我提醒自己先回答一个问题这个场景的操作边界是不是清晰可控的。适合接的典型特征有三个一是系统本身拥有标准API操作可以被枚举不需要依赖非结构化的UI自动化二是状态机清晰成功、失败、重试都能明确判定不存在模糊的中间态三是操作有明确的幂等键设计重复调用不会造成业务上的破坏。DolphinScheduler调度恰好满足这三条。如果你是在给一个没有API、只能靠模拟点击去操作的老系统做Agent我建议先把自动化底座补齐再谈Agent不然会给模型塞进海量的不确定性结果一定会很难看。6.3 个人最深的体会回顾这轮开发让我最意外的其实不是Agent技术本身而是使用者行为的改变。当企微群里的告警可以直接助手去查、去重跑、去跟踪时值班同事的操作路径从打开系统-找任务-看日志-判断-操作-通知六个动作压缩成了一句口语。工具没有变API没有变变的只是交互层把复杂结构全部折叠了。往后我大概率会把更多精力放在执行可靠性而不是对话花活上。代码问答这种能力早已不是壁垒真正难的是让AI每次调用工具都准确、可回滚、不越权、不重复。把这一层做扎实AI编码助手才真正配得上助手两个字。