☰
OpenClaw+飞书机器人:从事件订阅到工具调用的AI助手实践
2026/10/20 22:56:10 网站建设 项目流程

前两天帮一个项目组把飞书机器人整个链路重排了一遍,核心就是用 OpenClaw 当机器人的“大脑”,飞书当“前台入口”,让群聊和单聊里的那个机器人,真正能听懂你在说什么,并且能自己调用工具去干活。这篇不打算只丢个 hello world,我把从飞书开放平台建应用、配权限、写签名校验,到本地起中转服务、跟 OpenClaw 做消息联动,再到线上跑了几周后必须做的稳定性处理,一步步完整过一遍。适合已经写过一点 Python、想把 AI 能力落到真实办公场景的同学,照着抄基本能跑通。

很多人觉得“AI 机器人 = 接一个大模型 API”,实际上差得远。大模型只是“大脑”,它还需要一个能接入办公软件消息流的“身体”,还得有工具调用、会话记忆、权限控制这些配件。OpenClaw 负责把大脑和工具串起来,飞书负责把消息送到你面前,这中间还有一个轻量中转层要做。这篇就按我实际踩过的坑来讲,能让你少走不少弯路。

1. 项目拆解:机器人不是“接个模型”这么简单

1.1 这套方案到底解决什么问题

先说清楚我们要做的东西:一个部署在飞书里的机器人,用户在单聊或群聊里发一句话,机器人能理解这句话,给出靠谱的回答,还能执行一些操作,比如查资料、算数据、调接口、生成文档。它也支持多用户同时聊,每个用户有独立上下文,不会出现 A 同学问的问题被 B 同学看到的情况。

如果只是把飞书 webhook 收到的话原样转发给大模型接口,再原样把结果发回去,那当然也能响,但那个东西离“能用”还很远。办公场景需要的是:机器人能记住你们项目组的背景、能根据当前对话调整语气、能做到一定的工具调用,比如“帮我总结这周日报”就不是一句闲聊,而是要先拿日报数据、再做摘要、再返回结果。这些链路如果全自己搭,工作量非常大。OpenClaw 的价值就在这:它把大模型调用、工具执行、上下文管理、角色设定这些东西封装成了可编程的接口,我们只需要把飞书的消息事件转成一次 OpenClaw 调用,再把结果发回去就行。

另外,“专属”这个词也不是白说的。你可以给机器人定一套系统提示词,告诉它你是某个团队的助理、回答要简洁、不知道的事不要瞎编、涉密信息不要外传,也可以给它接上团队内部的知识库工具。这套配置做完以后,机器人才真正从“通用聊天”变成“干活助理”。

1.2 数据流:一条消息的三次跳跃

我习惯先把一条消息的完整路径画出来,不然写代码时会绕晕:

  1. 用户在飞书里发消息,飞书服务器通过你配置的事件订阅回调地址,把这个消息事件以 HTTP POST 形式推送到你写的中转服务。
  2. 中转服务先做验签,确认这条请求确实是飞书发的,然后解析出用户文本、用户 ID、会话 ID,再调用本地 OpenClaw 服务的命令行或接口。
  3. OpenClaw 拿到文本后,按系统提示词和会话历史去调大模型,可能还会调用工具,最终生成回答文本。
  4. 中转服务拿到 OpenClaw 返回的文本,再调用飞书的消息发送 API,把结果发给对应的用户或群聊。

注意这里有个关键区别:飞书回调是“飞书 → 你”,消息发送是“你 → 飞书”,这是两条不同的通道。很多人一开始只配了回调,以为机器人能自动回复,结果消息进来后没有出口,就是因为没搞明白这一步。回复的时候可以用回调接口直接返回一个消息结构,也可以用飞书主动发送消息的 API,我实际用下来更推荐后者,因为更可控,能处理异步长任务。

1.3 为什么选这套组合而不是其他方案

飞书在企业办公场景里覆盖很广,机器人、消息卡片、审批、文档、日程这些能力都开放了 API,而且权限体系比较成熟,可以让机器人在可控范围内访问群信息和用户身份。相比要去开发一个独立 App,直接在飞书里做机器人,省掉了大量前端工作,用户也不用学习新工具。OpenClaw 这边我更看重的是它对工具调用的处理能力,它能让模型不止会“说”,还会“做”,加上它有会话管理机制,正好补上大模型原生接口缺的那几块。

