
1. 项目背景与整体设计思路1.1 为什么团队要自研一个 AI 编码助手做这个项目之前我们团队其实已经尝试过市面上好几个现成的 AI 编程工具。日常写单测、生成样板代码确实好用但一旦牵扯到内部的基建体系问题就来了。我们的工程里大量涉及工作流调度、任务编排、告警通知这类的领域逻辑市面上的通用助手根本不理解我们的 DolphinScheduler 工作流长什么样不清楚企微告警机器人应该怎么接更不知道我们内部服务之间的调用约定。换句话说通用工具在“代码问答”这个层面做得很好但它离“真正帮我们把活干了”还很远。举个例子团队里新来的同学经常要问“我们那个订单超时未支付的任务挂在哪个工作流里”“这个工作流的参数是怎么传的”。这些问题的答案其实散落在代码仓库、调度平台配置和内部文档里。通用 AI 工具要么答不对要么答得似是而非。我们意识到要想让 AI 真正帮上忙它必须对我们团队自己的工程上下文有认知。这就引出了项目的核心命题做一个从“能回答问题”进化到“能执行任务”的 AI 编码助手。它能基于我们内部代码和调度平台的数据回答研发问题同时能接住“帮我把这个任务跑起来”“调度失败了帮我查一下原因”这类指令把 AI 从聊天窗口推到执行层面。1.2 两条能力线的取舍问答与执行的边界设计初期最纠结的其实是边界问题——AI 该不该直接操作生产环境的调度平台团队里反对的声音不少主要担心 AI 误操作把生产工作流给停了。最后我们达成的设计共识是问答能力面向全量代码和调度配置执行能力只开放给高频且低风险的操作场景。具体拆下来两条线的能力是这样的问答线负责什么代码结构问答、调度平台配置解析、工作流依赖关系查询、历史告警归因分析。比如“订单服务最近变更过哪些代码影响了工作流执行”这种问题助手能结合 Git 提交记录和 DolphinScheduler 的执行历史给出回答。执行线负责什么触发某个工作流的补数任务、暂停/恢复某个任务组、在测试环境跑一次数据校验、把任务执行失败的关键信息发到指定的企微群。这些动作有一个共同点可回滚、影响面可控、且操作路径清晰。这个取舍很关键。它保证了项目初期不会因为 AI 误操作导致重大事故同时也让 AI 真正跨过了“说到”和“做到”之间的坎。1.3 立项时的三个硬性指标项目启动前我们定了三个硬性指标用来评估这个 AI 编码助手是否真的值得做下去。第一代码问答的准确率要超过 90%。这个准确率不是让 AI 自己打分而是用我们内部标注的问答集来验证每条问题都有明确的参考答案AI 的回答必须包含关键信息点才算通过。第二任务执行的成功率不低于 85%。AI 理解了用户的意图之后能否正确映射到 DolphinScheduler 上具体的 API 调用并执行成功这个链路必须稳定。第三告警触达的延迟在 1 分钟以内。任务执行失败后AI 要在 1 分钟内把告警推到企微群并且包含足够多的上下文信息让收到告警的人不需要再去翻系统就能判断问题严重性。这三个指标后来成了整个项目迭代的导航仪每次改动都会跑一遍回归验证确保没有牺牲稳定性来换花活。2. 系统架构与技术选型2.1 整体架构不是微服务是模块化单体架构设计时我们故意没有上微服务。原因很简单团队规模有限AI 助手的调用链路本来就不复杂拆成微服务只会增加部署和运维成本。最终采用的是模块化单体架构在一个服务进程里通过清晰的模块边界来隔离不同职责。整个系统分四层接入层负责统一接收来自 IDE 插件、企微机器人、命令行三种渠道的请求把不同渠道的输入归一化成内部的协议格式。语义理解层承载了大模型调用、意图识别、槽位提取、上下文管理这些核心逻辑是 AI 能力的主战场。任务执行层负责把语义理解层产出的“意图参数”翻译成对 DolphinScheduler 的实际 API 调用并管理任务的生命周期。上下文服务层维护代码索引、调度配置快照、告警历史数据给上层能力提供数据支撑。为什么这么分层关键在于让“问答”和“执行”共用一套底层数据但走完全不同的处理逻辑。问答走的是检索加生成的路子执行走的是意图识别加工具调用的路子。如果混在一起改动一个功能很容易影响另一个。用生活场景来类比的话这个架构有点像一家饭店的厨房。接入层是门口迎宾的服务员语义理解层是掌握菜单的大厨执行层是负责颠勺的厨工上下文服务层则是后面的食材仓库。每个角色各管一摊但最终呈现在顾客面前的是一道完整的菜。2.2 语义理解层大模型之外的工程拼图很多人以为语义理解层就是调用一个大模型就完事了实际远没有这么简单。模型选择上我们对比过好几家最终选了编程能力相对均衡、工具调用格式稳定的模型作为底座。但真正花功夫的是模型外围的工程化组件。意图识别的做法是对齐 OpenAI 的 Function Calling 协议。我们预先定义了十几个工具函数每个函数都有严格参数 Schema。模型的任务不是自由发挥回答问题而是在接到用户输入后判断应该调用哪个工具函数。判断对了就提取参数提取完就交给任务执行层。这个设计的好处在于稳定性。模型擅长的事情是“分类”和“提取”而不是“编造”API 调用。一旦约束它只能输出工具调用格式胡说八道的概率大大降低。上下文管理上也踩了不少坑。最初我们是把用户对话历史和代码检索结果一股脑全塞给模型结果上下文一长模型就开始分不清哪些是用户的话哪些是参考材料。后来改为分段管理系统指令单独维护用户问题带时间戳代码片段和调度数据打上类型标记模型每次只读当前轮次真正需要的上下文。还有个容易被忽略的点是流式输出的处理。模型生成内容是流式返回的如果直接把流式内容往外部推送用户看到的可能是一半一半的内容。我们统一做了一层缓冲等模型完成全部工具参数输出后再拼接成完整的消息下发保证了体验的一致性。2.3 任务执行层与 DolphinScheduler 的通信协议设计任务执行层是整个系统里工程含量最高的部分。它要和 DolphinScheduler 打交道而这套系统的 API 设计比较重直接裸调很容易被对方的各种异常搞崩。我们做了一层适配器。适配器的作用是双面的对内给上层提供简单的接口比如 startWorkflow、pauseTask、queryStatus对外负责处理 DolphinScheduler 的认证、分页、限速等细节。具体场景打个比方。用户说“帮我把昨天的补数任务重新跑一遍”语义理解层把这句话转成了 startWorkflow(workflowNamefinance_daily_rebuild, execDate2024-01-22)。任务执行层拿到这个指令后先通过 DolphinScheduler 的查询接口定位到工作流的具体 ID再调用启动接口触发补数随后轮询执行状态直到终态。这里有个实践细节值得分享DolphinScheduler 的 API 返回结构在不同版本之间有差异我们一开始的适配器是每个接口都直接解析响应结果升级一次就坏一次。后来改成统一的响应包装器所有返回都先转成内部的 Result 对象再提供语义化的访问方法。另一个重点是幂等性设计。AI 助手可能因为网络超时重复发送同一个指令如果每次都真的触发一个新工作流那数据就全乱了。我们在任务执行层维护了一张指令幂等表以用户的对话 ID 加工具调用 ID 作为唯一键重复请求直接返回上一次的执行状态。2.4 为什么选 DolphinScheduler 而不是自研调度聊到任务编排有一个常见的坑团队觉得给 AI 助手做任务执行需要自己写一套调度引擎结果越写越复杂。我们很早就决定不重复造轮子选型时对比了 DolphinScheduler 和 Quartel 这类的方案最终选定 DolphinScheduler。原因有三个。第一它的工作流定义是可视化且持久化的。每个工作流可以导出 JSON 定义这意味着 AI 助手可以在问答场景直接分析这些 JSON 结构来理解业务流程不需要额外做一层抽象。第二它原生支持补数、定时调度、失败重试这些我们高频使用的功能能力是现成的。第三它有完善的 API 接口虽然有些别扭但覆盖了我们需要的操作场景。自研调度引擎的诱惑在于“完全可控”但代价是开发周期长、坑多、且后续维护成本高。对我们这个场景来说调度平台是用来支撑业务流水的基层设施没有必要自己去实现。现在回头看这个决策给项目省掉了至少一个月的开发量。DolphinScheduler 自带的告警配置、超时控制、资源管理都是直接从配置项里读取不用写一行代码。3. 代码问答功能的实现细节3.1 让 AI “看懂”代码库的三层索引设计代码问答的第一步是让 AI 具备“看懂”我们代码库的能力。所谓看懂不是把整个仓库当文本扔给大模型而是有结构、有层次地组织代码信息。我们设计了三层索引来支撑这件事。第一层是文件索引。基于所有仓库的文件路径、文件类型、关键类名和方法名建立倒排索引。用户问“订单服务的 timeout 方法在哪里”能通过关键词检索快速定位到具体文件和行号。第二层是结构索引。使用语法解析器把每个文件的类、方法、字段、依赖关系抽取出来存入图数据库。这层索引解决的是关系类问题比如“OrderService 依赖了哪些类”“这个方法被谁调用了”。第三层是语义索引。把代码片段丢给嵌入模型生成向量存入向量数据库。这层索引支撑的是语义接近的检索比如用户描述“查询超时未支付的订单”匹配到的不是关键词而是语义内容。三层索引的组合策略是这样的先用关键词和结构索引做粗筛把候选文件缩小到几十个再做语义重排选中最相关的三到五个文件作为上下文喂给模型。实测下来这种组合式检索准确率远高于单独靠任何一种方式。3.2 上下文打包策略控制 Token 成本与相关性代码问答场景最大的工程挑战是上下文窗口的控制。一个微服务仓库可能有几百个文件完整贴给模型Token 成本高且回答容易跑偏。我们的做法是“宁精勿多”。候选文件选出后还会做一个重要的裁切操作。不是把整个文件丢进去而是只提取与问题高度相关的类声明、方法签名、核心逻辑段落。比如用户问工作流的启动流程就只需要贴出启动方法所在的那一段代码外加它调用的关键服务接口签名。另外我们还给每个代码片段加了“定位头”格式是“文件路径行号范围核心说明”。模型看到这个结构后回答问题时会更倾向于引用具体的代码位置方便用户回头查看准确率也有提升。这种策略在成本上的收益是实打实的。一次代码问答平均消耗的输入 Token 数控制在 3000 以内相比全仓库扫描的方式降低了 30 倍以上响应速度也明显更快。3.3 意图识别细化“问一下”还是“帮我执行”问答功能开发到一定阶段后我们发现了一个有意思的现象很多用户提问的句式其实隐藏着执行需求。比如“订单超时未支付的任务是不是挂了”这句话字面上是询问状态但用户的真实意图是想让助手查一下调度平台的工作流执行情况。又比如“昨天的数据校验跑完了吗”用户期望的不是“应该是跑完了”这种模糊回答而是拿到准确的执行结果和时间。为了处理这种情况我们在意图识别模块加了一个分类器先把输入分成“纯咨询型”和“隐含执行型”。纯咨询型直接走问答链路隐含执行型则先激活对应工具的查询能力拿到实时数据后再组织回答。这个改动对体验的提升非常明显。以前助手回答类似问题时只能给通用建议现在它会自动触发对 DolphinScheduler 的状态查询回答里直接带上工作流的真实执行状态可信度完全不同。3.4 问答效果评测内部标注集与多模型对比问答功能不能靠感觉评估必须有量化指标。我们的做法是整理了一个内部问答评测集包含 300 条高频问题覆盖代码查询、调度配置、告警归因三大类。每条问题都有参考答案判定方式是检查 AI 回答里是否包含关键信息点。比如问题“finance_daily_rebuild 工作流的失败重试次数怎么设置的”正确答案必须包含“重试次数为 3”这个数字信息回答里没有就算失败。用这个评测集跑了多轮对比我们发现上下文打包策略对准确率的影响最大直接影响得分相差接近 20 个百分点。因此每个版本迭代后第一件事就是跑一遍评测集确保没有出现回归。目前我们的代码问答综合准确率稳定在 92% 左右达到了立项时定的 90% 目标。这个数据不是终点评测集本身还在不断扩充尤其是随着代码仓库增长问题的类型也在变多。4. 任务执行与告警链路的设计4.1 从 Function Calling 到工作流触发的完整路径任务执行链路是整个项目里最有技术含量的一部分。完整路径包括意图识别、参数提取、任务落库、任务调度、结果回传五个环节每个环节都有独立的状态跟踪。以“测试环境跑一次数据校验”为例完整的过程是这样的用户发送指令后语义理解层调用工具函数 validateData(envtest, workflowNamedata_check)参数提取完成后任务会先写入数据库状态设为 pending。任务执行层的调度器每秒扫描一次 pending 任务把任务提交给 DolphinScheduler同时更新状态为 submitted。DolphinScheduler 实际触发工作流后我们通过轮询它的状态接口把任务状态更新为 running、success 或 failed。这里做的最重要设计是状态机的引入。每个任务实例都有自己的状态流转图任何环节的超时、失败、中断都有明确的状态承接逻辑。不再出现“任务不知道进行到哪一步”的模糊情况。状态机定义如下pending 表示任务已接收未调度submitted 表示已提交到调度平台running 表示调度平台已确认开始执行success/failed 是终态timeout 是异常终态。任务卡在某个状态超过预设时间会自动被巡检逻辑标记为 timeout。这个状态机的好处是排障的时候特别清晰。收到用户反馈说“任务没跑起来”只要查一下任务当前状态就能定位问题出在哪个环节。是意图识别错了还是提交失败抑或是 DolphinScheduler 那边执行超时一目了然。4.2 任务编排与并发控制防止 AI 发起风暴AI 助手有一个独特的问题用户可能会连续发送类似“把所有工作流都查一遍状态”这种指令如果助手忠实执行会产生相当多的并发请求对 DolphinScheduler 造成压力。我们在任务执行层做了一层并发控制。具体规则是单用户同时最多只能有 3 个执行中任务超出范围的请求会排队等待同一工作流 5 分钟内不允许重复触发防止 AI 对同一指令的重复执行导致任务风暴针对查询类的工具调用设置了单用户每分钟 20 次的限流阈值。这些参数不是拍脑袋定的而是基于 DolphinScheduler 的 API 承受能力和日常调用量的统计来估算的。控制住的并发量既保证了任务的及时性又不会把调度平台搞挂。上线至今这套并发控制机制帮助挡掉了至少几十次可能产生的任务风暴。4.3 失败告警的企微推送链路与消息格式设计任务执行失败后的告警触达是整个系统里业务价值最高、也最需要打磨细节的模块。告警链路是DolphinScheduler 工作流执行失败后任务执行层通过轮询状态接口感知到 failed 状态触发告警事件告警模块按照预先配置的规则组织消息内容通过企微机器人 Webhook 推送到指定群。消息格式我们前后改了三版最终沉淀出一套相对稳定的模板。告警消息包含任务名称、执行环境、失败原因摘要、相关代码文件定位、以及一个跳转链接点击可以直接进 DolphinScheduler 查看详细日志。模板设计有一条核心原则告警消息必须让收到的人 30 秒内判断出是否需要介入。所以在消息里失败原因是单独放一段的用最直白的语言描述执行环境和时间戳是紧随其后的关联信息全部折叠到点击链接里避免刷屏式的一长串日志。另外推送频率也做了控制。同一个工作流连续失败时我们不会重复轰炸同一个群而是按 15 分钟为周期聚合一次把最近 10 次失败汇总成一条消息推送。这样既保证了问题的触达也不会让群消息变成噪音。4.4 告警升级从 AI 感知失败到“人一定知道”的闭环告警推送到群里了但万一没人看呢这也是我们在设计时必须考虑的场景。我们在告警模块里做了一条升级链路任务失败后推送企微群同时设置一个 30 分钟定时器如果 30 分钟后该工作流没有出现恢复执行告警会自动升级为私聊消息推送给这个工作流的责任人再过 30 分钟仍未处理会触发第二轮升级直接电话语音通知通过企微的电话告警能力。这套升级机制对应的是一条完整的责任闭环。自动化处理能力有限最终兜底的还是人。升级链路的实现比想象中简单核心就是一张告警事件表和几个定时任务。真正有价值的设计是让“告警沦为一条群消息”这个常见事故在机制层面被规避掉。补充一个细节告警消息里我们特意标注了失败任务的所属责任人和联系方式。告警升级查找责任人的时候直接按工作流绑定的负责人配置去匹配不需要额外维护一套人员映射表。减少了配置成本也避免人员变动后告警找不到人的尴尬情况。5. 踩坑实录与问题排查技巧5.1 问题排查任务提交成功却未实际执行这个坑是上线后遇到的最头疼的问题之一任务执行层显示 submitted 成功DolphinScheduler 那边也返回了成功响应但工作流始终没有真正跑起来。排查过程是这样的先确认 DolphinScheduler 的 API 返回是否正常翻日志发现子流程的启动参数中少了一个可选字段。这个字段用于指定工作流执行的触发方式缺失时系统会走到默认路径但默认路径在我们这套环境下是不生效的。修复方案说起来简单就是补全 API 请求里的条件字段。但因为这个问题是“偶发性”的只在特定工作流类型上复现排查花了不少时间。这也给我们提了个醒对接第三方系统 API 时响应的成功不代表真正成功必须对关键返回参数做二次校验。后来我们在 API 适配器里加了一个校验规则提交成功的响应必须包含有效的执行实例 ID否则按照失败处理并重试一次。这样避免了静默失败带来的困扰。5.2 问题排查企微告警消息被限流告警推送上线初期我们遇到过一个很奇怪的现象任务确实失败了但企微群里有时收不到消息延迟还很不稳定。究其原因是企微机器人的 Webhook 有严格的频率限制我们通过同一个机器人推送的告警频率超过了它的阈值导致接口返回限流错误。但因为我们推送逻辑是异步执行的限流错误被吞掉了没有重试消息就悄悄丢了。解决思路比较直接把同一个群的所有告警推送到一个统一的缓冲队列队列按 1 秒 1 条的速率消费保证请求频率低于企微的限流阈值同时推送失败增加重试机制最多重试三次并记录最终失败的事件以便后续查询。这里有个值得分享的思维模式像推送这类“非核心链路”的组件出问题往往很难被发现因为它不影响主流程执行。要监控这类组件的健康状态必须主动注入任务级别的观察比如告警推送完成后写一条日志定时对账。5.3 问题排查AI 误判意图导致错误任务触发AI 模型的意图识别做不到百分百准确这是大家都知道的。但执行线一旦出问题影响就不是丢掉一条消息而是可能触发错误的工作流。有一次用户问“把 dev 环境的重跑任务停一下”语义理解层错误地把“重跑任务”识别成了“启动某个补数任务”差点引发测试环境的数据覆盖事故。这个案例说明AI 执行类功能必须有过硬的前置保护机制。我们最终加了三层保护。第一层是意图确认高风险的执行指令重跑、暂停、停止生产任务执行前会弹出确认消息用户必须回复确认指令才真正执行。第二层是环境隔离执行环境标签强绑定dev 环境的任务只能在 dev 调度节点上执行不允许跨环境操作。第三层是操作审计所有 AI 触发的执行操作都会记录完整的操作日志包括用户 ID、原始指令、模型判断、实际调用的 API为事后 audit 提供完整证据链。这套保护机制不是说 AI 永远判断准确而是让它的错误不至于造成不可逆的后果。对 AI 编码助手这类系统来说宁可保守也不能冒险。5.4 常见问题速查表问题现象可能原因排查思路推荐解决问答回答跑偏上下文打包缺少关键文件查看本轮检索命中的文件列表优化检索策略补充结构索引任务提交后无响应DolphinScheduler 缺少必填参数检查 API 适配器日志对比参数差异补充二次校验与自动重试告警消息丢失Webhook 被限流或无重试机制查看推送后台日志检查限流错误码增加缓冲队列与重试AI 误触发任务意图识别模块出错查看意图分类结果与实际工具调用参数前置确认 环境隔离回答延迟过高检索阶段耗时过长分析 token 消耗和候选文件数优化上下文裁剪策略任务执行卡在 pending调度器未扫描到该任务检查任务状态机和数据库索引增加巡检机制超时自动标记6. 实际效果与复盘思考6.1 灰度上线后的使用数据与真实反馈项目灰度上线三个月收集到的使用数据比预期好不少。活跃开发者覆盖了后端团队约 80% 的人员日均问题数在 200 到 300 之间执行类指令日均约 40 次高峰期来自补数和工作流检查的诉求。最有价值的数据是任务执行类指令的准确率。系统对“执行指令”的语义理解准确率达到 93%但需要区分标准语义识别和目标参数提取两个维度。单纯识别意图准确率已经不错但提取的参数比如环境、时间、工作流名称如果出错整个执行也是错的。把这两个维度合起来整体任务执行成功率约为 88%略高于立项时定的 85% 目标。开发者反馈里最受欢迎的其实是代码问答这个功能大家觉得问“某段逻辑为什么这么写哪里调用了”非常方便。任务执行类功能虽然有使用量但信任度还在慢慢建立更多人把它当成查询工具用真正敢让它直接触发工作流的人还不多。这一点倒不意外。工具链的信任需要时间积累我们的预期也是先有查询、再尝试操作。随着执行成功率的稳步提升操作类指令的比重正在逐步增长。6.2 从一个编码助手到团队基础设施进化的观察这个项目做到现在我最大的体会是AI 编码助手的终点不是“更聪明的问答机器人”而是从认知工具进化成执行代理。问答让团队的知识查询成本下降了让新同学的适应期缩短了执行让重复的排障和调度操作自动化了让操作路径和结果可审计。这两件事叠加在一起改变的不仅是研发效率更是团队协作模式。以前“问代码逻辑找老同学”“排查任务失败看日志翻半天”的高频场景现在可以在对话框里直接完成。但这不等于整套系统不需要人了。AI 决策的可靠性还达不到生产环境完全放手操作的水平我们的设计里所有高风险动作都保留人工确认环节。让 AI 负责发现和准备让人类负责确认和决策这个边界在相当长时间内依然是安全与效率的最优解。6.3 后续演进路线从单点到全局的取舍当前项目的版本能力集中在“代码库理解 调度平台操作 告警触达”这条链路上。下一阶段的规划是往更广泛的基础设施延伸——比如接入日志系统、监控指标查询、配置管理平台让 AI 在执行类指令时能获取更丰富的诊断信息。但演进会坚持一条原则每扩展一个系统必须保持“问答可理解、执行可回滚、告警可触达”三件事同时成立。如果某个平台只做问答不做执行那它只算是扩充了上下文只有执行能力也补齐才算真正将该系统纳入 AI 助手的自动化半径。扩展的方向也包括支持更丰富的任务编排能力例如多工作流联动执行。当前助手只能触发单个工作流如果需要先跑数据校验再触发下游调度还做不到。这块是明确的需求也是接下来的开发重点。机会和挑战都在这里任务编排更像是 AI 助手从“单操作工具”向“流程自动化助手”转变的关键一步。6.4 几点真实的心得与提醒最后分享几条从实际操作中总结的经验希望对同样想做类似项目的团队有帮助第一不要一开始就追求大而全的智能化。先从“回答准确”做起积累高质量的内部评测集再逐步开放执行能力。执行能力一旦放开就要做好审计、确认、回滚三件套否则迟早出事故。第二大模型的能力迭代很快但工程系统的稳定性靠的是外围机制的完备。意图识别、参数提取、状态机、幂等表、告警升级这些环节每一个都比模型本身更值得花时间打磨。第三团队的工具平台如果 API 不全或文档滞后适配器的开发量比想象中更大。这类成本要在立项时就充分考虑进去宁可多预留两周缓冲也不要做到一半发现第三方接口能力不够。这个项目做到现在最有成就感的时刻不是某个准确率指标达标了而是看到开发者在群里说“这个 AI 助手是真能帮我干活的”。从一个问答工具到任务执行助手跨过这条线并不容易但确实值得走一趟。