前半年我一直在捣鼓一个叫羲和XiheAgent的 AI 编码助手目标很简单让模型不只是蹲在对话框里给代码建议而是能真的把任务接走、跑起来甚至在失败时通过企业微信把我喊醒。今天把整个设计思路和落地过程写下来包括怎么做代码问答、怎么接上 DolphinScheduler 执行调度任务以及任务执行失败后企微告警这条链路。这个项目适合正在做 AI Coding 工具、内部开发者平台或者想把大模型落地到真实工程流的团队参考哪怕你只是一个人维护仓库也能从里面抄走不少设计。1. 为什么我要自己做“羲和”从代码问答到任务执行的痛点1.1 代码问答工具聊得爽但动不了手我用过不少 AI 编程助手最开始的感觉是“真香”但用久了就发现一个问题绝大多数工具都停留在代码问答层面。你问它“这段代码为什么这么写”它能回答得头头是道你问它“把这个接口改成异步”它也能给你一段修改建议。可问题是它不会替你把代码改了、把测试跑了、把流水线触发了。对个人小项目来说改完复制粘贴还能接受但在团队协作里对话完还得人工去定位文件、修改代码、提交、再跑去 CI 触发构建这一套链路下来效率根本没提升多少。真正让我决定动手做羲和的是一次具体需求。同事提了一个“把订单模块的接口从 HTTP 改成 gRPC”这不是一句“建议”能解决的它需要模型理解仓库结构、定位涉及哪些文件、修改 protobuf / handler / client、跑单测、再触发构建。传统问答工具给不出这种闭环能力它没有执行权限也没有和我们的工作流打通只能“纸上谈兵”。所以我开始思考AI 编码助手能不能往前再走一步从回答问题变成执行任务。1.2 任务执行到底难在哪把“问答”升级到“执行”不是加一个终端模拟器那么简单。我拆了一下至少要解决五个问题。首先语义理解要跟得上。用户说“把同步脚本跑一下”和说“每天凌晨同步数据”是两种完全不同的任务一个是一次性执行一个是周期调度。模型要把自然语言转成明确的可执行计划。其次需要工具调用能力。模型得能调文件读写、命令行、Git、包管理这些基础工具不能只靠生成一段代码。第三权限和安全是硬门槛。一个 AI 助手要是能被提示词诱导去执行rm -rf /那它在生产环境里根本不敢用。必须限制它能碰什么、不能碰什么。第四任务编排很复杂。工程里很少有“跑一条命令”这么简单的任务更多是多步骤、有依赖、有超时、有重试。比如“同步数据”可能要先拉上游接口、再清洗、再写入数仓每一步都有失败的可能。第五结果反馈要可靠。执行成功还是失败卡住了还是超时了这些状态要让人第一时间知道。如果失败没有通知那自动化就变成了“无声事故”。这五个问题每一个都踩过坑也直接决定了我对羲和架构的选择。1.3 羲和的定位与设计原则羲和不是又一个大而全的通用聊天助手它的定位很明确团队内部的 AI 编码助手核心能力是从代码问答延伸到受控任务执行。名字“羲和”是神话里的太阳御者取“驾驭代码运行”的意思让大模型负责理解和规划让执行框架负责安全地驾车。设计上我给自己定了四个原则最小权限默认只读需要写操作时单独授权可观测所有任务状态都有记录失败能追溯可编排任务与现有调度系统DolphinScheduler对接而不是自研一套调度渐进式先做好代码问答再做受控执行最后再接入任务调度。这套原则在后续落地中帮了大忙尤其是“渐进式”这一步让我没有被一开始的复杂需求吓退。2. 羲和的整体架构与核心设计思路2.1 Agent内核任务规划与工具调用的解耦羲和的整体架构分三层交互层、内核层、执行层。交互层负责和用户对话管理上下文内核层是 Agent 循环负责理解用户意图、拆解任务计划、调用工具执行层是真正跑命令、改文件、调用外部 API 的地方。Agent 内核我采用了 ReAct 风格流程很简单思考 - 调用工具 - 观察结果 - 再思考直到任务完成或达到终止条件。但这里有个非常关键的抽象工具调用不是让模型直接执行命令而是让模型输出一个结构化的 JSON 指令。比如{ tool: bash, command: pwd, timeout: 10 }然后由执行层来校验、执行这段指令。这样模型始终碰不到真实的权限边界执行层可以加白名单、沙箱、超时这些控制。这个设计让我后来省了太多心因为模型可以随意“胡思乱想”但真正落地动作全在可控的框架里。2.2 知识库管道让模型真正看懂仓库要让模型做代码问答第一个要解决的是“上下文装不下”的问题。一个中型仓库可能有上万个文件全塞进 Prompt 是不可能的。我采用了 RAG 方案核心是四个环节仓库扫描按目录树扫描过滤掉二进制文件、node_modules、target这些依赖目录代码分块对源码文件做结构化切分按函数、类、方法切分保留文件路径和语言类型向量化用 Embedding 模型把代码块转成向量存进向量库混合检索查询时同时用关键词 BM25 和向量检索再做重排序。这套管道不只是给问答用的。任务执行同样需要“这个文件在哪、这个函数是干什么的”它和问答共用同一套索引只是下游用途不同。所以我把知识库管道做成了独立服务两边都对接它。2.3 执行引擎沙箱、权限和可观测性执行引擎是羲和和普通问答工具最大的区别。我用 Docker 沙箱模式每个任务一个独立容器容器内默认只挂载仓库的只读副本工作目录是临时层需要写文件时再以写模式挂载特定目录。容器超时默认 120 秒CPU 和内存限额固定防止一个“野脚本”把整个服务拖垮。权限模型也花了很大功夫。命令白名单git、go、python、npm等、危险参数拦截rm -rf /、curl | sh这类、路径白名单只能操作仓库内路径。所有执行的命令、输入输出都写入结构化日志并且通过接口暴露状态查询。这样不管是人工排查还是自动告警都能拿得到现场信息。2.4 对外集成DolphinScheduler与企微通知的设计任务执行不能只停留在“帮你在终端里跑一条命令”。工程里很多任务是周期性的需要进入正式的调度体系。团队里已经用 DolphinScheduler 做调度所以我不该重复造轮子而是把“自然语言生成的工作流”翻译成 DolphinScheduler 的 DAG。举个例子用户说“每天凌晨2点同步订单数据到数仓失败给我发企微”。羲和要解析出几件事调度周期是每天 2:00任务节点是同步脚本依赖关系是什么失败时通知哪个渠道。然后调用 DolphinScheduler 的 Open API 创建或更新 Workflow并把失败告警和企微渠道关联起来。DolphinScheduler 本身支持企业微信 Webhook 告警但“内置告警”的消息太粗糙只告诉你“这个任务实例失败了”不会告诉你为什么失败。所以我另外做了一个事件监听DolphinScheduler 任务状态变化时通过 Webhook 回调到羲和再把执行日志里的关键错误提取出来组装成一条更容易人工判断的企微消息。这条链路就是“dolphinscheduler 执行调度任务任务执行失败企微进行告警”的完整实现。3. 代码问答模块从索引到语义检索的落地细节3.1 仓库索引与分片策略索引的第一步是“仓库概览”。我使用 tree-sitter 对 Java、Go、Python、TypeScript 等主流语言做语法解析提取顶层符号比如 Python 的 class、defGo 的 func、typeJava 的 class、method。然后生成一个仓库地图相当于给模型一份“这个仓库里都有什么”的目录。然后再对每个源码文件做分片。这一步最关键的是“按代码结构切”而不是按字符数硬切。我按函数、类、方法作为最小代码块保留片段所属的文件路径、语言、符号名这些元信息。对代码问答来说文件路径本身就是答案非常关键的一部分。比如用户问“用户登录逻辑在哪”模型回答“auth/login.go的LoginHandler”比单纯贴一段代码更有用。索引不是建一次就完了。我还写了基于 Git 事件的增量更新文件提交后自动触发相关代码块重新 embed避免仓库变了但索引还是旧数据。离线全量构建加增量更新这套组合能保持索引基本实时。3.2 Embedding与重排序的选型Embedding 模型我对比了不少国产开源的有 bge-m3、text2vec 这些也有商业 API 的 Embedding。实践下来bge-m3 在中文代码混合场景效果不错性价比也高。但我很快发现一个现象向量检索对“精确符号名”并不可靠。你搜GetUserById向量排序可能给你一堆GetUserByName看着像却不对。所以必须做混合检索BM25 关键词检索负责精确匹配向量检索负责语义召回两者结果合并后再用 Cross-Encoder 做重排序。这一步收益非常明显Top5 准确率能从 70% 左右提升到 90% 上下。对代码问答来说Top5 准确率是决定性指标因为模型拿不到正确片段后面生成什么都可能是错的。3.3 问答上下文组装与Prompt工程问答时我先用意图识别判断用户是想“直接对话”还是“代码搜索”。代码搜索就把 query 转成搜索关键词走检索管道如果是“这段代码什么意思”这种也要检索相关片段才能让模型有据可依。上下文组装顺序是我调了很多版才定下来的。先放“仓库地图摘要”再放“相关代码片段”最后放“用户问题”。每个片段前必须标注【来源文件】让模型知道它引用的代码在哪里。这一步能显著降低幻觉因为模型不再凭空回答而是基于给定的代码片段推理。Prompt 里我加了很硬的约束只能基于提供的上下文回答如果信息不足必须明确说“仓库中没有找到相关实现”而不是自己编一个 API。这个约束配合本文提到的引用标注把幻觉率压到了可接受范围。3.4 一个直白的代码问答流程示例假设用户问“订单模块的支付回调在哪个文件它校验签名的方式是什么”流程是这样意图识别判断为代码搜索提取关键词“订单、支付、回调、签名校验”。搜索先用 BM25 召回再用向量召回合并后做 rerank。组装从仓库地图找到 order 相关目录加入 Top5 片段生成上下文。生成模型定位到internal/handler/pay_callback.go说明verifySign的具体校验过程。听起来简单但真实落地里每一步都要修。比如 query 扩展直接拿原始问题去 embedding 效果很差。我先让大模型把自然语言问题转成一组检索关键词再拿去搜索。query 理解和结果重排序这两步是我花时间最多、收益最大的部分强烈建议优先打磨。4. 任务执行模块从意图到调度任务落地的完整链路4.1 任务意图识别与指令Schema任务执行模块的起点和问答完全不同。用户说“把订单同步脚本跑一下”和说“每次订单数据更新后自动重跑同步任务”这两个意图一个是一次性执行一个是事件触发。所以羲和要先做任务意图识别把用户输入转换成标准化的指令。我定义了一个指令 Schema核心字段包括trigger可以是 “immediate”立即执行、cron 表达式周期执行或者 “event”事件触发action要执行的工具链比如 bash 脚本、Python 脚本、Git 操作params执行参数像命令内容、文件路径、环境变量notify失败或成功时通知的渠道depends_on任务依赖的其他任务用于生成 DAG 边。模型的输出要严格限定成这个 JSON Schema然后我再用 Schema 校验工具检查。一旦校验失败就让 LLM 重新生成一次绝不对不合法指令硬执行。这一步让我避免了无数潜在的坑。4.2 执行器与DolphinScheduler任务映射对于一次性任务执行器直接在沙箱容器里跑就够了。但对于周期任务或者编排任务需要映射到 DolphinScheduler。映射逻辑并不复杂关键是要有模板。我在羲和内部维护了一套任务模板库每个模板对应 DolphinScheduler 的一种任务类型Shell 任务、Python 任务、SQL 任务等。生成流程解析用户指令中的触发条件转成 cron 表达式把 action 部分映射成 Shell 或 Python 脚本如果指令里带了“依赖上一任务”就给 DAG 添加对应依赖边调用 DolphinScheduler 的 Open API 创建 Workflow。最大的坑在 DolphinScheduler 的 API 兼容性上。不同版本 API 路径和请求体差别不小我用的 3.2.1 版本创建流程是POST /projects/{projectCode}/process-definition需要先拿到projectCode再组装一个特别死板的定义 JSON。建议第一次接入时先用 API 调试工具手动把创建流程跑通再让模型去生成对应 JSON不要直接指望模型一次写对。4.3 失败检测与企业微信告警的联动调度任务执行失败后DolphinScheduler 自带告警确实能发消息但内容太干瘪就一句“工作流实例失败”运营和研发看到一头雾水。所以我在外层做了一个增强DolphinScheduler 的任务实例状态发生变更时通过 Webhook 回调到羲和羲和拿到失败的任务实例 ID再主动去拉执行日志提取错误关键字组装成可读告警消息。企业微信告警我采用的是 MarkdownMessage 格式比纯文本强很多任务名订单同步失败节点sync_orders.sh错误摘要exit code 127, command not found追溯链接执行日志地址把消息发到群机器人 Webhook团队群里能快速看到问题并且能顺着链接点进日志而不是看到一个干巴巴的“失败”。4.4 守卫措施重试、超时、人工确认自动化执行最怕误操作守卫措施必须设计到位。我做了四层超时控制所有沙箱执行都有硬超时默认 120 秒防止模型或者脚本卡在某个命令里。重试策略对可重试的失败网络抖动、依赖下载失败等自动重试最多 2 次使用指数退避间隔。人工确认危险操作比如发布到生产环境、清空表数据必须经过人工确认。羲和生成“待确认”任务推送通知给用户用户确认后才真正执行。审计留存每个任务的输入输出、模型生成计划、实际操作记录全部保存并支持导出。这些守卫措施看起来繁琐但它们是“AI 助手敢不敢上生产”的唯一答案。5. 实操过程与核心实现片段5.1 我用的开发环境和技术栈分享下我实际使用的配置仅供参考开发语言Python 3.11 FastAPILLM 调用通过团队网关调用大模型 API支持流式输出本地测试时也跑过 Qwen 量化版模型做代码问答实验向量库Milvus存储大量历史仓库索引小规模 Demo 我用过 SQLite 简单向量检索沙箱Docker SDK for Python镜像用python:3.11-slim openjdk-17兼顾多语言调度DolphinScheduler 3.2.1队列Redis Stream 简单 Worker用来异步执行任务这套技术栈不需要多高配的服务器单机也能跑通。核心是要把模型输出和执行器解耦架构清晰比炫技重要得多。5.2 关键代码片段任务注册与执行上下文我写个简化版的任务执行伪代码重点看“模型只产生 JSON执行器负责校验并执行”这个原则class TaskExecutor: def execute(self, instruction: dict, context: dict): tool instruction[tool] if tool not in self.allowed_tools: raise PermissionDenied(f{tool} not allowed) path instruction.get(path, ) if not self.in_sandbox_path(path): raise PermissionDenied(path out of sandbox) cmd instruction[command] if self.is_risk_command(cmd): raise PermissionDenied(risk command rejected) result self.run_in_container( cmd, timeoutinstruction.get(timeout, 120), workdirpath ) return self.format_result(result)这段代码看起来非常简单但它挡住了大部分隐患。命令白名单、路径校验、风险命令识别每一步失败都会直接中断执行。关键是还要把拒绝原因写进审计日志而不是简单抛个异常。5.3 关键配置DolphinScheduler的告警通道在 DolphinScheduler 里配置企业微信告警我踩过不少坑。现在稳定使用的配置流程是这样的在“安全中心 - 告警组管理”新增一个告警组类型选“企业微信”填写群机器人 Webhook 地址不同群用不同机器人告警组和项目/工作流关联在需要告警的工作流定义里配置“失败”事件发送到该告警组另外在羲和侧配置同一个 Webhook用于发送深度失败详情。这里特别提醒一点Webhook 地址一定要用群机器人地址如果企业微信自建应用机器人需要走应用消息接口要配置corpid、agentid、secret复杂度高很多。建议先手动用 curl 发一条消息确认网络通再接入 DolphinScheduler不然很容易出现“文档没问题但消息发不出去”的玄学问题。5.4 本地跑通一个端到端示例的步骤一个最容易复现的端到端流程如下准备一个示例仓库放一个 Python 脚本sync_order.py故意留一个未定义变量错误在羲和 Web 界面提问“运行 sync_order.py 并告诉我结果”羲和识别意图立即执行 bash命令是python sync_order.py执行器校验命令合法在沙箱容器中运行退出码 1返回 stderr羲和提取到错误“NameError: name x is not defined”整理结果给用户接着配置周期任务“每天5点执行该脚本失败企微告警”羲和调用 DolphinScheduler 创建 Workflow并绑定告警组。我自己本地跑通这套流程不到 20 分钟。出问题最多的不是大模型反而是 DolphinScheduler 的 API 鉴权和企微地址配置。所以如果你也想复现建议先手动把这些外部依赖调通再折腾 Agent 逻辑。6. 常见问题与排查技巧实录6.1 问答环节检索不准、上下文溢出、幻觉严重检索不准先看 rerank 结果。如果 Top5 不对优先调分块粒度和 Embedding 模型。代码块按函数切分后如果函数太长就继续按语句块拆如果函数之间频繁引用公共变量可以额外把 import 信息也带进片段。单纯加大 TopK 不解决问题反而会把无关内容塞进来。上下文溢出代码问答里最常见。我限制每个问题最多拼 10 个片段每个片段不超过 400 token超过就裁剪优先保留用户直接提到的文件。实在装不下就启用“摘要模式”先让模型把大量片段总结成一段再基于总结回答。幻觉严重除了在 Prompt 里限制“只能基于上下文回答”我还会要求模型在回答时标注引用文件路径。如果检索到的内容和问题不相关模型必须主动说“仓库中没有找到相关实现”。这个做法比单纯靠 Prompt 承诺更靠谱因为模型有了“引用责任”的意识。6.2 执行环节权限越界、命令注入、任务悬挂权限越界最典型的错误是模型想操作仓库之外的文件比如修改/etc/hosts。路径白名单能拦住大部分但模型有时会用符号链接绕过。所以沙箱容器里要禁用mount和symlink权限避免逃逸。命令注入用户可能在自然语言里夹带恶意变量比如“运行 echo $HOME rm -rf 某个目录”。要做两层防御一是输入消毒把命令里以;、、|连接的部分拆开分析二是容器内用非 root 用户运行并且不安装包管理器写权限。这样即使模型“疯了”破坏范围也会被限制住。任务悬挂Python subprocess 没设超时或者容器内进程在等待输入都会造成任务悬挂。我在容器启动时加--init进程并用timeout命令包裹实际执行的命令确保进程树能被完整杀掉。这个“杀进程树”的操作很重要否则 kill 掉的只是父进程子进程还在跑。6.3 调度环节状态不同步、重复执行、时区问题状态不同步羲和创建 Workflow 后如果 DolphinScheduler 实例重启回调状态可能丢失。我最终用数据库表记录“羲和任务 ID”和“DolphinScheduler 工作流 ID”的映射并通过定时任务轮询任务状态兜底确保不丢状态。重复执行用户说“每天2点跑”DolphinScheduler 会按 cron 调度。但模型生成的 cron 表达式如果和派发时间有偏差可能出现“创建后立即执行一次”的意外。我的解决办法是在创建 Workflow 前把“下一次执行时间”预览给用户确认确认后再落地。时区问题DolphinScheduler 默认用中国时区但如果服务器系统时区不对cron 可能差 8 小时。我统一在创建参数里显式指定时区比如Asia/Shanghai避免环境差异。这个坑很隐蔽排查了大半天才发现是时区问题。6.4 企微告警限流、字段缺失、告警风暴企微机器人限流是每分钟 20 条。如果一次调度有几十个任务同时失败告警消息会直接被堵死。我加了一个聚合器同一任务在 30 分钟内只告警一次并把失败次数合并到消息里。这样既能知道有问题又不会被信息轰炸。字段缺失DolphinScheduler 回调的 payload 里有时只有任务实例 ID没有任务名。如果不在羲和侧维护 ID 到任务名的映射告警消息就会变成“任务 ID 12345 失败”团队根本看不懂。这个映射表要提前建好。告警风暴除了聚合我还设置了“静默期”配置。凌晨 2 点到 6 点告警只发到值班群而不是全员群白天再发到项目群。这种细节非常影响团队对告警系统的信任度宁可“少而准”不要“多而杂”。6.5 我的几条“血泪”经验第一不要把执行逻辑写死在模型 Prompt 里一定要让模型输出结构化指令由外部执行器执行。模型负责出主意系统负责做决定这个边界不能模糊。第二先做“只读问答”再渐进放开写权限。安全边界怎么强调都不过分先验证了执行引擎能挡住恶意行为再考虑开放更多能力。第三DolphinScheduler 的 API 文档更新不及时用之前一定先抓包看真实请求。被我坑过多次官方文档写的不一定对以实际请求体为准。第四告警不是越多越好。要让人看得懂、找得到根因最好附带日志链接。如果一条告警消息没法指导人解决问题那它就是噪音。第五日志就是生命。模型决策、执行动作、校验结果全部要记录。否则一旦执行出错你找不到是模型的问题还是执行器的问题那就只能甩锅给 LLM这种事绝对不能发生。7. 后续扩展与个人体会7.1 后续可以做的方向羲和目前已经能在问答和执行之间走通闭环但还有很多可以扩展的地方。比如支持更多调度系统Airflow 也能作为映射目标支持更多告警渠道钉钉、飞书、短信都可以接只要加适配器就行。再比如把“代码修改”能力做深。现在是问答加执行但更进一步的“生成 PR”能力值得探索模型修改代码、跑测试、生成 commit、创建 Pull Request整个过程待在受控流水线里。当然这一步对代码评审、测试覆盖率、权限控制的要求会高很多可以按团队承受能力逐步放开。还有一个方向是“项目级知识库”不只是单仓库还能把多个服务的文档、接口定义、事故记录都索引起来。AI 助手不只是“懂代码”还能“懂业务背景”回答质量会再上一个台阶。7.2 个人体会做这个项目最大的体会是AI 编码助手的价值在于“敢执行”但“敢执行”不等于“乱执行”。代码问答做得再好也只是第一步真正让团队效率提升的是把问答和任务执行串起来模型负责理解和规划系统负责安全落地人来做最终决策。如果你也想做类似的工具建议从自己的真实痛点出发不要一上来就堆各种大模型能力。先跑通一条最简单的链路问答、检索、执行、告警再逐步加功能。这条链路里每一个环节都有很多细节可以打磨但最少能让你看到一个 AI 助手真正“干活”的样子而不是只在对话框里陪你聊代码。
阅读完成 · 觉得有帮助?