拓冰建站拓冰建站
首页 / 资讯中心 / 正文

AI代理接入飞书:以OpenClaw为例的企业助手集成实践

手头有个内部工具基于开源 AI 代理框架 OpenClaw 搭的功能不复杂读取文件、跑脚本、调模型、整理资料。问题是它一直活在命令行里除了我自己组里没人愿意碰终端。我后来花了两个晚上把它接到了飞书机器人上效果立竿见影——同事在群里 一下机器人任务就派下去了结果直接落到对话里全程不需要打开任何 IDE。这篇文章把我当时的整个集成过程、架构取舍、飞书那边怎么配、消息链路怎么打通以及踩过的那些坑完整写出来。如果你正在做企业内部的 AI 助手或者想把一个开源 Agent 框架接到飞书上这应该能省你不少事。1. 项目背景与整体思路1.1 为什么选择这个组合AI 助手在企业里落地最大的瓶颈从来不是模型能力而是入口。你把一个 Agent 做得再强如果调用方式还是ssh 到服务器敲命令那它就只是极少数人的玩具。飞书这类办公协作工具天然就是员工每天停留时间最长的地方把 AI 助手放进飞书群、单聊、工作台等于把能力直接送到业务人员面前零学习成本。选 OpenClaw 的原因也很实际。它是一个开源的 AI 代理运行时核心能力是让大模型连接到本地命令执行、文件系统、脚本调用和外部工具并且自带技能Skill机制可以按需扩展企业内部的运维脚本和 API。更重要的两点一是模型层可替换云端模型、本地模型都能接适配不同数据合规要求二是它的授权策略把高风险操作兜在了一道人工审批的闸门后面这对于企业内使用是关键前提。1.2 可选的三种接入方案我在设计集成方案时先列了三种接入路线逐一对比后才确定最终架构。第一种是飞书开放平台直接配置回调地址让 OpenClaw 自带一个 HTTP 服务来接收飞书事件。缺点很明显OpenClaw 本身并不面向 Webhook 场景设计把飞书的验签、解密、重试逻辑塞进去耦合太深而且每次升级 OpenClaw 都可能要重新适配。第二种是中间加一层桥接服务由桥接服务统一对接飞书开放平台收到用户消息后转换成 OpenClaw 命令行任务执行再把结果通过飞书 API 回传。这套方案的好处是边界清晰飞书侧逻辑和 Agent 侧逻辑互不干扰出了问题可以单独排查还能在不影响 AI 代理的情况下调整消息格式。第三种是直接在飞书机器人里调用 OpenClaw 的远程 API如果未来 OpenClaw 版本提供稳定的服务端 API这是最干净的方式。但当时我用的版本还没有成熟的操作接口所以没有选它。如果你用的是更新版本可以在落地前先确认有没有官方 HTTP API有的话建议直接走这条路线。1.3 最终架构和数据流我敲定的结构是这样的飞书客户端 ↓ 用户发消息 飞书开放平台事件订阅 ↓ HTTPS 回调 桥接服务Python/FastAPI ↓ 组装 Prompt / 解析指令调用 OpenClaw CLI OpenClaw Agent ↓ 读文件/执行命令/调用模型 返回结果 → 桥接服务 → 飞书 API → 群聊/单聊回复飞书侧我把机器人应用成了审核并代为执行的角色桥接服务收到消息后先判断发送者是否在白名单内再决定是直接交给 OpenClaw还是先走一层审批。这个看起来多出来的中间层后来被证明是整套系统里最值得投资的部分。2. 先把 OpenClaw 跑起来2.1 安装方式对比OpenClaw 的官方文档提供了几种安装方式我用下来最顺的是命令行安装脚本。注意不同版本、不同操作系统的安装方式会有差异建议以你拿到版本对应的文档为准。我当时在 Ubuntu 22.04 服务器上操作安装命令大概长这样curl -sSL https://get.openclaw.sh | bash除了联网安装OpenClaw 还提供便携包适合内网环境离线部署。如果你所在的企业网络无法访问外网安装脚本便携包会更友好——把整个运行时目录拷贝到服务器上配上环境变量直接启动。这一点在部署到生产环境时尤其重要因为很多企业的服务器访问公网是受限的尤其是不能访问一些特殊域名的时候联网安装脚本可能跑不完。安装完成后建议先跑一下版本号确认装好了openclaw --version2.2 模型配置换模型其实是换一个 keyOpenClaw 默认配置里会写一个默认模型但实际使用中几乎都要改成你自己的模型。它支持两种接入方式一类是 OpenAI 兼容的 HTTP API另一类是本地推理服务比如 Ollama、NVIDIA NIM 这类。我在配置里把模型调整成了兼容 OpenAI 格式的内网模型地址配置项大概是这样model: provider: openai-compatible base_url: http://192.168.1.10:8000/v1 api_key: sk-xxxx model_name: qwen2.5-72b-instruct temperature: 0.3 max_tokens: 4096如果你用的是本地模型base_url 换成 Ollama 的默认地址即可model: provider: ollama base_url: http://127.0.0.1:11434 model_name: llama3.1:8b选云端模型还是本地模型核心要看数据敏感度。企业内部对话内容十有八九不能出内网所以我在生产环境用的是内网部署的模型服务。在测试阶段用云端模型没什么问题但一旦涉及真实业务数据请务必想清楚数据流向。这个决定越早做越好省得后面返工。2.3 工作目录与命令审批OpenClaw 会在用户目录下生成一个.openclaw文件夹里面包含配置文件、工作区workspace和执行审批记录。它的设计思路是Agent 可以自主完成很多事但在执行 shell 命令之前会先检查命令是否在允许列表中如果不在会弹出一个确认请求。在服务器上无交互运行时这个确认请求会卡死任务所以需要提前配置。审批文件路径通常是~/.openclaw/exec-approvals.json里面可以放允许自动执行的命令规则{ allow: [ ls, cat, pwd, find, python3 /opt/scripts/*.py, git status ], allow_prefix: [ python3 /srv/agent/tasks/ ], deny: [ rm -rf /, mkfs.* ] }这里我踩过一个很有意思的坑因为放在 cron 里跑我没法人工点确认一开始任务总是静默失败。后来查了日志才发现是审批弹窗一直挂着。把常用命令加进 allow 列表后任务就顺了。但同时我刻意保证exec-approvals.json只允许白名单内的操作因为一旦 Agent 可以被飞书群里任何人触发它手上的命令权限就等于群聊权限这里必须收敛。3. 飞书开放平台侧准备3.1 创建自建应用去飞书开放平台后台创建企业自建应用这一步不需要写代码但每一步都要仔细。创建完成后你会拿到App ID和App Secret两个关键凭证。接着在应用能力里启用机器人能力。飞书机器人本质上是应用的一个入口启用后你才能把应用加到群聊或让员工在单聊里找到它。我当时创建应用时容易忽略的是应用发布环节。自建应用如果在开发状态下只有应用管理员和自己能看到没法被普通员工搜索到。要让组员都能用需要创建版本并发布由企业管理员审核。最好提前跟管理员打好招呼因为审核环节经常被卡上一两天。3.2 权限范围与事件订阅机器人要接收用户发来的消息必须开通对应的权限和事件。权限方面我开通的是这两个核心权限im:message读取群聊和单聊中的消息也可用im:message.receive_v1范围的细化权限im:message:send_as_bot以机器人身份发送消息事件订阅方面需要添加接收消息im.message.receive_v1事件。添加事件后飞书才会在有人 机器人或给机器人发私聊时把消息内容推送到你配置的回调地址。这里有一个关键点首次配置事件订阅时飞书会要求你填一个回调地址并做验证。机器人应用没有加密时验证过程比较简单如果开启了加密飞书会推送一个加密的 challenge 数据你必须在回调中解密后原样返回答数字段验证才会通过。我在 3.4 节会给一套能直接用的解密示例。3.3 回调地址和验证模式的选择飞书事件订阅支持两种模式长连接和 Webhook 回调。长连接模式适合内网开发环境不用暴露公网地址Webhook 回调则是公网可访问域名加 HTTPS。我在内网落地时优先用了长连接模式因为省去了暴露端口的麻烦但如果你的桥接服务和飞书之间还要经过网关Webhook 更容易排查链路。长连接模式下飞书开放平台会提供一个 SDK让本地服务跟飞书服务器保持一个 WebSocket 长连接。消息到达后由事件监听回调触发你的代码。这种模式下不需要处理公网回调签名和加解密对初次上手的团队友好很多。但要注意长连接只适用于事件接收消息回传仍然要调用飞书开放平台的 API不能省掉 access_token 的获取逻辑。4. 桥接服务实现让消息变成任务4.1 目录结构和依赖桥接服务我用了 Python FastAPI 来实现。选择 FastAPI 的原因很简单异步性能足够、自带 OpenAPI 文档方便调试、代码量少。目录结构这样安排feishu-bridge/ ├── app.py # FastAPI 入口 ├── openclaw_runner.py # OpenClaw CLI 调用封装 ├── feishu_client.py # 飞书 API 客户端 ├── config.py # 配置项 ├── templates/ │ ├── system_prompt.txt # 给 Agent 的指令 │ └── ... └── logs/依赖只有几个小库pip install fastapi uvicorn requests pycryptodome4.2 消息接收与解密逻辑应用启用加密后飞书推送到回调地址的数据体是经过 AES 加密的。消息格式大约是{ encrypt: 随机加密字符串 }需要先解密拿到真正的 JSON 事件。使用 AES-256-CBC 加解密key 是应用的Encrypt Key取前 32 字节IV 也是 key 的前 16 字节解密前要做 Base64 decode。核心逻辑参考如下import base64 import hashlib import json from Crypto.Cipher import AES def decrypt_feishu_payload(encrypt_str: str, encrypt_key: str) - dict: key encrypt_key.encode()[:32] iv key[:16] cipher AES.new(key, AES.MODE_CBC, iv) decrypted cipher.decrypt(base64.b64decode(encrypt_str)) # PKCS5Padding 去除 pad_len decrypted[-1] plaintext decrypted[:-pad_len] return json.loads(plaintext)解密后你会得到事件结构{ schema: 2.0, header: { event_id: xxxx, event_type: im.message.receive_v1, create_time: 1700000000000, token: xxx }, event: { sender: { sender_id: { open_id: ou_xxx, union_id: on_xxx, user_id: xxxx }, sender_type: user }, message: { message_id: om_xxxx, message_type: text, content: {\text\:\帮我统计上个月的发布记录\}, chat_id: oc_xxxx, chat_type: p2p } } }接收事件后第一件事不是处理逻辑而是做消息去重。飞书事件是至少一次投递不处理幂等同一个消息你可能收到很多遍。我当时的做法是拿event_id做 Redis 去重没有 Redis 也可以用本地 SQLite 记录处理过的消息 ID简单可靠。4.3 把消息文本转成任务飞书推送来的message.content是字符串里面嵌套了 JSON需要先解析出来拿到文本。拿到的文本一般是这样的机器人 帮我查一下本周的线上事故总结一下这个文档里的事项这里要注意如果机器人在群里飞书推送的文本可能包含_user_123这类占位符要用正则把它去掉只保留真实指令。import json import re def parse_message_content(content: str) - str: try: content_dict json.loads(content) text content_dict.get(text, ) except json.JSONDecodeError: text content # 去除 占位符 text re.sub(r_user_\w, , text).strip() return text解析完成后桥接服务要决定如何执行。我最初的版本是让模型自己理解指令直接把文本传给 OpenClaw 的处理入口。后来发现效果不够稳定用户说得很随意Agent 有时候会做很多多余的事。于是我加了一层指令归类前缀任务的开头加上系统提示告诉 Agent 当前用户身份、所在群组、可用工具范围明确只做用户要求的事不要扩展执行标记安全等级如果指令涉及删除文件、修改线上配置、给外部发请求需要二次确认把上下文喂给 OpenClaw 之前我用了类似这样的提示你是企业内部的 AI 助手运行在 OpenClaw 中。 当前用户张三工号 1001 用户所在群运维值班群 指令统计这个 IP 的登录失败次数 要求只返回最终统计结果和简要说明不要输出执行过程的中间信息。4.4 OpenClaw 执行与结果回传桥接服务调用 OpenClaw 命令行把任务喂给它并等待执行结束。我封装成一个独立的 runner用subprocess来做import subprocess def run_openclaw_task(task_text: str, timeout: int 180) - str: proc subprocess.run( [openclaw, run, --task, task_text], capture_outputTrue, textTrue, timeouttimeout, cwd/srv/openclaw-workspace ) if proc.returncode ! 0: return f[执行失败] {proc.stderr} return proc.stdout这里有个很现实的问题OpenClaw 处理任务通常是秒级到分钟级而飞书机器人要求收到用户消息后的回调要在短时间内响应否则飞书会重试或者报超时。我最初天真地直接把任务执行放在回调函数里执行结果经常触发飞书重试同一条指令被执行了多次。解决方案是异步化回调接口只负责接收消息、写入任务队列、立即返回一个空 JSON 给飞书后台 worker 从队列里取任务调用 OpenClaw等执行完再通过飞书 API 回传。队列可以就用内存队列但为了服务重启不丢任务后来我换成了 Redis 队列。核心伪代码from fastapi import FastAPI, Request import asyncio app FastAPI() async def handle_message(event): task_text parse_message_content(event[message][content]) chat_id event[message][chat_id] # 异步队列或 Redis 队列 await task_queue.put({chat_id: chat_id, task: task_text}) app.post(/feishu/event) async def feishu_event(request: Request): body await request.json() if encrypt in body: body decrypt_feishu_payload(body[encrypt], ENCRYPT_KEY) # 处理 URL 验证请求 if body.get(type) url_verification: return {challenge: body[challenge]} event_type body.get(header, {}).get(event_type) if event_type im.message.receive_v1: asyncio.create_task(handle_message(body[event])) return {code: 0}后台 worker 回传消息时普通文本直接发送如果 Agent 返回大段 Markdown我就用 post 消息接口的msg_typetext或interactive卡片卡片对长文本更友好。飞书接口调用示例import requests def send_text(chat_id: str, text: str, token: str): url https://open.feishu.cn/open-apis/im/v1/messages headers { Authorization: fBearer {token}, Content-Type: application/json } payload { receive_id: chat_id, msg_type: text, content: json.dumps({text: text}, ensure_asciiFalse) } # 请注意 receive_id_type 需要根据 receive_id 类型设置 resp requests.post(url, headersheaders, params{receive_id_type: chat_id}, jsonpayload) return resp.json()关于 access_token需要通过应用凭证换取并且要缓存。飞书的tenant_access_token有效期通常为 2 小时频繁获取会被限流。我封装了一个带缓存的客户端过期自动刷新逻辑不复杂但很重要。import time import requests class FeishuClient: def __init__(self, app_id, app_secret): self.app_id app_id self.app_secret app_secret self.token None self.expires_at 0 def get_token(self) - str: if self.token and time.time() self.expires_at - 60: return self.token resp requests.post( https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal, json{app_id: self.app_id, app_secret: self.app_secret} ).json() self.token resp[tenant_access_token] self.expires_at time.time() resp[expire] return self.token4.5 命令行直接调用的坑我最开始调用 OpenClaw 时用的命令是openclaw run --task 帮我分析一下日志文件后来发现不同版本可能支持子命令不太一样有的版本需要先启动交互式会话再往 stdin 写入内容。如果你遇到 agent failed before reply: unknown model: xxx 这类报错大概率不是调用方式问题而是配置里的模型名没对上。这类问题在 6.2 节展开说。另外一定要设置超时。一个任务如果让模型自由发挥理论上它能跑到天荒地老几分钟不返回很常见。我设了 180 秒超时超时后直接杀掉子进程回传抱歉任务执行超时请简化指令或拆分后再试。业务人员听到这句话会比干等五分钟好受得多。5. 企业级细节身份、权限与观测5.1 识别群聊中的真实用户飞书机器人被拉进群后任何群成员 它都能触发任务。这里必须解决的问题是Agent 执行完任务后返回的消息要能定位到是谁发起的。飞书事件里的sender.sender_id.open_id是用户在当前应用下的唯一身份拿这个值可以反查员工信息但我在实际设计中更倾向于把这个值直接透传给 OpenClaw 的上下文让 Agent 在回复时带上。例如桥接服务构造任务文本时会拼上用户 OpenIDou_xxxx 群 ChatIDoc_xxxx这样在最终响应里可以礼貌地 提问用户。如果要通过飞书 API 实现 某个用户需要知道用户的 open_id 并构建富文本消息比纯文本多一步解析工作。如果你一开始追求简单让 Agent 在文案里带上对方称呼也可以但请务必别把工号 姓名明文放进 Agent 上下文里到处打印注意最小化敏感信息。5.2 执行权限的白名单设计这套系统最值得反复强调的就是权限收敛。飞书群里的消息能触发 OpenClaw而 OpenClaw 能操作服务器文件、执行命令如果不对用户和指令做限制相当于把一个能执行命令的后门挂在群聊里。在集成时我设置了下面几道闸第一道用户白名单。只有特定部门的同事或特定的群才能触发任务。判断依据是open_id或chat_id白名单之外的消息直接忽略或者回复一句抱歉你没有使用权限。第二道指令白名单。对于一些固定格式的任务比如查监控、查排班、查发布记录我让桥接服务直接匹配关键词并映射到指定的脚本不经过 OpenClaw 的自由推理。自由推理看似强大但业务人员无意的模糊描述可能导致 Agent 做了超出预期的事。第三道OpenClaw 内置审批。所有 shell 命令必须经过exec-approvals.json白名单校验不在白名单命令会挂起等待人工确认而不自动执行。三层下来出问题的概率会小很多。我单位里有人试过让机器人把服务器上的所有 mysql 数据库删掉命令直接被审批策略拦截了这就是那道闸门的价值。5.3 日志、审计与可观测性企业内部用的工具最容易被忽视的就是日志。AI 助手出问题以后模型胡说八道、命令执行错误、权限误判如果没有任何日志排查起来会非常痛苦。我在桥接服务里加了请求日志每条消息处理过程记录如下信息用户 open_id、昵称所在群 chat_id原始消息内容脱敏生成给 OpenClaw 的任务文本执行耗时返回码和返回内容截断使用的模型日志写到 JSON 文件或者标准输出方便接入日志收集系统。OpenClaw 自身的日志也需要保留因为很多错误信息尤其是模型调用失败只会出现在 OpenClaw 的运行日志里而不一定出现在桥接服务侧。另外建议给回复里加上一个任务编号比如[T-20250315-001]这样用户在群里说刚才的任务结果好像不对时你可以根据编号快速定位到日志里那一条完整链路。6. 常见问题与排查实录6.1 问题速查表现象可能原因排查方法飞书机器人完全不回复事件订阅未配置、回调地址不通、事件类型没订阅查看飞书开放平台的事件投递记录用 curl 手动模拟推送首次配置验证不通过加密模式没有正确解密 challenge检查 Encrypt Key 是否和应用一致确认解密逻辑用了 AES-CBC收到消息但任务没执行用户或群不在白名单看桥接服务日志确认是否被白名单拦截OpenClaw 执行卡住命令不在审批白名单弹确认框没人点把允许命令加进 exec-approvals.json或检查审批策略报agent failed before reply: unknown model模型名配置和实际模型不一致检查 OpenClaw 配置文件里的 model_name确认与实际模型服务匹配OpenClaw 结果返回到了群聊但格式错乱直接复制 Agent 原始输出没有做格式转换统一使用纯文本摘要或飞书 Markdown 卡片飞书重复执行同一指令事件重试导致没有做幂等用 message_id 或 event_id 做去重6.2 我印象最深的三次排障第一次是发布机器人后没人能找到它。查了半天发现应用没发布还在开发中状态只有我能搜到。企业自建应用要经过创建版本 - 申请发布 - 管理员审核流程这一步不完成所有配置都是白搭。第二次是群聊里 机器人没有反应但私聊可以。后来才发现我在事件订阅里只配置了接收单聊消息没有添加群聊消息的事件。飞书对不同聊天场景会分发不同事件群聊里 机器人需要的事件和单聊是独立的要分别开通。第三次比较隐蔽OpenClaw 执行耗时超过飞书回调等待时间飞书认为回调失败就重试了结果用户一条指令被重复执行了三遍。我最终通过把消息接收和任务执行异步解耦解决接收消息后立即返回 200任务丢进后台队列慢慢跑跑完再主动调 API 回传。这个改动之后重复执行的坑就再也没出现过。还有一个经验可以分享用事件订阅的调试工具而非直接 机器人来调试。飞书开放平台提供事件调试台可以手动构造事件体推送到你的回调地址。我用它来验证桥接服务是否正常比跑到群里手动发消息高效得多而且不会污染真实用户的消息记录。6.3 保持这套系统长期稳定运行的建议AI Agent 插到企业协作软件里本质上是一个带执行力的自动化工件稳定运行需要持续关注。建议每周做一次模型输出的抽查确保 Agent 没有在一周的使用中慢慢学坏——特别是如果模型升级或技能库更新后原有行为很可能出现偏移。其次OpenClaw 的技能Skill和模型配置可能更新频繁不要随意在生产环境升级。升级前先在测试环境跑通回归用例比如准备固定的几条典型任务查一下某目录下最近的文件统计某接口的调用量等升级后逐条跑一遍确认没有回归再切换。最后留意飞书侧的权限变更。企业应用如果被管理员调整了权限范围可能导致消息收不到或发送失败。建议保留一份权限清单和最近变更记录避免问题发生后无从追溯。我个人在实际集成中的体会是OpenClaw 和飞书本质上都在解决同一个问题——把人的意图变成可执行的行动只是前者靠近执行端后者靠近交互端。两者之间那层桥接服务的价值不在于代码写了多漂亮而在于它强制你思考了身份认证、权限控制、超时重试、消息幂等这些不性感但救命的细节。后续如果你要在里面加日程查询、审批流触发、知识库检索结构和思路是一样的飞书侧继续收敛权限OpenClaw 侧继续扩展技能中间做好灰度和审计。这套底座打好了往里面填场景只是时间问题。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门