
如果你手里有一个基于 OpenClaw 这类开源智能体框架搭出来的个人助手大概率会遇到同一个问题功能做的很完整但只能留在终端里或者本地接口里平时根本想不起来用。我之前也是这个状态直到把它接到 Telegram Bot 上整个用法才真正活过来。手机、电脑、平板随时都能对话还把机器人拉进了常用群里让群里的几个人一起用。这篇文章就把完整的接入过程、群组配置和常见坑一次讲清楚按步骤走基本不会出大问题。1. 项目整体思路与接入方案选型1.1 OpenClaw 负责什么Telegram 负责什么先说清楚这件事的分工。OpenClaw 这类开源智能体项目核心价值在于把大模型对话、工具调用、知识库、任务调度这些能力聚合到一起。它本身不绑定任何聊天界面天然是一个“能力引擎”而不是一个“消息客户端”。所以要做的事情本质上是把 Telegram 收到的消息原样丢给 OpenClaw等它算完再把返回的文本包装成 Telegram 消息发回去。这个分工其实非常重要。如果你把 OpenClaw 理解成一个黑盒接入层只做“搬运工”后面换任何聊天平台都只需要改搬运层。我之前见过有人把 Telegram 消息处理和智能体逻辑全写在同一个回调里结果后面拆工具、加权限管理时非常痛苦。正确做法是写一个薄薄的适配层里面只处理 Telegram 的消息格式和会话映射业务判断尽量往 OpenClaw 那边放。Telegram Bot 本身提供了一套非常成熟的 HTTP API消息接收、发送、图片、键盘、群组权限都可以通过标准请求完成。它的生态里还有各种语言的 SDKPython 这块有 python-telegram-bot 和 aiogram 两家用的最多。我自己常年在用 python-telegram-bot生态稳定、文档全、坑也被踩的差不多了后面代码示例也基于它来写。1.2 工作模式选型为什么首选 Long PollingTelegram Bot 接收消息有两种模式Webhook 和 Long Polling。很多人一上来直接被“Webhook 才是生产环境标准”这个说法带跑偏了实际上在个人项目里Long Polling 反而是最省心的选择。Webhook 要求你的服务有一个公网可访问的 HTTPS 入口还要配置合法证书端口也基本限定在 443、80、88、8443 这么几个。这对跑在家里服务器、宿舍电脑或者局域网里的小项目来说太苛刻了。你要么去搞端口映射要么找一台带公网 IP 的机器要么折腾各种内网穿透方案每一步都是额外维护成本。Long Polling 的本质是机器人程序主动向 Telegram 服务器发起 getUpdates 请求保持连接挂着有新消息就立刻返回。不需要任何公网入口只要你的程序能访问 Telegram 的 API 就行。OpenClaw 这类项目通常就跑在自己手头的机器上本地网络能访问到 API 就完全够用了。跑出来的效果是长连接常驻消息延迟基本是秒级别个人使用、小群使用完全感觉不到差别。什么情况下再切 Webhook当你把机器人部署到一台有公网 IP 的云服务器或者需要把它嵌入到已有网站服务里共用同一个域名端口时再考虑 Webhook。前期千万别折腾直接 Long Polling 起步后面真有需要再改。2. 环境准备与前置配置从零到能收发消息2.1 基础环境与依赖安装在写任何代码之前先把环境搭好。OpenClaw 项目大多基于 Python 3.9 以上构建Telegram 接入端也建议用同一套解释器避免版本不一致带来的兼容问题。我习惯给这个接入服务单独建一个虚拟环境不污染 OpenClaw 本来的依赖。操作很简单python3 -m venv venv source venv/bin/activate激活虚拟环境之后安装两个核心依赖python-telegram-bot 和 python-dotenv。前者是 Telegram Bot SDK后者用来管理密钥配置避免把 token 写死在代码里。pip install python-telegram-bot python-dotenv装完之后确认版本python-telegram-bot 20.x 以上 API 是完全异步的写法和旧版 13.x 差别很大。网上很多老教程还在用Updater、dispatcher.add_handler那一套你直接照抄会报错。可以用这条命令检查pip show python-telegram-bot如果看到 Version 是 20.x 或更高就是新版异步 API。后面的代码我都按新版的ApplicationApplicationBuilder这套来写。2.2 创建 Bot 与获取 API Token创建机器人的入口是 Telegram 官方的机器人管理入口通常大家叫它 BotFather。私聊找到它发一条/newbot指令它会依次问你两个名字第一个是机器人显示用的名称随便起一个你能认出来的第二个是机器人的唯一用户名必须全局不重复而且结尾一定要带bot后缀比如my_openclaw_bot。创建成功之后BotFather 会返回一串形如123456789:AAExampleTokenValue的字符串这就是 API Token。所有操作 Telegram Bot API 的鉴权都靠它拿到之后千万别把这段字符串提交到公开的 Git 仓库里也不要截图发到任何群。有人会随手把 token 贴进博客代码里然后等着被扫号机器人薅羊毛这个坑我见过不止一次。推荐放环境变量。项目根目录建一个.env文件TELEGRAM_BOT_TOKEN123456789:AAExampleTokenValue然后在代码里用 dotenv 加载from dotenv import load_dotenv import os load_dotenv() TOKEN os.getenv(TELEGRAM_BOT_TOKEN)这样即使代码仓库不慎被翻出来token 也不会泄露。顺带一提.env文件记得加进.gitignore。2.3 将 Bot 加入群组的三个关键设置机器人在私聊里可以直接对话但要在群组里工作还需要做三件事。很多人漏了第二步导致群组消息收不到排查半天。第一步是把机器人加进群。打开目标群组进入成员管理搜索你刚才创建的用户名添加即可。这一步没什么坑顶多是有的群开启了“仅管理员可添加成员”需要群管理操作一下。第二步最关键默认情况下Telegram 机器人有一个“隐私模式Privacy Mode”开启状态下群聊里所有不是主动提及、不是命令、不是回复机器人本人的消息机器人根本收不到。所以如果你想在群里实现类似“所有群成员发言后都能触发 AI 回答”的效果必须在 BotFather 里发送/setprivacy指令选择当前机器人然后设为 Disable。注意关闭隐私模式后机器人能收到群内的所有消息。这也意味着它实际上是在“读取”整个群聊内容。涉及敏感信息或内部讨论的群你要想清楚再关或者保留隐私模式只响应 提及。第三步是权限处理。如果只是想让它响应 提及和命令不一定要给管理员。但如果关闭了隐私模式并且需要读取全部消息某些情况下 Telegram 会要求机器人具有管理员权限才能正常拉取群消息直接给它一个“仅消息可读”类的最小权限即可。给管理员权限时要留心最大权限会让机器人能删成员、改群设置通常没必要。这三步做完群里搜到机器人用户名发一条消息测试一下看它有没有反应。没反应就查两点隐私模式是否已经关闭、重新拉起进程没。3. 核心代码实现让 OpenClaw 通过 Telegram 聊起来3.1 最小可用消息循环 Demo先把整个链路跑通再用 OpenClaw 替换核心逻辑。下面这版代码是 python-telegram-bot 20.x 的典型写法注册一个命令处理器和一个普通消息处理器start命令回一句欢迎语其他文本消息都走echo原样返回。import os from dotenv import load_dotenv from telegram import Update from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, filters, ContextTypes load_dotenv() TOKEN os.getenv(TELEGRAM_BOT_TOKEN) async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text(OpenClaw Bot 已就绪直接发消息试试。) async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE): text update.message.text if not text: await update.message.reply_text(当前只支持文本消息。) return await update.message.reply_text(text) if __name__ __main__: app ApplicationBuilder().token(TOKEN).build() app.add_handler(CommandHandler(start, start)) app.add_handler(MessageHandler(filters.TEXT ~filters.COMMAND, echo)) print(Bot 启动成功正在监听消息...) app.run_polling()这段代码放到服务器上跑起来私聊里给你的机器人发一句话它会原样回给你。看到回显说明 token 有效、网络链路通、事件循环正常整个接入的“地基”已经稳了。run_polling()是一个阻塞调用它会在内部维护长连接有消息就派发给对应 handler。你在终端里 CtrlC 可以退出正常退出会做清理。处理完消息后进程会继续保持等待状态不会因为一次对话结束而退出。3.2 集成 OpenClaw 完整接入逻辑回声测试通过后把echo函数里的逻辑替换成调用 OpenClaw。这里有一个非常关键的设计不要在 Telegram 的 handler 里写太多业务代码而是把 OpenClaw 调用封装成一个独立方法。async def handle_openclaw(text: str, session_id: str) - str: # 这里替换成你的 OpenClaw 实际调用方式 # 可能是一个本地 HTTP 请求也可能是直接导入项目模块调用 result your_openclaw_client.chat(text, session_idsession_id) return result async def message_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): text update.message.text if not text: return user update.effective_user session_id ftg_{user.id} await update.message.reply_text(收到正在处理请稍等...) try: reply await handle_openclaw(text, session_id) except Exception as e: reply f处理出错了{e} await update.message.reply_text(reply)这里your_openclaw_client只是占位实际项目里可能是 HTTP 调用本地端口也可能是直接导入 Python 包执行。关键在于session_id的设计用 Telegram 用户的 ID 作为会话标识这样每个用户对应 OpenClaw 侧的一条独立上下文互不干扰。后面如果要做多用户记忆隔离这个是核心。还有一点值得留意用户在群里聊天的场景下effective_user拿到的是群内发言人的用户 ID不是群 ID所以不同群里同一个用户也会被识别为同一个人上下文能跨群共用。如果你希望按“群 用户”双维度隔离就把update.effective_chat.id也拼进去。3.3 群组场景适配提及响应与上下文隔离在群组里机器人最容易遇到的问题就是“该回的不回不该回的瞎回”。Telegram 默认发到群里的消息如果机器人没被 也没被回复理论上它不该插嘴否则整个群会被刷屏烦死。可是我们又希望它能在群里被 的时候快速响应怎么办处理逻辑其实很简单判断消息文本里是否包含机器人的用户名。上下文对象里有context.bot.username直接拿它做字符串匹配。async def group_message_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): text update.message.text or bot_username context.bot.username is_mention f{bot_username} in text is_reply_to_bot ( update.message.reply_to_message and update.message.reply_to_message.from_user and update.message.reply_to_message.from_user.id context.bot.id ) if not is_mention and not is_reply_to_bot: return session_id fchat_{update.effective_chat.id}_user_{update.effective_user.id} cleaned_text text.replace(f{bot_username}, ).strip() await update.message.reply_text(正在处理请稍等...) reply await handle_openclaw(cleaned_text, session_id) await update.message.reply_text(reply)is_reply_to_bot处理的是“用户直接回复机器人消息”的场景这在群聊里很常用。比如机器人给出一个回答用户觉得不对直接回复它的那条消息提出追问这时候应该触发。cleaned_text把 前缀去掉避免把“Bot 你好”这种整段原样丢给 OpenClaw减轻无效 token 消耗。如果你希望机器人只在被 时响应保留隐私模式开启就行省流量也减少噪音。如果你确实要让它接收全部群消息做自动分析才需要前面提到的关闭隐私模式。但即使收到全部消息也建议在上面的 handler 里做 过滤否则机器人会接话茬接到让你怀疑人生。3.4 并发与超时控制接入 OpenClaw 之后最容易被忽略的是事件循环的阻塞问题。python-telegram-bot 的 handler 是异步执行的但如果你在handle_openclaw里同步调用了一个耗时 30 秒的 HTTP 请求整个事件循环会被卡住后面的消息都处理不动。表现为群里几个人同时在发消息机器人来回转圈不回复或者顺序全乱了。解决办法有两条路。第一把耗时调用放进线程池import asyncio def sync_call_openclaw(text, session_id): # 同步阻塞的函数可以是 requests.post 等 return your_openclaw_client.chat(text, session_id) async def handle_openclaw(text, session_id): return await asyncio.to_thread(sync_call_openclaw, text, session_id)asyncio.to_thread在 Python 3.9 可用它把同步函数丢到后台线程池执行事件循环不会被卡住。如果 OpenClaw 的客户端本身提供异步版本比如httpx.AsyncClient直接用异步版本更顺。第二给请求加上超时。不要让它无限等下去大模型接口偶尔会卡死或者假死。一般 60 秒到 120 秒比较合适超过就返回一个“处理超时请稍后重试”的兜底回复。这里用asyncio.wait_forasync def handle_openclaw_with_timeout(text, session_id): try: return await asyncio.wait_for( handle_openclaw(text, session_id), timeout120 ) except asyncio.TimeoutError: return 处理超时了请稍后再试。还有一个细节是并发会话的锁。如果多个用户同时触发对话而 OpenClaw 侧的单例模型不保证线程安全建议按 session_id 加一个字典级锁同一时间只让同一用户的请求串行执行不同用户之间互不影响。实现不复杂用一个defaultdict(asyncio.Lock)即可。4. 常见问题排查与避坑实录4.1 Token 报错 / 401 Unauthorized 的排查接入过程中最常见的报错是401 Unauthorized大概率 token 有问题。我在实际项目里见过的原因主要有三种token 复制时多复制了换行符或空格.env文件里的变量名和代码里读的变量名不一致某些编辑器会在文件末尾自动补空行导致读取异常。排查思路很直接在启动文件里临时打印一下 token 看看print(ftoken: {TOKEN})正常格式应该是一段数字加冒号再加一段字母数字混排不含任何空白符。如果 token 正常仍然 401检查是不是同一个 token 被多个人同时用了。Telegram 对同一 token 的并发连接有限制两个进程同时轮询大概率会有一个被挤下线表现为间歇性 401。提示修改机器人用户名不会导致 token 失效但通过 BotFather 重置 token 会让旧的立刻失效。如果你有多个环境共用同一个 token重置前务必确认所有服务都已停止。我个人习惯把 token 写入系统环境变量或 systemd 的 EnvironmentFile 里用.env也 OK但一定不要硬编码在.py文件里。接着前面说的每次重启前先env | grep TELEGRAM确认环境变量真的存在很多“莫名其妙 401”其实就栽在这里。4.2 群组里 Bot 收不到消息私聊正常、群里没反应这是几乎每个接群项目都会踩的坑。原因通常是隐私模式没有关闭或者关闭后进程没有重启。Telegram 的隐私模式不是即时生效的。你在 BotFather 里/setprivacy设置为 Disable 后最好等十几秒再把当前正在跑的 bot 进程重启一下。我之前遇到过改完设置后群里还是收不到消息后来发现是进程是从旧会话启动的根本没刷新权限配置。重启之后立刻好了。另外还要区分“收不到消息”和“收到了但不响应”。如果是前者群聊日志里应该完全看不到包含群 ID 的入站消息重点查隐私模式如果是后者说明消息有过来但被我们自己写的 过滤逻辑挡掉了可以临时注释过滤条件排查。还有一个容易忽略的场景机器人是在隐私模式开启时加入的群之后再关闭隐私模式需要注意群内管理员可能默认看不到机器人“已读取消息”但机器人实际能收。不等同于没生效。可以加一段轻量日志每天记录收发的消息量级。我习惯把入站消息的 chat_id 打出来logger.info(f入站消息 chat_id{update.effective_chat.id} from{update.effective_user.id})这样群里发一条消息日志立刻能看到来源问题定位就快了。4.3 getUpdates 409 Conflict 冲突日志里如果出现Conflict: terminated by other getUpdates request或者 HTTP 409说明当前 token 被另一个长轮询连接占用。最常见的触发场景是你原来已经有一个 bot 进程在后台跑着又在终端里重新启动了一次或者同时开了两个脚本文件或者用 Jupyter Notebook 调试后没关闭内核残留连接依然占着 token。排查方式按顺序来ps aux | grep python找到旧的 PID杀掉即可。如果杀完还报冲突检查是否给这个 token 配置过 Webhook因为设置过 Webhook 后 getUpdates 会失效或冲突。可以先尝试清空 Webhookcurl -s https://api.telegram.org/bot你的TOKEN/deleteWebhook返回{ok: true}表示清理成功。再用getWebhookInfo确认一下 webhook 地址为空curl -s https://api.telegram.org/bot你的TOKEN/getWebhookInfo这一步在从 Webhook 模式切回 Long Polling 时尤其重要Webhook 和 Long Polling 是互斥的必须二选一。如果你之前为了测试临时挂过 Webhook后面完全忘了这回事就会出现明明本地进程没问题但消息就是推不进来的诡异现象。4.4 消息响应缓慢或超时无回复消息发出去了机器人也显示“正在处理”但过了一两分钟还没有最终回复这在接大模型服务时很常见。绝大多数原因是上游模型接口稳定性和耗时问题不一定是你代码的问题。遇到这种情况我的策略是三层处理机制。第一层请求超时前面说过用asyncio.wait_for包一层120 秒还不回就直接返回错误。第二层重试机制OpenClaw 侧如果支持重试针对瞬时故障网络抖动、并发限流做 2~3 次退避重试。第三层兜底缓存把最近一次相同问题得到的回复缓存起来如果接口彻底挂了至少还有“上次回答过类似问题”的降级方案。此外还有一个常见的性能坑发送长文本时Telegram 单条消息最长 4096 字符超过会被截断报错。OpenClaw 的答案如果很长比如几千字必须先切片再分批发送否则机器人会静默失败表现就是“处理成功但没回复”。async def reply_long_text(update, text, chunk_size3500): for i in range(0, len(text), chunk_size): await update.message.reply_text(text[i:ichunk_size])切片长度我留了 3500给 Telegram 自身格式化和编码留点余量实测稳定。4.5 服务挂掉与日志排查本地终端跑着没问题但关掉终端、睡一觉起来发现机器人没反应了这是新手几乎必踩的问题。原因很简单——前台进程跟着终端会话一起退出。这类问题最好的解法是用 systemd 把这个进程托管起来设置崩溃自动重启而不是每次手动开终端挂着。具体怎么配下一部分细说。排查挂掉的原因先看进程在不在ps aux | grep openclaw_bot如果进程还在但消息不回就要看日志。没有日志的话一切排查都是瞎猜。我的建议是启动脚本里至少加一句PYTHONUNBUFFERED1环境变量让 Python 输出不缓冲日志实时落盘。如果用的是 systemd用journalctl -u就可以直接拉日志如果手动跑输出重定向到文件nohup python main.py bot.log 21 然后随时tail -f bot.log观察。日志里如果看到Traceback把堆栈信息拉到解决方向就明确了。5. 服务化部署与日常维护建议5.1 使用 systemd 托管机器人进程前面提过认真跑服务就别 nuhup 裸奔。用 systemd 把 bot 变成一个受管理的服务开机自启、崩溃重启、日志统一处理省心太多。写一个 service 文件放在/etc/systemd/system/openclaw-bot.service[Unit] DescriptionOpenClaw Telegram Bot Afternetwork.target [Service] Typesimple WorkingDirectory/opt/openclaw EnvironmentFile/opt/openclaw/.env ExecStart/opt/openclaw/venv/bin/python /opt/openclaw/main.py Restartalways RestartSec5 EnvironmentPYTHONUNBUFFERED1 [Install] WantedBymulti-user.target关键配置说明一下EnvironmentFile加载我们前面写的.env这样 token 就不会出现在 systemd 命令里ExecStart用虚拟环境里的 Python 绝对路径避免 systemd 环境里 PATH 不一致找不到解释器Restartalways配合RestartSec5崩溃后 5 秒自动拉起。启动并设为开机自启sudo systemctl daemon-reload sudo systemctl enable --now openclaw-bot用 systemd 之后三个常用命令sudo systemctl status openclaw-bot sudo journalctl -u openclaw-bot -f sudo systemctl restart openclaw-botjournalctl默认会把日志输出到系统日志里配合之前说的PYTHONUNBUFFERED1实时性非常好。改完代码后记得sudo systemctl restart openclaw-bot不然跑的还是旧进程。5.2 升级与多实例容灾注意事项OpenClaw 项目本身更新很勤python-telegram-bot 也有大版本频繁迭代的情况。升级前备份好会话数据文件尤其是 OpenClaw 侧的记忆、会话历史、用户配置。别问我为什么强调备份我经历过一次升级后会话文件格式不兼容整个项目往前回滚了两天才恢复。python-telegram-bot 从 13.x 到 20.x 是一次大版本重构API 从同步风格变成了纯异步。如果你在网上搜到老代码看都不用看直接跳过。20.x 之后的升级相对平滑但也别盲目pip install -U先读 changelog再在测试群里跑半天确认没问题再切换主服务。多实例容灾这块如果你真的想让机器人高可用可以考虑启动两个实例并且用不同 token 指向同一个智能体后端前端群只留一个。这属于容灾方案日常个人项目用不上但值得知道有这么个思路——避免你自己脑补“跑两个进程就一定更强”的错误认知同 token 跑两个实例只会引来 409 冲突。最后再分享一个维护上的小技巧给 OpenClaw 侧增加一个简单的审计日志记录每次入站会话 ID、消息长度和处理耗时。不用特别复杂一条结构化字符串即可。出问题时你通过时间戳能快速回溯这个用户在什么时间点发过什么消息、拿到了什么回复前面所有排查隐私模式、409、超时都会从“猜谜游戏”变成“看日志找证据”效率完全不是一个级别。