OpenClaw 对接钉钉实战:从单向 Webhook 到双向 Stream 机器人

发布时间:2026/10/8 19:02:33

OpenClaw 对接钉钉实战:从单向 Webhook 到双向 Stream 机器人 把 AI 接进钉钉这件事我前后折腾了三个周末最后发现真正的坑基本都集中在钉钉开放平台那一层权限配错、加签算法算错、机器人发了消息群里却看不到。OpenClaw 本身反而是最省心的部分。如果你也想在钉钉群里养一个“AI 同事”让它定时发日报、回答群成员提问、把模型算出来的结论推成卡片这篇教程就是给最初期的你准备的。我会先讲清楚 OpenClaw 负责什么、钉钉负责什么再按两条真实链路走一遍最后把最容易踩的坑整理成速查表。整个过程不需要你会 Java懂一点 Python、能把 YAML 配置明白就够了。1. 先把 OpenClaw 和钉钉的角色分清楚1.1 OpenClaw 是一个“技能调度中枢”最近在技术社区看到不少人在搜“OpenClaw 部署”“Ollama 部署 OpenClaw”它本质上是一个开源智能体执行平台。你可以把它理解成一个中控台给它一个任务它会按照已经定义好的 Skill技能列表去拆解执行——需要查数据库就去调数据库接口需要推理就去调用模型需要发消息就去请求外部应用。模型层可以接云端 API也可以接本地通过 Ollama 部署的开源模型所以网上才会有“OpenClaw 只能用接入 API 的方式使用算力吗”这种讨论答案是可以混着用本地模型省钱云端模型省心。那么 OpenClaw 和钉钉对接以后是什么场景简单说就是把钉钉群当成智能体的交互入口把群里的消息当成触发事件让 OpenClaw 里的技能在收到消息或定时任务后跑起来。没有钉钉这一环OpenClaw 就是一台只会埋头计算的服务只能在命令行里手动触发业务方根本用不上。接上钉钉之后任何会用聊天软件的人都能直接跟它交互这才是落地。1.2 钉钉开放平台实际上给了我们三条通道对接钉钉最容易懵的就是通道太多。我建议先记住三类自定义群机器人 Webhook有一个 URL 就能往里推消息单向输出适合告警、日报、通知。企业内部应用机器人正经的双向通信方式能接收群消息、回复消息、查用户信息适合做互动问答。Stream 长连接事件订阅钉钉把消息事件主动推送到你的客户端不需要公网 IP也不需要在服务器上配 HTTPS 证书。这三类不是互斥关系。想做到全双工通常就是企业应用机器人加上 Stream 模式一起用。为了更直观我把它整理成了下面的对照表。通道通信方向配置难度典型用途自定义群机器人 Webhook单向推送低定时日报、监控告警、通知广播企业应用机器人 Stream 模式双向收发中群里 机器人问答、任务触发企业应用机器人 HTTP 回调双向收发中高需要公网回调地址的团队1.3 对接完成后能得到什么我实际跑起来的两个场景可以给你参考。第一个是每天早上 9 点OpenClaw 去数据库拉取前一天的订单量调用本地模型做一段总结生成 Markdown 卡片推到项目群里整个过程全自动。第二个是群成员在群里 机器人问“昨天华东区的销售额多少”OpenClaw 识别意图、查数据、把结果回复到当前会话。前者只需要 Webhook 单向推送后者才需要双向链路。把这两个场景在写代码之前想清楚能帮你判断自己到底需要复杂方案还是简单方案——这是我最想强调的一点不要在需求都没分清的时候就去把所有权限都申请一遍。2. 动手前必须准备好的三样东西2.1 钉钉侧的账号和组织权限能不能创建企业内部应用直接取决于你是否是钉钉组织的管理员或子管理员。很多新手卡在第一关登录钉钉开放平台以后发现根本没有“企业内部应用”的入口十有八九是权限不够。我实测下来的流程是用管理员账号登录 open.dingtalk.com在「应用开发」菜单下找到「企业内部应用」然后点击创建。如果页面提示没有权限那就得先联系管理员让管理员帮你开通应用开发者权限。这里有个小建议尽量申请一个独立的子管理员账号来操作开放平台不要直接拿超级管理员日常登录。原因很简单超级管理员的权限范围太大一旦 AppSecret 泄露攻击面会很吓人。2.2 OpenClaw 侧先跑通一个最小环境OpenClaw 的部署方式会因为版本不同略有差异但逻辑是一致的。我在确认它是否准备好对接钉钉时只看三件事确认 OpenClaw 服务进程已经跑起来用一个最简单的 Skillecho 技能手动触发一次确认能正常返回结果确认 OpenClaw 能访问模型服务比如本地 Ollama 的 API 端口或者远程模型接口。这三步里任何一步挂掉都不要急着去碰钉钉。否则后面出了问题你会分不清到底是模型的问题、OpenClaw 的问题还是钉钉的问题排查成本会翻倍。如果你是在 ARM 小主机这类低功耗设备上部署 OpenClaw模型选择建议用小体量模型并且做一次“预热”先把模型加载进内存。不然群里第一次 机器人时响应时间可能长达十几秒体验很差。2.3 准备一个测试群和常用工具不管做推送还是做双向对话都强烈建议单独建一个测试群把机器人拉进去避免在生产群里反复发送调试消息打扰同事。测试群两三个人就够了自己在里面随便发消息错误日志不尴尬。工具方面需要一个 Python 3.8 以上的环境最好能用虚拟环境隔离依赖。会用到的库主要是 requests 和钉钉官方的 dingtalk-stream。顺便说一句不要把密钥直接写在代码里我习惯用.env文件加载环境变量后面在 OpenClaw 的配置里引用来引用去也方便。3. 钉钉侧配置教学两个通道分别怎么开3.1 先走通最简单的 Webhook 推送通道把 Webhook 当成陪练链路最短能最快确认钉钉侧配置没问题。在测试群里添加自定义机器人的入口是群设置 - 智能群助手 - 添加机器人 - 自定义机器人。填写机器人名称之后安全设置强烈建议选“加签”。为什么不是自定义关键词因为关键词会对消息内容做限制比如我推送的 Markdown 内容里必须有“通知”两字否则消息会被拒收这对内容自由度太不友好。IP 白名单则对动态 IP 环境不友好家里和公司 IP 一变就废。加签的算法官方文档写得很清楚但网上贴的代码版本参差不齐。我直接给你贴一份实测可用的import time import hmac import hashlib import base64 import urllib.parse def dingtalk_sign(secret: str): timestamp str(round(time.time() * 1000)) string_to_sign f{timestamp}\n{secret} hmac_code hmac.new( secret.encode(utf-8), string_to_sign.encode(utf-8), digestmodhashlib.sha256 ).digest() sign urllib.parse.quote_plus(base64.b64encode(hmac_code)) return timestamp, sign这里有几个细节特别容易踩坑我一个个说。第一timestamp 必须是毫秒级字符串不是秒如果你用time.time()直接转字符串结果只有一个签名永远对不上。第二官方签名算法里是“时间戳 换行符 secret”换行符别漏了。第三sign 拼接的时候是在 Webhook URL 后面加timestampxxxsignyyy不是把参数放到请求体里。还有一个隐蔽问题不同语言的 Base64 结果在 URL 编码上处理不一致如果你发现签名偶尔能过偶尔报错看一下 sign 里是不是有号变成了空格。Python 的quote_plus处理是正确的。3.2 要双向通信就开企业内部应用机器人单向推送搞定以后再做双向通信会顺畅很多。在企业应用后台配置机器人的消息接收模式时我推荐选 Stream 模式。原因是 Stream 模式不需要公网回调地址、不需要配置 HTTPS 证书钉钉服务器会主动向你的客户端建立一条长连接把消息事件推过来。这特别适合本地开发或内网部署的环境。操作的路径一般是开发管理 - 消息接收模式 - Stream 模式。选完以后页面会生成新的 Client ID 和 Client Secret。注意这里生成的 Client Secret 和创建应用时的 AppSecret 不是同一个东西。我见过好几个人拿 AppSecret 去填 Stream 的 ClientSecret结果就是连不上。这也是我反复强调要仔细看配置页的原因。配置完 Stream 模式后把机器人发布到组织然后拉到测试群里发一条 消息。如果 Stream 连接正常你会在客户端日志里看到一条事件推送看到的那一刻基本就可以确定链路通了。3.3 权限点申请一次给到能干活就够了钉钉开放平台的权限体系很考验耐心一上来就把所有权限全勾上既没必要也有安全隐患。做 OpenClaw 对接初期我们实际上只需要这几个权限点用途是否必须成员信息读取拿到发送人 userid 和昵称做权限校验时建议开机器人发送群消息机器人主动/被动在群里说话必须Stream 消息接收接收群消息事件必须卡片模板读写发送更复杂的交互卡片非必须申请完权限之后不是立刻生效的实测有时候要等一两分钟。如果你在调用接口时返回 Forbidden先检查应用是不是还在“开发中”状态其次是等权限生效后再试。把应用从“开发中”切换到“已发布”这一步很多新手会漏掉。4. OpenClaw 与钉钉连通我的实测过程4.1 从单向推送开始写一个日报推送 Skill老规矩先做最简单的。在 OpenClaw 的技能目录下创建一个 dingtalk_report 技能核心逻辑就是调用钉钉 Webhook 把文本或 Markdown 发到群里。我用的是前面那段签名代码然后封装一个简单的推送函数import requests import json def push_dingtalk_text(text: str, webhook: str, secret: str): timestamp, sign dingtalk_sign(secret) url f{webhook}timestamp{timestamp}sign{sign} payload { msgtype: markdown, markdown: { title: OpenClaw 日报, text: f### OpenClaw 自动推送\n{text} } } resp requests.post(url, jsonpayload) return resp.json()为什么选择 markdown 消息类型而不是 text因为在群里可读性好很多标题、加粗、列表都能正常显示。钉钉对 Markdown 的支持有限保留简单的标题和列表语法就够了不要整太复杂的样式。推送函数写好后手动执行一次技能确认测试群能收到消息。之后再把它接入 OpenClaw 的定时任务或者事件触发就成了正式的日报机器人。我坚持先做推送练手的原因在于双向交互要考虑消息去重、会话上下文、超时回复等问题而推送只涉及一个 HTTP 请求是链路最短的场景能最快确认钉钉侧配置全对。4.2 再做双向通信群里 机器人OpenClaw 回答双向通信的实现分四步连接 Stream、接收消息、交给 OpenClaw 处理、把结果回复到当前会话。代码骨架大概是这样的import dingtalk_stream from dingtalk_stream import AckMessage def on_message(msg: dingtalk_stream.ChatbotMessage): data msg.data content data.get(text, {}).get(content, ) content content.replace(, , 1).strip() # 把消息交给 OpenClaw 的 skill 去处理 answer openclaw_dispatch(content) # 回复到当前会话 dingtalk_reply.send(data[sessionWebhook], answer) return AckMessage.STATUS_OK, def main(): client dingtalk_stream.DingTalkStreamClient({ client_id: ..., client_secret: ... }) client.register_callback(dingtalk_stream.ChatbotMessage, on_message) client.start_forever()有几个很实际的细节。消息里的text是一个字典内容在content字段里不要直接拿data[text]当字符串处理。回复消息的方式可以直接发到sessionWebhook这个地址每个会话动态生成不需要额外权限就能发消息是最简单的回复方式。此外如果任务执行时间很长——比如要调一次本地模型跑复杂的推理整个耗时超过几秒——我建议先给群里回复一句“收到正在处理”然后异步执行任务处理完再补发结果。否则钉钉在交互模式里长时间没有应答会表现为机器人“失联”体验很糟糕。4.3 把对话记忆和权限控制考虑进去只做“问一句答一句”太浪费 OpenClaw 的能力了实际上用户会连续追问比如“昨天的数据呢”“算一下华东区的”这时候就需要跨轮次的记忆能力。我建议在技能里加一个简单的dingtalk_memory模块按conversationId 日期建一张 SQLite 表存最近 N 轮的对话摘要。每次收到新消息时把历史摘要拼进 prompt再调用模型效果会一下子提升一个档次。这条对群场景特别重要因为一个群里可能有几十人提问如果不按会话隔离上下文会乱成一锅粥。权限控制也不要忽略。像“删除数据”“修改配置”这类高危操作我默认全部禁止只有在技能入口处校验senderStaffId在白名单里时才放行。用白名单判断而不是在业务逻辑里到处写 if结构要清晰得多。4.4 上线前的自检清单每次我部署新的 OpenClaw 钉钉连接器都会过一遍这个清单几乎能堵住 90% 的问题Webhook 推送消息是否成功到达测试群加签拼接是否正确重跑一次签名算法是否稳定Stream 客户端是否保持长驻运行断开后能否自动重连长耗时任务是否先回复了“正在处理”再异步执行OpenClaw 的日志是否按“钉钉事件 - 技能处理 - 模型调用 - 回复消息”四个阶段分开打印方便排查。日志分级这一点容易被忽略。一开始我也把所有信息全混在 stdout 里出了问题根本不知道卡在哪一环。后来改成每个阶段打一个前缀例如[dingtalk]、[skill]、[model]、[reply]排查效率翻了不止一倍。5. 常见问题与排查技巧实录5.1 返回 ok 但群里没有消息这是发生频率最高的问题。钉钉接口返回{errcode:0,errmsg:ok}结果群里的消息就是看不见。大概率的原因有三个。第一自定义机器人的安全设置没通过。比如你选的是自定义关键词但推送内容里没有那个关键词消息就会被静默拒绝接口照样返回 ok。第二机器人已经被移出群或者群内停用了机器人。第三Markdown 内容触发了钉钉的内容限制这个比较隐蔽接口返回 ok但实际没有投递成功。我的排查顺序是先检查消息文本是否包含要求的关键词再检查机器人是否还群里最后看 Markdown 语法是否含有被拦截的标签。5.2 加签出现 sign 不匹配签名问题死磕过的人应该都有印象。我总结出四个高频原因timestamp 用的是秒而不是毫秒timestamp 与钉钉服务器时间差超过 1 小时钉钉会直接拒绝拼接签名时换行符丢失字符串 时间戳 \n secretBase64 编码后带号没做 URL 编码。只要这四项逐一对上sign 不匹配基本都能解决。还有一个不太起眼但很常见的坑是系统时钟不正确。如果你的服务器是物理机且没有做 NTP 时间同步时间偏差超过小时级别签名绝对验不过。5.3 Stream 连接不上或者反复断开Stream 模式连不上先检查三件事。首先是 Client ID 和 Client Secret 是否对应 stream 模式生成的那一对而不是 AppKey 和 AppSecret。其次是机器人是否已经发布上线还在开发中状态的应用无法建立有效连接。最后是重复实例问题钉钉允许同一应用建立多个 Stream 连接但会踢掉旧连接。如果你本地调试跑了一个脚本OpenClaw 服务里又跑了一个后启动的实例会把前一个挤下线表现出来就是连接反复断开。这种问题在日志里会看到 session 冲突之类的记录。5.4 OpenClaw 回复慢或者超时本地模型冷启动是最常见的原因。第一次调用时模型权重还在加载会非常慢。解决办法是提前触发一次“预热”调用把模型常驻内存。还有一个因素是配置的模型参数量对于当前设备的算力来说太大了这种情况只能换更小体量的模型或者降低生成参数中的最大 token 数。OpenClaw 侧的超时设置也要检查。默认的超时时间可能只有 30 秒甚至更短但如果模型推理本身就需要几十秒那一定是超时报错。建议把超时时间设置成任务预估耗时的两倍留足余量。下面的表是我遇到过的几个经典问题的汇总。问题现象大概率原因解决办法接口返回 ok 但群内无消息安全设置未通过、机器人被移出群检查关键词/加签配置确认机器人状态sign 不匹配毫秒/秒混淆、时间偏差、Base64 未编码校准系统时间核对签名算法Stream 反复断开ClientSecret 填错、重复实例连接确认密钥对确保只有单个实例OpenClaw 回复超时模型冷启动、超时时间过短预热模型调大超时时间权限调用返回 Forbidden应用未发布、权限未生效发布应用等待权限生效最后再分享一个我实际使用中的体会对接这类办公工具一定要先做单向推送再做双向交互每一步都能稳定跑通再进入下一步。不要着急把复杂功能全部一次接上否则出问题时你会同时面对“钉钉配置对不对”“OpenClaw 技能写没写对”“模型回答质量差”三个变量根本无从下手。等单向链路稳定之后扩展双向对话、卡片回执、审批流联动这些玩法就都是水到渠成的事了。
延伸阅读

更多相关文章

2026/10/8 18:57:33

Context Is All You Need:读千问办公CEO陈宇森2026云栖演讲

基于 2026 云栖大会技术主论坛MaaS & Agent 演讲《千问办公:Context Is All You Need》 演讲人:阿里巴巴集团副总裁、千问办公 CEO 陈宇森 视频源:Bilibili BV1K1hE6VEhq 核心论断:大模型参数狂飙的阶段过去后,企…

2026/10/8 19:52:47

非比较排序三兄弟:计数排序、桶排序、基数排序详解与C实现

排序算法里的“非比较排序三兄弟”,我愿称之为算法面试和工程项目里性价比被严重低估的一组工具。大多数人一提到排序就条件反射式地写快排,但真遇到特定形态的数据时,快排反而成了下策。这篇文章我把计数排序、桶排序、基数排序从原理到C语言…

2026/10/8 19:52:47

Docker常用命令详解:从容器生命周期到镜像与数据管理

1. Docker是什么,以及命令为什么值得系统学一遍 我先说一个大多数新手都会经历的尴尬场景:照着教程把Docker装好了,兴奋地敲下 docker run hello-world ,看到一段欢迎信息,然后……就不知道下一步该干嘛了。网上一搜…

2026/10/8 19:52:47

Pluto Filtered List鸿蒙化适配指南:从依赖预检到性能调优

最近在折腾 Flutter 业务的鸿蒙化迁移,翻了翻手上的依赖清单,大多数纯 Dart 的 UI 包都能直接跑,唯独 pluto_filtered_list 让我多留了个心眼。名字里带着“filtered list”,再加上流式数据响应,看起来就像是一个藏着…

2026/10/8 19:52:47

深拷贝与链表排序:LeetCode Hot 100 经典题的指针操作全解析

先声明一下:这两道题我在刷 LeetCode Hot 100 的时候反复遇到,后来在周赛、模拟面试里也经常能瞥见它们的影子。T138 随机链表的复制考的是你对“深拷贝”这件事的理解,以及链表中“指针映射关系”怎么处理;T148 排序链表则是把链…

2026/10/8 19:52:47

华为9006C麒麟V10SP1 LiveCD救援指南:不进系统修复与数据备份

简介:这份PDF文档面向具备一定Linux操作基础的技术人员与开发者,针对银河麒麟桌面操作系统V10SP1(华为9006C版本)进入LiveCD模式这一具体需求,给出可落地的操作指引。当用户希望在不安装系统的前提下体验或测试该系统功…

2026/10/8 19:47:46

WinForms实时绘制生命体征波形:从模拟数据到双缓冲画布实现

简介:这是一份面向C# WinForms开发者的生命体征波形绘制Demo,涵盖心律、血氧、呼吸曲线等常见监护波形的实时显示与绘制逻辑,适合需要快速实现医疗健康类界面原型或学习GDI自定义绘图的初中级开发者。压缩包共53个文件,整体仅129K…

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/8 10:03:20

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:02:17

自然数立方等于连续奇数之和:从证明到编程验证

十几年来我一直游走在数学科普和编程教学这两块内容之间,对“看起来像魔法、拆开全是数学”的结论总是格外敏感。最近翻资料时又撞见一句话:任何一个自然数 m 的立方,都可以写成 m 个连续奇数之和。2 的立方等于 3 加 5,3 的立方等…

2026/10/8 0:02:17

C#上位机SSH连接实战:用SSH.NET补齐超时、批量与密钥认证

简介:这是一份基于 C# 开发的 SSH 连接功能半成品工程,原本作为另一个主项目的子功能模块,现独立打包分享。工程采用 WinForms 界面,包含源码、解决方案、安装部署工程、NuGet 依赖包及说明文档,适合正在做远程连接、网…

2026/10/8 0:02:17

Java SpringBoot一体化智能售后系统设计与实现全解析

毕业设计年年做,Java Web 方向的题目翻来覆去就那么几个,但“一体化智能售后系统”这个题,每次看到我都觉得值得认真聊一聊。它不是一个简单 curd 堆出来的管理系统,而是把客户、工单、派单、处理、回访、统计整条链路串起来的一套…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