当然你也可以用别的 agent 框架或者自己硬写,但那样的话,模型请求、工具调度、错误重试、上下文截断这些全部要自己维护,成本会高出不少。这套组合的定位就是“飞书负责交互,OpenClaw 负责智能,中间代码负责黏合”。

2. 飞书开放平台配置:三步拿到机器人的“入场券”

2.1 在开发者后台创建企业自建应用

这一步不复杂,但步骤不能乱。先进入飞书开放平台的开发者后台,选择“企业自建应用”,填一个应用名称和头像。名字建议起得直白一点,比如“某某项目助理”,因为后面群聊里展示给用户的就是这个名字,太抽象的名字会影响使用意愿。

创建成功后你会拿到两个重要凭证:App ID 和 App Secret。这两个值相当于机器人的身份证和钥匙,App ID 是公开的,App Secret 一定要保密,不要提交到 Git 仓库,也不要写死在前端页面里。后续事件订阅和 API 调用都要用它们。

这里有个容易忽略的点:很多人在创建完应用后直接去配回调地址,结果发现机器人能力还没开通,或者没发布版本,导致怎么调都不对。正确顺序是先开通机器人能力、配好权限、发布正式版,最后再配事件订阅和回调地址。“发布版本”这一步很多人会漏掉,自建应用只有发布后才会真正生效,测试阶段也至少要把“可用范围”设置成包含自己的部门或全员,哪怕只有一个测试人。

2.2 开通机器人能力与权限范围

在应用能力里添加“机器人”能力,然后去权限管理里开通消息相关的权限。我用到的核心权限有两个方向:一个是接收消息事件,对应权限标识通常是im:message或im:message:receive这类只读权限;另一个是主动发送消息,对应im:message:send_as_bot。不同版本的权限标识叫法有差异,你在权限库里搜“消息”就能看到,把“接收消息”和“获取与发送单聊、群组消息”都加上即可。

权限设计上我只有一个建议:能少开就少开。比如机器人如果只需要读文本消息,就没必要开“读取用户全部信息”这类权限;如果不需要操作文档,就不要申请文档权限。这样就算 App Secret 泄露,攻击者的破坏面也小。权限开完之后,去“版本管理与发布”里提交一个新版本,等审核通过或者设定可用范围后生效。

提示:飞书权限生效有缓存,有时候你刚加了权限,立刻测试发现还是报权限不足。遇到这种情况不要急着怀疑代码,先在后台确认版本已发布,再等一分钟左右重新测试。

2.3 配置事件订阅与回调地址

这是飞书侧最核心的一步。在“事件订阅”里添加事件,选择“接收消息im.message.receive_v1”,然后填一个“请求地址 URL”。这个 URL 必须是你自己能够控制且公网能访问的地址,飞书才会把跑进来的新消息推送过去。

为什么要公网能访问?因为消息进来时,是飞书服务器主动往你这个地址发请求。如果你把服务跑在本地电脑或公司内网,飞书根本连不到,事件自然就丢了。我建议直接准备一台轻量云服务器来承接回调,域名、HTTPS 证书最好也一并弄好,省事也稳定。如果只是本地调试,可以先手动模拟飞书请求,等逻辑满足了再部署到服务器,不一定非得第一步就上公网。

回调地址保存时,飞书会立刻发送一条url_verification请求,如果你服务还没起,保存就会失败。你需要在代码里对这个请求返回一个challenge值,飞书校验通过后地址才能保存成功。后面我会贴出完整的代码实现。

另外,飞书支持明文模式、加密模式和双向 TLS 三档。日常开发建议用“加密模式”,在事件订阅页面生成一个 Encrypt Key 和 Verification Token。开启后,飞书推送的事件体会带签名,你可以校验签名防止伪装请求;如果选择了加密,请求体还会被 AES 加密,那就还得额外做解密。我下面的示例用明文模式 + 签名校验来讲,逻辑最直观,也足够覆盖大部分场景。

3. 把 OpenClaw 接进来:先让“大脑”单独跑通

