--- title: "基于钉钉机器人的 Qoder CLI / Claude Code 双引擎 AI 助手实践" source: wechat source_url: "https://mp.weixin.qq.com/s/UdQ7xhM25Er6Eyk0xs577w" author: 久梦 feed: 阿里云开发者 ingested: 2026-06-02 sha256: pending tags: [article, dingtalk, mcp, cli-agent, qoder, claude-code, knowledge-evolution, devops, ai-assistant] type: article --- # 基于钉钉机器人的 Qoder CLI / Claude Code 双引擎 AI 助手实践 > 闪购搜索团队 久梦 @阿里云开发者 > 原文:https://mp.weixin.qq.com/s/UdQ7xhM25Er6Eyk0xs577w ## 一、背景与问题 在闪购搜索团队的日常工作中,我们需要频繁地进行搜索问题排查、性能分析、实验管理等操作。这些操作分散在多个平台(SLS日志、TPP实验平台、代码仓库等),效率低下。 **目标**:在钉钉群里直接对话一个AI助手,它能代替人去查日志、看实验、分析性能、甚至部署代码。 **核心挑战**: | 挑战 | 具体描述 | |------|---------| | 内网部署限制 | 服务部署在内网,无法暴露公网回调地址,传统Webhook方案不可行 | | 实时性要求 | AI推理耗时较长(30s-120s),用户无法接受"发消息→等2分钟→一次性返回" | | 安全性要求 | 需要权限隔离,非管理员不能执行写操作(部署代码、修改配置等) | | 工具集成需求 | 需要访问代码仓库、日志系统、实验平台等多种外部工具 | ## 二、方案概览 **采用"钉钉 Stream + CLI 代理"方案**: | 组件 | 方案 | 选择理由 | |------|------|---------| | 消息通道 | 钉钉 Stream(WebSocket) | 内网无需公网回调地址,完全规避DNS/防火墙限制 | | AI引擎 | CLI 代理模式 | 轻量无框架依赖,复用成熟 CLI 生态 | | 流式展示 | 钉钉 AI 卡片 | 原生支持打字机效果,用户体验好 | | 进程管理 | Java ProcessBuilder | 进程级隔离,一个请求一个进程,互不影响 | | 工具扩展 | MCP(Model Context Protocol) | 标准化协议,可插拔配置驱动 | | 上下文存储 | 内存 LinkedHashMap | 自动 LRU 淘汰,无需外部存储 | ## 三、双引擎选择(Qoder CLI → Claude Code) **Qoder CLI 选型考虑**: - CLI 模式轻量无框架依赖 - 支持 MCP 工具调用 - 可执行文件部署简单(单二进制 + 配置文件) **实际使用发现问题**: - 复杂问题排查场景表现不如预期 - 答案"差点意思",需要反复调整 prompt **引入 Claude Code 后验证**: - 复杂排查能力显著提升 - MCP 兼容性更好 **双引擎并行**:通过不同的入口调用。 ## 四、Docker 部署方案 两个引擎部署在同一个 Docker 容器内,共享工作目录和 MCP 配置。 ## 五、MCP 工具集成与 OAuth 认证跳过方案 **MCP(Model Context Protocol)**:AI 调用外部工具的标准化协议。覆盖代码仓库、日志查询、实验管理。 **MCP 默认 OAuth2 流程在无头环境的困境**: - 本地开发:浏览器打开授权页面点击授权 - Docker 容器:无浏览器,无法完成交互式授权 **解决方案:预先获取 token,静态注入配置文件,跳过运行时 OAuth**: ```json [ { "serverName": "git", "accessToken": "xxx", "refreshToken": "xxx", "expiresAt": 1234567890 } ] ``` **工作原理**: - 提前在本地浏览器完成 OAuth 授权 - 拷贝 tokens 数组格式 JSON 到远端容器配置目录 - MCP 启动时读取该文件,跳过 OAuth 流程 **Token 管理要点**: - 注意 token 有效期,提前刷新 - 凭据通过 antx/diamond 注入,禁止硬编码 ## 六、关键技术细节 ### 6.1 ProcessBuilder 子进程调用 通过 Java ProcessBuilder spawn 子进程调用两个引擎。 ### 6.2 流式输出 + 进程控制 ```java try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()), 256)) { String line; while ((line = reader.readLine()) != null) { lineConsumer.accept(line); } } if (!process.waitFor(120, TimeUnit.SECONDS)) { process.destroyForcibly(); } ``` **关键设计点**: - `stdbuf -oL`:强制行缓冲,避免 Node.js 4KB 全缓冲导致的延迟 - 256字节 BufferedReader:Java 侧小缓冲确保及时输出 - 异常即杀进程:consumer 异常立即 `destroyForcibly`,停止消耗推理额度 - 进程引用暴露:支持用户发"停止"命令时主动中断 ### 6.3 AI卡片流式更新 ```java // 频率控制:累计超50字符才更新一次 if (fullContent.length() - lastUpdateLen > 50) { String content = fullContent.length() > 3000 ? fullContent.substring(0, 3000) : fullContent.toString(); cardService.streamUpdate(trackId, content, false, false); lastUpdateLen = fullContent.length(); } ``` ### 6.4 用户上下文管理(三重防护) ```java private final LinkedHashMap contextMap = new LinkedHashMap<>(16, 0.75f, true) { @Override protected boolean removeEldestEntry(Entry eldest) { return size() > 500; } }; ``` | 保护层 | 机制 | 触发条件 | 效果 | |--------|------|---------|------| | 第一层 | TTL过期 | 48小时无活动 | 清空上下文 | | 第二层 | 滑动窗口 | 单用户超200KB | FIFO删除最早对话 | | 第三层 | LRU淘汰 | 全局超500用户 | 淘汰最久未使用 | ### 6.5 权限隔离 - 管理员:完全权限(读写+命令执行+部署) - 普通用户:只读模式(系统指令最高优先级强制约束) ### 6.6 并发控制 ```java private final ExecutorService qoderExecutor = new ThreadPoolExecutor( 10, 15, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(30), new ThreadPoolExecutor.AbortPolicy() ); ``` ## 七、知识自进化机制 **五级知识沉淀模型**,让 AI 越用越聪明: ``` L0 git history(自动追踪代码变更) ↓ L1 .qoder/context/(每次任务的过程报告) ↓ L2 .qoder/memory_recent.md(最近5次会话摘要) ↓ L3 .qoder/rules/candidates/(候选规则,经验沉淀) ↓ L4 AGENTS.md + P0-constraints.md(正式规则) ``` **自动晋升**:候选规则触发 ≥3 次且成功率 ≥80%,自动提议晋升为正式规则。 ## 八、钉钉机器人配置与申请指南 ### 8.1 创建机器人应用 - 登录钉钉开放平台 - 创建企业内部应用 → 选择"机器人"类型 - 获取 appKey 和 appSecret ### 8.2 开通必要权限 | 权限名称 | 用途 | |---------|------| | 企业内机器人发送消息 | 主动回复用户消息 | | 互动卡片实例写权限 | 创建和投放AI卡片 | | AI卡片流式更新权限 | 实现打字机效果 | ### 8.3 启用 Stream 模式 - 机器人配置页 → "消息接收模式" → Stream 模式 - 无需填写回调URL - 发布到组织内 ### 8.4 AI卡片模板配置 - 进入卡片平台 - 新建卡片模板 → 场景选"消息卡片 + AI卡片" - 开启"流式组件"开关 - 记录模板ID配置到 application.properties ### 8.5 关键配置项 ```properties dingtalk.stream.enabled=true # Stream开关(预发/线上设false) dingtalk.app.key=${DINGTALK_APP_KEY} # antx注入 dingtalk.app.secret=${DINGTALK_APP_SECRET} dingtalk.robot.code=${DINGTALK_ROBOT_CODE} ``` ### 8.6 注意事项 - 多实例部署需通过环境开关控制,只在日常环境开启 Stream,否则消息会重复处理 - 凭证必须通过 antx/diamond 注入,禁止硬编码 - AI卡片需选择"AI卡片"场景并开启流式组件 ## 九、运行效果展示 - 9.1 性能追踪 - 9.2 问题场景 - 9.3 需求跟进 ## 十、踩坑经验 1. **stdbuf 行缓冲是必须的**:Node.js 进程在非 TTY 环境下默认全缓冲(4KB),不加 `stdbuf -oL` 用户会看到"卡住→突然一大段"的糟糕体验。 2. **MCP OAuth Token 格式要求**:`mcp-oauth-tokens.json` 必须是 JSON 数组格式 `[{...}]`,对象格式会导致 crash。 3. **Stream 模式多实例冲突**:多容器实例同时开启 Stream 监听会导致消息重复处理,需用环境开关精确控制。 4. **AI 卡片权限**:流式更新需单独申请"AI卡片流式更新权限",否则返回 403。 5. **Claude Code 权限透传**:Claude Code 的 `-p` 模式不会自动应用 `settings.json` 中的 permissions,需通过 `--allowedTools` 参数显式传递工具权限。 ## 十一、总结 本方案通过 钉钉 Stream + CLI 代理 的架构,实现了: - 完全内网部署:WebSocket 长连接规避公网回调 - 实时流式回复:stdbuf + AI卡片打字机效果 - 安全权限隔离:管理员/只读双模式 - MCP 工具开放:静态 Bearer token 跳过 OAuth,实现无头环境下的工具调用 - 引擎可切换:从 Qoder CLI 到 Claude Code,复杂排查能力显著提升 - 生产级稳定:线程池+超时+LRU 三层保护 以最低的侵入度、最轻的工程成本,实现了企业级 AI 助手从零到一的落地。 ## 参考链接 - [1] https://open.dingtalk.com/ - [2] https://card.dingtalk.com/