国庆没出门。老家那边人挤人,想了想还是把票退了,窝在家里蹲了七天。本来打算纯休息,结果队友群从早响到晚,不是发报错截图,就是问“这个需求你能看下不”,再不就是“帮我在那台机器上跑个脚本”。问题是代码都在书房那台台式机上,人在床上或者沙发上,每次都得爬起来开远程,干两分钟又躺回去,来回折腾比上班还累。
被搞烦了之后我灵光一闪:那台电脑上不是装了 Claude Code 嘛。为什么不做一个群聊机器人,让群里的人直接@我,然后消息跑到电脑上触发 Claude Code,它自己读代码、跑命令、该改就改,最后把结果发回群里?我只需要躺在沙发上,偶尔瞄一眼手机。
说干就干。国庆第四天,我花了大概一天多时间,把整个链路搭起来了。这篇文章就把这个项目的完整思路、技术选型、关键代码实现和踩过的坑全部拆开讲一遍,给也想在家里、工作室或者小团队里搞“群聊驱动开发机”的人一个可复现的参考。
先说明一下,我下面讲的以通用套路为主,聊天机器人平台你用哪个都行,只要是那种支持群消息回调的 IM 机器人,原理完全一样。
1. 整体思路与方案选型
1.1 核心问题不是“远程执行命令”,而是“协作上下文断档”
很多人一看这项目,第一反应是“这不就是个远程执行命令的工具吗”。一开始我也以为是,做完了才发现真正烦人的不是执行,而是协作上下文不停断档。
平时别人请你帮忙看个代码问题,流程是什么?先截图,再贴路径,然后语音打过来补三句背景,你跑到电脑前复现一遍,发现问题,改两行,截图发回去,对方再问一句“那这个会不会影响另一边?”。这一来一回,大部分时间不是在“干活”,而是在“传递信息”。
群聊天然解决上下文问题。消息记录自带背景,@ 自带指代关系,发代码片段、贴日志都很自然。我要做的只是让电脑变成一个“听得懂群聊的队员”,让 Claude Code 作为执行引擎,把“人的描述”变成“对代码库的操作”。
所以这个项目的本质是:用群聊做接口,用本地守护进程做调度,用 Claude Code 做执行器,把整个编程协作闭环搬到聊天框里。
1.2 为什么选了“群聊机器人 + 本地守护进程”而不是远程桌面或自建任务系统
我其实先列了三个备选方案,每一个都花了几分钟否掉:
| 方案 | 优点 | 致命缺点 |
|---|---|---|
| 远程桌面/SSH 手动操作 | 能力完整,什么都能干 | 必须有人在电脑前配合,没法无人值守;移动端操作体验极差 |
| 自建一个 Web 任务面板 | 功能自由,接口可控 | 队友要记网址、登录、学习新系统,使用门槛高,基本没人会用 |
| 群聊机器人 + 本地守护进程 | 零学习成本,聊天记录即上下文,手机就能发指令 | 需要写一点胶水代码,但一次写完长期受益 |
最终方案没什么悬念,就是群聊机器人。因为目标用户就是身边这几个队友,他们已经在群里了,不需要再打开任何新工具。所有操作就是“在聊天框里 @ 一下,把任务说清楚”,这对任何人来说都没有学习成本。
而且这个方案有一个额外好处——任务过程自动留痕。谁在什么时候让电脑干了什么,结果如何,群里全都有记录,想复盘随时往上翻,比自己在终端里记命令靠谱多了。
1.3 技术栈取舍与架构总览
技术栈非常简单:
- 群聊机器人:用你现有平台提供的 Webhook / API,负责收消息和发消息。
- 本地守护进程:我用的 Python 3,主要看中它处理 JSON、调子进程、拼路径方便。你想用 Node 或者 Go 也完全没问题,逻辑一样。
- 执行引擎:Claude Code 的 CLI 非交互模式,接收自然语言指令后直接操作代码目录。
- 后台运行:守护脚本挂在
nohup或者screen里,不占终端窗口。
整体流程长这样:机器人在群里收到消息 → 守护进程拉取消息 → 解析 @ 对象和任务文本 → 判断是不是自己负责的任务 → 丢进队列 → 依次交给 Claude Code 执行 → 把结果整理成 Markdown 发回群里 → 结束。
你仔细看就会发现,中间没有数据库,没有消息中间件,就是一个单机守护进程在循环里转。做这个项目最忌讳一开始就上复杂架构,先把主链路跑通,后面真不够用了再说。
2. 核心组件拆解与原理说明
2.1 消息接入层:能收消息、能发消息,就成功了一半
你要做的第一件事,是让你的群机器人能“收到消息”和“主动发言”。不同 IM 平台差别挺大,但大体上就两种方式:
第一种是平台把群消息实时推送到你的回调地址。这种方式需要你有一个公网可达的 HTTP 服务,机器人每收到一条消息,就往你的服务 POST 一份数据。优点是实时性最好,消息一来立刻就能处理;缺点是你得想办法让本机服务被外网访问到,开发机上还得挂个反向代理,麻烦。
第二种是平台提供“主动拉取消息”的 API,你隔几秒调一次接口拿新消息。我强烈推荐这种方式,理由很简单:本机不用暴露任何公网端口,守护进程只要内网能访问机器人 API 就行,安全风险小一大截。代价是最多有几秒延迟,但群聊场景根本不在乎这几秒。
2.2 消息解析:怎么认出“这是在叫我干活”
机器人收到消息后,守护进程要先做四件事:
- 去重:每条消息平台都会带一个唯一 ID,记下已处理过的 ID,防止重复执行。
- 认人:看消息里是否包含 @ 我的昵称。每个守护进程启动时读配置,知道自己在群里叫什么。
- 认任务:把消息里“@昵称 任务描述”的“任务描述”部分提取出来,丢掉情绪词和客套话,只留核心需求。
- 认权限:根据发送人的 ID 判断他是什么角色,能不能让我执行改文件这类操作。
这四步看起来简单,实则有坑。比如群昵称和平台用户 ID 的对应关系,比如消息里有多个 @ 时怎么分辨哪个是“叫我干活”而不是“顺嘴提我”。我最后实现的规则是:只有 @ 开头且后面直接跟任务文本的消息才触发,如果 @ 出现在消息中段,一律忽略。
2.3 Claude Code 非交互模式:让它“跑完就闭嘴”
Claude Code 日常是交互式终端工具,你敲一句它回一句。但在我们这个场景里,不能有人坐在终端前跟它对话,必须让它接收一个完整任务然后自动执行、自动退出。
Claude Code 提供了非交互执行模式,大致就是在命令行后面直接挂任务描述,它会执行完任务把结果输出到标准输出。我实际用下来最重要的三个点是:
- 工作目录:Claude Code 会在当前目录里读代码、改代码,所以守护进程必须把工作目录固定到某个项目目录,绝对不能让它“整个硬盘到处跑”。
- 执行时限:任务复杂了它会思考很久甚至卡住,必须用超时机制兜底。
- 权限控制:AI 工具执行代码时经常要动文件系统、跑命令,要提前限制可用操作范围。具体参数不同版本有差异,但思路是固定的:能不给的权限就不给,尤其是“跳过所有权限确认”这类开关,非受控环境千万别开。
3. 实操过程:一步步把群聊和 Claude Code 串起来
3.1 先搭目录和配置文件
我在电脑上建了一个统一的工作目录,所有任务都只能在这个目录里操作:
claude-workspace/ ├── jobs/ # 每个任务的独立临时目录 ├── logs/ # 全部执行日志 ├── config.json # 机器人、权限、目录配置 ├── bot_worker.py # 守护进程主脚本 └── commands/ ├── test.sh # 测试类命令封装 └── review.sh # 审查类命令封装config.json是整个系统的核心,我用它管所有可变项:
{ "bot": { "nickname": "书房主机", "chat_id": "群ID", "api_base": "https://your.im.bot/api", "poll_interval": 3 }, "allowed_users": { "user_a_id": "admin", "user_b_id": "member" }, "workspace": "/home/me/claude-workspace", "timeout_seconds": 600, "claude_bin": "/usr/local/bin/claude" }把配置和代码分开是我一直以来的习惯,因为配置变来变去(今天换群号、明天加人),代码最好一行都不用动。顺便说一句,config.json这种纯文本格式谁都能看懂,比起搞一堆环境变量更直观。
3.2 消息轮询与 @ 解析
核心脚本用一个死循环定时拉新消息。逻辑不复杂,但注意要把“去重”和“归属判断”做扎实:
import json import time import hashlib from datetime import datetime POLL_INTERVAL = 3 seen_messages = set() def fetch_new_messages(chat_id): # 调用 IM 平台 API,返回消息列表 # 如果平台不支持拉取,换成 webhook 接收后調用同一个处理函数 return api_get(f"/chat/{chat_id}/messages") def parse_mention(msg_text, my_nickname): # 只处理以 @昵称 开头的消息 prefix = f"@{my_nickname}" if msg_text.startswith(prefix): task_text = msg_text[len(prefix):].strip() return task_text or None return None def main_loop(): while True: try: messages = fetch_new_messages(config["bot"]["chat_id"]) for msg in messages: msg_id = msg["id"] if msg_id in seen_messages: continue seen_messages.add(msg_id) task_text = parse_mention(msg["text"], config["bot"]["nickname"]) if task_text is None: continue if msg["user_id"] not in config["allowed_users"]: send_message(config["bot"]["chat_id"], "你没有权限叫我干活") continue dispatch_task(task_text, msg["user_id"]) except Exception as e: # 网络抖动、API 错误都跳过,不能让守护进程死掉 log_error(e) time.sleep(POLL_INTERVAL)这里有一个细节值得说一下:seen_messages在内存里无限增长,跑久了会占内存。我实际的做法是只保留最近 2000 条消息 ID,超过就丢旧的,反正正常使用不会回退处理那么早的消息。
3.3 任务分发:排队执行,别挤到一起
Claude Code 执行任务很吃资源,两个任务同时跑轻则互相串目录,重则把机器搞到卡死。所以我用了一个简单队列,保证同一时间只执行一个任务。
import queue import threading task_queue = queue.Queue() # 生产者:收到合法任务后入队 def dispatch_task(task_text, user_id): task_id = hashlib.md5((task_text + str(time.time())).encode()).hexdigest()[:8] task_queue.put({ "task_id": task_id, "text": task_text, "user_id": user_id, "create_time": datetime.now().isoformat() }) send_message(config["bot"]["chat_id"], f"收到任务 {task_id},排队中,前面还有 {task_queue.qsize() - 1} 个任务") # 消费者:串行执行 def worker(): while True: task = task_queue.get() run_task_with_lock(task) task_queue.task_done()排队的另一个好处是,群里的人能看到“前面还有几个任务”,就不会觉得机器人没反应。用户体验差一大截和好一大截,往往就差这一句反馈。
3.4 核心动作:把任务交给 Claude Code
这是整个项目最关键的一段。我要把群里的自然语言任务文本交给 Claude Code 执行,并且在超时后强制止损。
import subprocess import os def run_claude(task_text, cwd): # 把原始任务润色一下,补充约束条件 prompt = ( f"你现在在项目目录 {cwd} 执行任务。\n" f"任务需求:{task_text}\n" f"要求:如果需要修改代码,先说明改动思路;" f"执行完要给出结论;不要主动提交远程仓库;不要删除重要文件。" ) cmd = [ config["claude_bin"], "-p", prompt, "--output-format", "json" ] proc = subprocess.run( cmd, cwd=cwd, capture_output=True, text=True, encoding="utf-8", timeout=config["timeout_seconds"] ) return proc.stdout, proc.stderr我一开始的 prompt 非常简单,就是原样把群里的话丢进去,结果 Claude Code 经常领会不了“只跑测试不要改代码”这类隐含要求。后来我把常见的约束直接做成固定模板,每次都在任务描述前面拼接一段“行为规范”,效果立刻稳定很多。
有人可能会问,为什么不直接让 Claude Code 自己读群聊上下文?我试过,但效果不稳定。群聊里太多口语、小表情、无关对话,反而干扰判断。把上下文提炼成一段干净的任务描述,是最可控的方式。
3.5 结果回传:把冷冰冰的输出翻译成“人话”
Claude Code 的输出是 JSON 结构,里面可能有日志、代码 diff、思考过程,直接丢群里没人看。我在回传前做了一层整理:
- 只取关键结论:从输出里提取“做了什么”“结果如何”这两段。
- 附带关键证据:比如测试通过的数量、修改了哪些文件。
- 失败时贴出错误日志的最后 20 行。
回传消息的格式我固定成这样:
任务完成|任务号:8f3a2c1d 执行耗时:247秒 结论:测试全部通过,修复了登录超时的问题。 修改文件:src/auth/login.py、tests/test_login.py 详细输出见:https://your.log.server/jobs/8f3a2c1d这里我加了一个细节:每个任务都会把完整执行日志落到logs/目录,同时在群里只发摘要。因为群聊消息刷得快,完整日志发出去刷屏不说,也没法搜索。想看细节的人自己打开日志文件,群里的消息保持简洁。
3.6 权限与安全控制
安全是这个项目里不能省的部分,因为我把“能改文件的 AI”暴露在了群里。以下是我实际做的四级控制,供你参考:
第一级:人名单。配置里只允许特定成员发任务指令,其他人 @ 了也只会收到“没权限”的回复。
第二级:目录围栏。Claude Code 的工作目录固定在某个 workspace 里,子进程启动前强制切换目录,参数里明确禁止读写目录范围之外的路径。
第三级:命令封装。对常见任务写成固定脚本,比如run_test、run_review,机器人只允许调用这些封装过的命令,而不是把整段 shell 透传给 Claude Code。注意这里 Claude Code 内部的工具能力依然存在,所以还需要配合版本本身的权限参数限制。
第四级:危险操作拦截。我专门写了一个关键词检查,任务文本里出现“删库”“清空”“强制 push”“sudo”之类的高危词,直接拒绝执行并要求人工确认。
这套权限体系我花了半个下午,带来的安全感是无价的。毕竟你也不想某天群里来个人说“帮我把 / 目录清空”,然后你的 AI 助手真的照做。
4. 常见问题与排查技巧实录
4.1 消息重复执行了两次
这是我遇到的第一个问题。现象是同一个任务,群里收到了两次“收到任务”的确认,Claude Code 也被调用了两次。排查后发现是平台在回调超时后自动重发了一次消息,而我本地代码在收到第一条时还没把消息 ID 记下来就处理完了,重复消息进来时 ID 没进过seen_messages,于是又被执行了一遍。
解决方法是把所有消息 ID 在处理前先写入一个本地文件做持久化,而不是只放内存。这样即使守护进程重启,也能记住哪些消息已经处理过。对于这种低频率场景,SQLite 都嫌重,一个单文件就够了。
4.2 Claude Code 卡住不动,任务挂死
这是最头疼的问题。任务发出去,群里半天没反馈,一看进程还在,但不知道它在干嘛。
我的处理分两步。第一步是给 subprocess 加超时参数,超过 10 分钟直接kill,并把锅明确告诉群友:超时了,建议拆小任务。第二步是给 Claude Code 的 prompt 里加一个“如果任务超过预期复杂度,输出当前进度并停止,不要无限思考”的约束,很多卡死其实是模型在上下文里绕圈出不来。
实测下来,5 分钟内能完成的任务成功率最高,超过 10 分钟的任务基本都有问题。如果你的任务普遍要跑很久,建议你自己先拆一下描述,不要指望一个指令搞定全部。
4.3 返回值乱码,中文全变“口口”
一开始我用 subprocess 拿 stdout 时没指定编码,Python 在某些平台默认用了系统的编码,中文直接乱掉。Claude Code 输出是 UTF-8,所以读取时必须显式指定:
proc = subprocess.run(..., text=True, encoding="utf-8", errors="replace")errors="replace"很重要,它保证即使遇到特殊字符也只是替换掉个别字,不会让整个输出崩溃。
4.4 任务结果里带了 Markdown 语法导致群里排版错乱
Claude Code 输出里经常有代码块标记(三个反引号)、# 号标题、* 号列表。直接发到群里,有时候会被平台的编辑器当成 Markdown 渲染,把原本想展示的内容搞乱。
我的处理是:发回群里前,把特殊情况转义掉。比如代码块改为引用格式,长路径用行内代码包住。具体怎么转义要根据你的 IM 平台来调,核心思路是“用平台支持的格式来展示,而不是直接贴原始输出”。
4.5 多台电脑/多个人怎么区分任务归属
如果你的场景里有多台开发机、多个队友,每个机器人守护进程只处理 @ 自己昵称的消息。我给每台电脑设置了不同的昵称,比如“书房主机”“备用机”“测试服务器”。群友 @ 对应昵称,就由对应电脑执行。
如果两个人昵称一样,就会冲突。我的建议是给每个人设一个唯一的“机器人名字”,跟日常昵称区分开,这样既好认又能避免歧义。
4.6 我的几条实操心得
做完这个项目,我最大的感受是:这类工具的难点不在技术,而在把“人的预期”和“AI 的执行”对齐。技术链路我用一个下午就通了,剩下的大半天全在调槽位、整理 prompt、处理边界情况。
不要一上来就整权限系统、分布式队列、消息总线。先把“群里发消息→电脑执行→结果回群里”这个最小闭环跑通,哪怕丑一点、糙一点都行。闭环通了之后,你再根据实际使用中的痛点,一个一个补,比一开始空想设计要高效得多。
还有一个很实用的小技巧:把日志按天分割,文件名带上日期。因为群聊记录容易刷掉,但日志不会骗人,哪天出问题直接看当天文件,排查效率翻倍。
最后分享一个我现在觉得最值钱的地方。把这个群聊机器人接上 Claude Code 之后,一台开发机就变成了一个“带 AI 能力的远程协作者”:群友想看报错原因,@ 一下书房的机器,AI 自己登录查日志、看代码、给结论;想跑测试,@ 一下备用机,AI 自己改完跑完发报告。对我这种节假日不想出门、又不得不处理点破事的人来说,这个国庆过得简直太值了。