3.1 OpenClaw 在这个方案里的角色

OpenClaw 是一套面向开发者的智能体运行框架,它把底层大模型接口、工具调用、会话上下文、任务循环这些都封装好了。你可以把它理解成一个跑在服务器上的“大脑进程”:通过命令行或 API 给它一句任务,它会自己规划该调用什么工具、该怎么组织回答。

实际使用中,它暴露给我最核心的能力有三个:

  • 会话管理:用同一个会话标识连续对话,它会自动维护历史,不让我自己去拼上下文。
  • 工具调用:我能预先把一些函数注册给它,比如“查天气”“读日报”“执行某段脚本”,模型需要时自己会去调用。
  • 人设设置:通过一个配置文件或启动参数,告诉它“你是什么身份、回答问题的风格、边界在哪里”。

它跟飞书没有直接关系,是独立的服务。飞书消息进来后,由中转程序决定怎么把文本转成 OpenClaw 的一次调用。正因为这种解耦,以后你想把同一套智能体能力接到企业微信、钉钉或者网页聊天框,只需要换掉前面这一层“入口适配器”,大脑完全不用动。

3.2 环境准备与一次性调通

OpenClaw 的安装方式不复杂,我按常见流程走一遍。首先准备一台 Linux 服务器或电脑,保证能访问外网,装好 Python 3.10+ 和 Node 运行时,这俩是它常见依赖的主环境。然后从官方仓库把代码拉下来,按它的安装脚本装好依赖。

装好之后最关键的一步是配置大模型服务商。你需要在它的配置文件里填上 API Key、模型名称、接口地址这些参数。第一次跑之前,先执行一条最简单的命令:

openclaw chat --message "你好,请做一个自我介绍"

如果正常情况下返回一段文本,说明模型调用没问题,OpenClaw 这个“大脑”已经醒了。这一步值得花时间跑通再往下走,因为后面所有问题排查都要先从“单测大脑”开始,否则飞书消息进来后卡在哪一段都说不清。

3.3 设计一个稳定的调用封装

由于 OpenClaw 的交互入口是命令行,我在中转服务里最直接的做法就是通过subprocess调用它。每条消息调用一次,没问题,但我要给这个调用加三层保险:

  1. 超时控制。模型回答慢的情况经常发生,我不能让 HTTP 请求无限等下去。命令行调用设置 120 秒超时,超过就标记失败并给用户返回“稍后再试”之类的提示。
  2. 会话标识。把每个用户的识别 ID 作为会话参数传给 OpenClaw,这样每个用户天然有独立上下文。
  3. 错误捕获。OpenClaw 可能返回非零退出码,或者输出含大量日志,我要把标准输出和标准错误分开收集,只把正常的回答文本提取出来。

核心封装长这样:

import subprocess def chat_with_openclaw(text, session_key): cmd = [ "openclaw", "chat", "--message", text, "--session", session_key, "--plain", # 尽量返回纯文本结果 ] try: result = subprocess.run( cmd, capture_output=True, text=True, timeout=120, ) except subprocess.TimeoutExpired: return "抱歉,处理超时了,请稍后再试。" if result.returncode != 0: return "大脑开小差了,暂时没法回答。" output = result.stdout.strip() if not output: return "收到了,但我这边没有生成有效内容。" return output

实际生产环境里我还会额外加一个并发控制,同一时刻只允许同一会话跑一个任务,防止用户连续发多条消息把模型服务打爆。这个后面在进阶部分展开。

3.4 把对话调成你想要的味道

“专属”两个字,主要靠系统提示词来实现。OpenClaw 配置文件里通常会有一个角色设定字段,我在项目里给机器人写的提示词大概是这样的:

你是某项目组的 AI 助理。回答要保持简洁,优先用列表呈现关键信息。遇到你不确定的知识,不要编造。涉及内部数据时,先明确说明你使用的是哪个数据源。回复时使用中文。

这段提示词看着不起眼,实际效果非常明显。不加提示词时,模型会默认用一段“作为一个人工智能模型……”这种废话开头;加了之后,回答会直接进入正题,更像一个职场助理。还可以在提示词里禁止它输出代码块以外的 Markdown 复杂格式,因为飞书文本消息对部分 Markdown 渲染有限,发送之前最好把内容统一换成纯文本或最简单的格式。

4. 手写中转服务:接住飞书回调,答完题再发出去

4.1 Webhook 服务的骨架

中转服务我用 Python 的 FastAPI 来写,运行在 8080 端口,对外开放一个POST /webhook/feishu路由。FastAPI 的异步支持和参数校验都不错,代码量少、逻辑清晰,非常适合这种胶水服务。

import json import time import hmac import hashlib import base64 import requests from fastapi import FastAPI, Request, Response app = FastAPI() APP_ID = "cli_xxxxxx" APP_SECRET = "xxxxxxxxxxxxxxxx" ENCRYPT_KEY = "xxxxxxxxxxxxxxxx" # 事件订阅里的加密密钥 # 简单的会话锁,防止同一个人并发多次请求 session_locks = {}

启动命令很简单,本地直接uvicorn main:app --host 0.0.0.0 --port 8080。部署到服务器时我建议跑在 Caddy 或 Nginx 后面,把公网 HTTPS 流量反向到这台服务的 8080。

4.2 验签与 url_verification

飞书在事件订阅保存地址时,会先发一个验证请求,请求体里带有type: "url_verification"和challenge字段。我们的服务收到后,要把challenge原样返回,飞书确认“这个地址确实可用”,保存才会成功。

验证签名时,飞书请求头里会带着X-Lark-Request-Timestamp、X-Lark-Request-Nonce、X-Lark-Signature三个参数。签名的计算方式是用加密密钥对“时间戳 + 随机值 + 原始请求体”做 HMAC-SHA256,再进行 Base64 编码,最后和X-Lark-Signature对比。这里有个细节:一定要用请求的原始字节去计算,不能先用json.loads再重新json.dumps,因为格式一变,签名就对不上了。

def verify_signature(timestamp: str, nonce: str, body: bytes, sign: str) -> bool: string_to_sign = f"{timestamp}{nonce}{body.decode('utf-8')}" digest = hmac.new( ENCRYPT_KEY.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha256, ).digest() expected = base64.b64encode(digest).decode("utf-8") return hmac.compare_digest(expected, sign)

路由里先验签,再判断事件类型:

@app.post("/webhook/feishu") async def feishu_webhook(req: Request): raw_body = await req.body() ts = req.headers.get("X-Lark-Request-Timestamp", "") nonce = req.headers.get("X-Lark-Request-Nonce", "") sign = req.headers.get("X-Lark-Signature", "") if not verify_signature(ts, nonce, raw_body, sign): return Response(content="invalid signature", status_code=403) payload = json.loads(raw_body) if payload.get("type") == "url_verification": return {"challenge": payload["challenge"]} if payload.get("type") == "event_callback": # 事件处理放后台,避免阻塞飞书重试 event = payload.get("event", {}) from fastapi.concurrency import run_in_threadpool run_in_threadpool(handle_event, event) return {"code": 0, "msg": "success"}

注意:飞书接口对回调有超时要求,你必须在很短时间内返回响应。因此费时的 OpenClaw 调用绝对不能放在同步请求里等,否则飞书会判定失败并重试,导致消息重复处理。这里我先用线程池把事件丢到后台,后面进阶部分再讲更稳的队列方案。

4.3 消息事件的解析

飞书推送的im.message.receive_v1事件里,核心字段在event.message和event.sender。我只需要提取用户发来的纯文本,以及发送者的open_id。open_id是用户的身份标识,在同一企业内稳定不变,适合用来作为会话 ID。

def handle_event(event: dict): message = event.get("message", {}) message_id = message.get("message_id") message_type = message.get("message_type", "") if message_type != "text": return content = json.loads(message.get("content", "{}")) text = content.get("text", "").strip().replace("@_user_1", "").strip() sender = event.get("sender", {}) open_id = sender.get("sender_id", {}).get("open_id", "") if not text or not open_id: return # 同一个消息ID只处理一次 if message_id in processed_ids: return processed_ids.add(message_id) answer = chat_with_openclaw(text, session_key=open_id) send_feishu_text(open_id, answer)

这里有两个容易被新手踩的坑。第一,飞书群聊里用户发送的消息如果是@机器人形式的,文本里可能带有占位信息,我在解析时把@_user_1这样的占位符清掉,避免把“@机器人”这种内容也当作问题的一部分传给模型,不然回答会莫名其妙。第二,消息去重一定不能省,因为飞书在没收到快速响应时按它的策略会重试推送,消息 ID 去重可以保证同一条消息不会被重复消费。

群聊场景还想区分“谁在问”的话,可以在提示词里保留用户昵称,或者用sender_id作为身份来源。但最基础的单聊场景,用open_id做会话隔离就够了。

4.4 获取 token 并主动发送消息

前面说过,回复这条路要走飞书主动发送消息 API。调用它之前要先用 App ID 和 App Secret 换取一个临时令牌,叫tenant_access_token。这个令牌有效期约两小时,不能每次发消息都去换一次,要在代码里做好缓存,快过期再刷新。

_token_cache = {"token": "", "expire_at": 0} def get_tenant_token() -> str: now = time.time() if _token_cache["token"] and _token_cache["expire_at"] > now + 60: return _token_cache["token"] resp = requests.post( "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", json={"app_id": APP_ID, "app_secret": APP_SECRET}, timeout=10, ) data = resp.json() token = data.get("tenant_access_token", "") _token_cache["token"] = token _token_cache["expire_at"] = now + data.get("expire", 7200) - 60 return token

发送文本消息时,请求地址是im/v1/messages,必须带上查询参数receive_id_type=open_id,请求体里的receive_id填入用户的open_id,msg_type = "text",content是一个 JSON 字符串,里面再放text字段。这个两层层层套的格式容易写错,我贴一下完整代码:

def send_feishu_text(open_id: str, text: str): token = get_tenant_token() payload = { "receive_id": open_id, "msg_type": "text", "content": json.dumps({"text": text}, ensure_ascii=False), } resp = requests.post( "https://open.feishu.cn/open-apis/im/v1/messages", params={"receive_id_type": "open_id"}, headers={"Authorization": f"Bearer {token}"}, json=payload, timeout=15, ) data = resp.json() if data.get("code") != 0: # 记录日志,最好能带上错误码 print("send failed", data)

顺带说一句,飞书还支持“卡片消息”,可以做得更漂亮,比如让机器人回答时带按钮、带标题、带折叠区。但卡片消息的 JSON 结构复杂度明显高,对纯文本场景没有必要。先把文本消息跑通,再考虑升级卡片。

5. 联调实录:从本地模拟到真实飞书消息

5.1 先把本地服务跑起来,模拟验证回调

代码写完后,先不着急部署,在本地起服务,然后用 curl 模拟一次飞书的url_verification请求。这一步可以避免把服务部署到公网后才发现问题。

因为要验签,我需要在本地生成一个合法签名。可以写一个临时 Python 脚本,把你要发送的 body 原文、时间戳、随机值按签名算法算出来,然后把签名放进 curl 请求头里:

curl -X POST 'http://127.0.0.1:8080/webhook/feishu' \ -H 'Content-Type: application/json' \ -H 'X-Lark-Request-Timestamp: 1710000000' \ -H 'X-Lark-Request-Nonce: abc123' \ -H 'X-Lark-Signature: 生成的签名值' \ -d '{"type":"url_verification","challenge":"challenge-abc-123"}'

如果能正确返回{"challenge": "challenge-abc-123"},就说明验签和入口路由没问题。然后再模拟一条真实消息事件,注意要把message_type设置成text,content设置成 JSON 字符串。

curl -X POST 'http://127.0.0.1:8080/webhook/feishu' \ -H 'Content-Type: application/json' \ -H 'X-Lark-Request-Timestamp: 1710000001' \ -H 'X-Lark-Request-Nonce: abc124' \ -H 'X-Lark-Signature: 生成的签名值' \ -d '{ "type": "event_callback", "event": { "message": { "message_id": "mock_msg_001", "message_type": "text", "content": "{\"text\":\"你好\"}" }, "sender": { "sender_id": {"open_id": "ou_mock001"} } } }'

模拟事件后,观察中转服务日志,看 OpenClaw 有没有接收到消息,以及有没有调用发送 API。这时候可能发现open_id是假的,发送 API 会报错,没关系,重点先看链路前半段是否通。

5.2 部署到公网服务器

本地模拟没问题后,把代码和依赖同步到云服务器,启动服务。为了保证事件订阅保存时飞书能访问,我把服务挂在 nginx 后面,为域名申请了 HTTPS 证书,然后把飞书后台的请求地址 URL 填成https://你的域名/webhook/feishu。

保存事件订阅这个动作是立刻的,如果保存失败,九成是下面几种情况:

  • 公网地址访问不到服务,比如防火墙没放行端口、域名没解析过来。
  • 证书有问题,飞书拒绝连接。
  • 服务里验签逻辑没跑通,返回 403。

保存成功后,后台一般会显示“订阅成功”。接着把机器人拉进一个测试群,或者在单聊里找到这个机器人,先发一条“你好”,整个链路就开始真实跑水了。

5.3 真实飞书消息调试的观察点

真实消息进来时,我习惯在日志里打印四个关键信息:请求头里的时间戳、事件里的message_id、用户open_id、以及文本内容。只要这四个对得上,链路就基本成立。如果消息没进来,就先回飞书后台看“事件订阅”里的投递记录,那里能直接看到飞书是否真的发了请求、请求是否超时、响应码是多少。

一次完整的成功日志看起来应该是这样的:

[2025-01-10 10:00:01] receive event: message_id=om_xxx, open_id=ou_xxx, text=你好 [2025-01-10 10:00:02] openclaw result: 你好,我是你的项目助理,有什么可以帮你? [2025-01-10 10:00:02] send success: code=0

我建议在代码里把日志写得结构化一点,至少包含event、track_id、timestamp,这样排查问题时能快速定位到一条消息全生命周期。

5.4 常见故障速查表

这部分直接给结论,都是我实际跑下来遇到的坑,整理成一个速查表,按需查阅:

现象原因处理方法
事件订阅保存失败服务没启动、验签失败、地址不可达先本地模拟验证,再检查公网连通性
消息事件推不过来未订阅im.message.receive_v1或未发布版本后台确认事件已添加,且应用版本已发布
机器人不在群里/无法 @机器人未在群内或应用可用范围不包含该群把机器人拉进群,检查应用可用范围
发送消息报 401token 获取失败或权限不足检查 App Secret,确认send_as_bot权限
发送消息报 400 参数错content不是合法 JSON 字符串确认content是json.dumps之后的字符串
回调超时收到重复消息处理时间过长,飞书重试推送改造为异步处理,并用message_id去重
OpenClaw 没有生成内容模型 API Key 失效或上下文超长单独测试 OpenClaw,查看完整输出日志
回答内容被截断文本长度超过飞书单条消息限制分片发送或生成摘要

提示:飞书文本消息单条有长度限制,超长内容要么截断,要么拆成多条消息发。我自己比较常用的是先让 OpenClaw 返回一个精简版,再附上“详细执行日志”之类的附件,避免大段文字刷屏。

6. 让机器人更“稳”的进阶优化

6.1 异步化:别让飞书等你的大脑

第三步我一开始用同步方式写,结果一次回答如果超过 3 秒,用户那边就可能看到机器人没反应,然后收到重复回复。后面我把处理流程改成:接住事件后,立刻返回成功回执,把真正的处理丢到后台队列里执行。队列简单一点可以用 FastAPI 的BackgroundTasks,复杂一点就引入 Redis 队列或任务系统。

异步化之后还有一个附带好处:就算 OpenClaw 崩了或者超时,飞书消息也不会丢,因为后台任务会在失败后按策略重试。我还会给每个消息维护一个简单的状态映射,消息进来先标“处理中”,处理完了再更新状态,防止重复回答。

代码如下,主要就是把handle_event丢进后台:

from fastapi import BackgroundTasks @app.post("/webhook/feishu") async def feishu_webhook(req: Request, background_tasks: BackgroundTasks): # ... 验签和挑战逻辑不变 ... background_tasks.add_task(handle_event, event) return {"code": 0}

6.2 会话隔离与上下文记忆

单聊场景天然按用户隔离,群聊场景则需要进一步细化。我在项目里用“会话 key”来区分场景:

  • 单聊:open_id作为 key。
  • 群聊:tenant_key + chat_id + open_id作为 key,这样同一个群里,多个用户各有各的上下文,不会串台。

上下文记忆如果靠 OpenClaw 自带会话机制,理论上不用自己处理。但跑久之后,上下文会越来越长,消耗模型额度,回答速度也会变慢。我后来的做法是:在提示词里要求 OpenClaw 只保留最近几轮对话,并定期清理老会话。如果你希望机器人记住用户偏好,可以把关键偏好写进一个小的配置库里,每次会话启动前注入到提示词里,这比把全部对话历史一直留着更省钱也更灵巧。

6.3 频率控制:防止机器人被刷爆

办公机器人只要上了热搜或一传开,就可能被同事当玩具连环发消息。模型 API 是按调用量计费的,如果服务没有限流,月底账单会比较难看。我做了两层频率控制:第一层,每用户每分钟最多 10 条消息;第二层,同一用户上一轮没过完不得再投递下一轮。简单实现可以维护一个字典:

from collections import defaultdict import time user_counter = defaultdict(list) def allow_user(user: str, limit=10, window=60): now = time.time() user_counter[user] = [t for t in user_counter[user] if now - t < window] if len(user_counter[user]) >= limit: return False user_counter[user].append(now) return True

这个方案够用但不适合多进程部署。如果服务水平扩展,建议把计数放到 Redis,用INCR和EXPIRE实现滑动窗口。另外,OpenClaw 调用本身也应设置并发上限,可以在调用前加一个asyncio.Semaphore或操作系统的进程池。

6.4 出参安全与敏感信息过滤

机器人一旦接入办公系统,必须考虑输出内容的安全。这不是形式主义,而是实打实的运维红线。我在中间层加了两个过滤器:

  • 输入过滤:对某些明显包含恶意指令的消息直接拒绝,不把内容传进 OpenClaw。
  • 输出过滤:OpenClaw 返回结果后,检查是否包含不应出现在聊天里的关键词,比如明文密钥、内部敏感标记,命中就拦截并替换成一句通用提示。

还要强调:日志里不要记录飞书消息的明文文本,更不要把完整正文打到控制台或日志文件。我会把文本转成 hash 或只保留前几个字,方便排查问题,又不至于泄露信息。涉及员工手机号、身份证这类信息,根本不该让机器人有能力处理,也应该在系统提示词里明确禁止。

6.5 监控、告警与长期维护

机器人跑久了你可能会发现,它偶尔会出一些很“隐性”的问题:某条消息发送失败但日志没打清楚、某个用户发的消息太长把上下文爆掉了、模型服务商偶尔限流。为了不等到同事来投诉才发现,我在服务里加了一个最简单的健康检查和指标输出:

  • 每 5 分钟主动调用一遍 OpenClaw 的本地接口,确认“大脑”还活着。
  • 用统计变量记录成功数、失败数、平均响应时长,外部监控每隔一段时间拉一次。
  • 连续失败超过 10 次就通过邮件或另外一个群机器人告警。

这一套东西不复杂,但能把一次事故的响应时间从半天压到几分钟。做生产环境长期运行,稳定性永远比功能炫酷更重要。

我自己跑下来最大的体会是,OpenClaw 和飞书这套组合的瓶颈不在“能不能打通”,而在细节处理。签名校验、异步化、去重、会话隔离、限流,这些都是表面上看不见,但线上实际一定会遇到的环节。后来我又给机器人加了一个小功能:当用户问“帮我查一下某个人是否在项目组里”时,OpenClaw 会通过预定好的接口去查成员名单再组织语言回答,而不是硬猜。这个扩展能力才是这套架构最值钱的地方——你随时可以在 OpenClaw 侧加一个工具函数,飞书侧完全不需要改代码,机器人的能力就升级了。

如果后续想继续折腾,可以顺着这几个方向走:给机器人接企业内部的文档搜索,让它能基于资料回答;把回答从纯文本升级成交互卡片;在飞书侧增加审批触发,让机器人能“发通知——等确认——干活”一条龙。把地基打稳,这些进阶玩法其实都只是在一个已经跑通的管道里再接几个新零件而已。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询