1. 项目概述与核心价值最近在折腾一个挺有意思的项目叫“Claude Code 飞书机器人”。简单说就是把 Anthropic 那个能直接操作你本地电脑的 Claude Code给接到飞书里去了。这样一来你就能在飞书的聊天窗口里像跟一个懂技术的同事聊天一样让它帮你写代码、跑脚本、查文件甚至开个音乐播放器。这玩意儿特别适合我们这种经常在多个项目间切换、电脑桌面乱成一锅粥的开发或者运维。核心就一句话让 AI 助手从“聊天机器人”变成你电脑的“远程操作员”。以前用 Claude API 或者 ChatGPT它再聪明也只能跟你“纸上谈兵”告诉你代码怎么写但执行还得你自己来。现在不一样了你直接在飞书里 它说“帮我在/home/project下建个test.py”它真就给你建好了。这种“所见即所得”的体验对于提升日常工作效率尤其是处理那些重复、琐碎的本地操作帮助太大了。我之所以花时间搞这个是因为发现市面上的同类工具比如 Slack 上的 ClawdBot功能大多集中在对话和集成云端 API真正能安全、可控地操作本地环境的很少。而这个方案利用 Claude Code 的原生能力和飞书的长连接机制实现了一个轻量、私有且上下文隔离的助手。下面我就把自己从零搭建、踩坑调试到最终稳定运行的完整过程以及背后的设计思考详细拆解给你看。2. 核心设计思路与架构解析2.1 为什么是“Claude Code 飞书”这个组合选择这个技术栈不是拍脑袋决定的而是基于几个核心需求点的权衡对本地环境的安全操作需求这是首要驱动力。很多自动化需求比如批量重命名文件、执行本地测试套件、清理临时目录都需要直接读写文件系统或执行 Shell 命令。Claude Code 作为官方 CLI 工具提供了经过授权的、安全的本地操作能力这是纯对话模型 API 无法比拟的。企业级沟通工具的便利性飞书是国内很多团队的首选协作工具。将助手集成进去意味着无需切换应用在工作流中自然触达。相比自建 Web 页面或使用小众聊天工具飞书的用户接受度和使用频率更高。长连接 vs Webhook 的稳定性飞书支持两种事件接收方式Webhook需要公网域名和 HTTPS和长连接WebSocket。对于个人开发者或内网环境申请公网域名、配置 SSL 证书是一道门槛。长连接方案让机器人主动连接飞书服务器完美规避了这道坎部署极其简单。会话上下文的精细化管理在群聊中不同话题的讨论容易混杂。本设计为每一个飞书聊天窗口私聊或群聊分配一个独立的会话 ID。这意味着你和机器人的私聊是一个独立的知识库项目群聊是另一个它们互不干扰。重启服务后基于 SQLite 的持久化存储能让对话继续不会失忆。整个架构可以看作一个高效的路由器飞书负责接收和发送消息核心的main_websocket.py是消息分发中心而 Claude Code 则是真正干活的“执行引擎”。这种松耦合设计也带来了另一个好处Agent 后端可拔插。如果你哪天觉得 Claude Code 不够用想换成 OpenAI 的 Assistant 或者本地部署的 Llama只需要替换其中一个模块其他部分基本不用动。2.2 技术栈选型深度剖析Python 3.10没什么好说的生态丰富异步支持成熟asyncio是快速开发这类集成脚本的首选。claude-agent-sdk这是 Anthropic 官方为 Claude Code 提供的 Python SDK。它封装了与本地 Claude Code 守护进程的通信协议。关键点在于它并不是去模拟一个浏览器或 CLI 环境而是通过一个本地 Socket 或 HTTP 接口与 Claude Code 核心进程交互保证了操作的稳定性和授权状态的一致性。lark-oapi飞书官方 OpenAPI 的 Python SDK。用它来处理身份认证App ID/Secret、建立和维护 WebSocket 长连接、解析飞书的事件消息格式能省去大量手动处理 HTTP 请求和签名的麻烦。SQLite轻量级文件数据库。选择它而不是内存存储核心目的是会话持久化。当你的机器人服务因为更新或故障重启时所有正在进行的对话上下文session_id都能被保留下来用户完全无感。这对于打造一个可靠的服务体验至关重要。注意这里有一个容易混淆的点。claude-agent-sdk是用来驱动本地的 Claude Code 应用的。你需要先在电脑上通过claude login完成 Anthropic 账号的授权。这个 SDK 本身不包含大模型它只是一个“遥控器”。因此所有的模型推理和代码执行能力都依赖于你本地安装的 Claude Code 应用及其背后的 Claude 模型服务。3. 环境准备与详细配置指南3.1 基础依赖安装与虚拟环境管理第一步把代码拉下来。建议使用git方便后续更新。git clone 项目仓库地址 cd remote-claude-code强烈建议使用 Python 虚拟环境来隔离项目依赖避免污染系统环境或与其他项目冲突。这里我用venv# 创建虚拟环境 python3 -m venv .venv # 激活虚拟环境 # Linux/macOS source .venv/bin/activate # Windows .venv\Scripts\activate激活后命令行提示符前通常会显示(.venv)表示你已经在这个虚拟环境里了。接下来安装项目依赖pip install -r requirements.txtrequirements.txt里主要就是lark-oapi和claude-agent-sdk。如果安装速度慢可以临时换用国内镜像源比如pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple。3.2 飞书应用创建与关键配置详解这是整个流程中相对复杂但至关重要的一步一步错可能导致机器人收不到消息。登录与创建应用访问 飞书开放平台 。点击“创建企业自建应用”。应用名称可以随意比如“我的Claude助手”。创建成功后进入应用详情页。在这里你需要找到并记录下App ID和App Secret。这相当于机器人的账号密码。配置事件订阅核心步骤在应用管理后台找到“事件订阅”菜单。你会看到“请求地址配置”选项。这里请务必选择“使用长连接接收事件”。这是本项目能免公网域名运行的关键。在“订阅事件”部分点击“添加事件”。在搜索框里输入im.message.receive_v1接收消息v1版本并勾选它。这个事件表示机器人订阅了接收聊天消息的能力。配置权限找到“权限管理”菜单。机器人要能收发消息需要对应的 API 权限。在“搜索权限”框里输入im:message。通常你需要勾选以下几个im:message发送与接收单聊、群组消息的基础权限im:message:send_as_bot以机器人身份发送消息im:message.p2p_msg:readonly读取用户发给机器人的单聊消息im:message:send主动发送消息勾选后记得在页面底部点击“批量开通”来申请这些权限。发布与启用在“版本管理与发布”里创建一个新版本比如1.0.0并填写简单的版本描述。然后“申请发布”。通常企业自建应用审核很快几乎是秒过。发布后回到“凭证与基础信息”页面你会看到“应用启用状态”变为“已启用”。将机器人添加到聊天在飞书客户端里找到任意一个你想让机器人加入的群组或者直接搜索机器人的名称。在群组中“添加成员”搜索你的应用名添加即可。对于私聊直接在搜索框搜应用名然后点击“发送消息”即可开始私聊。实操心得飞书后台的 UI 偶尔会有调整但核心流程不变创建应用 - 获取凭证 - 订阅消息事件 - 开通权限 - 发布启用。如果遇到机器人收不到消息99%的问题出在“事件订阅”没有正确配置为“长连接”或者权限没有开通成功。可以尝试重新保存一下事件订阅配置。3.3 Claude Code 的安装与登录我们的机器人后端是 Claude Code所以必须在运行这台机器上安装它。# 对于 macOS 和 Linux 用户官方推荐的一键安装脚本 curl -fsSL https://claude.ai/install.sh | bash # 安装后Claude Code 的命令行工具 claude 应该就被添加到你的 PATH 了。 # 对于 Windows 用户可以通过 npm 安装 npm install -g anthropic-ai/claude-code安装完成后需要进行登录授权将你的 Claude 账号与本地工具绑定claude login执行这个命令后它会自动打开你的默认浏览器跳转到 Anthropic 的授权页面。你用注册 Claude 的账号登录并授权即可。授权成功后命令行会显示登录成功的信息。验证安装可以运行claude --version查看版本或者简单问个问题claude hello看是否能正常返回以确保 Claude Code 守护进程在正常运行。3.4 项目环境变量配置项目根目录下有一个.env.example文件这是环境变量的模板。我们需要复制它并填入真实信息。cp .env.example .env然后用文本编辑器打开.env文件内容很简单APP_IDcli_xxxxxx # 替换为你的飞书应用 App ID APP_SECRETxxxxxx # 替换为你的飞书应用 App Secret注意事项.env文件包含了敏感信息千万不要把它提交到 Git 仓库中。项目自带的.gitignore文件通常已经包含了忽略.env的规则但你自己最好再确认一下。4. 核心模块源码解析与实操4.1 会话管理如何实现上下文隔离与持久化这是项目的智能所在。核心代码在src/data_base_utils/session_store.py。我们来看它的设计。# 示例性代码展示核心逻辑 import sqlite3 from typing import Optional class SessionStore: def __init__(self, db_path: str data/sessions.db): self.db_path db_path self._init_db() # 初始化数据库和表 def _init_db(self): conn sqlite3.connect(self.db_path) cursor conn.cursor() # 创建会话表核心字段是 session_id 和 chat_id cursor.execute( CREATE TABLE IF NOT EXISTS sessions ( chat_id TEXT PRIMARY KEY, session_id TEXT NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) conn.commit() conn.close() def get_session_id(self, chat_id: str) - Optional[str]: 根据飞书聊天ID获取对应的Claude会话ID # 查询逻辑... pass def save_session_id(self, chat_id: str, session_id: str): 保存或更新聊天ID与会话ID的映射关系 # 插入或更新逻辑... pass关键设计解析chat_id是飞聊的唯一标识飞书为每一个单聊或群聊分配一个唯一的chat_id。当消息事件到来时我们会提取这个chat_id。session_id是 Claude Code 的会话句柄Claude Code SDK 在创建新对话时会返回一个session_id用于后续在同一上下文中继续对话。映射关系持久化SessionStore类在 SQLite 数据库中建立了一张表将chat_id和session_id一一对应存储起来。当收到来自某个chat_id的新消息时程序会先查表如果找到已有的session_id就将其传给 Claude Code从而继续之前的对话如果没找到则创建一个新的 Claude 会话并保存这个新的映射关系。这样做的巨大优势用户A与用户B私聊互不干扰因为他们的chat_id不同所以对应不同的session_id和 Claude 对话上下文。项目群聊共享知识项目群的所有成员都在同一个chat_id下所以他们共享一个session_id。张三问“我们的项目结构是怎样的”李四接着问“帮我写一个README”Claude 能基于之前的回答来生成更相关的 README。服务重启无感因为映射关系存在数据库里即使机器人进程重启重新加载后每个聊天依然能找回之前的会话对话不会中断。4.2 飞书长连接通信核心主入口文件src/main_websocket.py负责处理与飞书的通信。它使用lark-oapi的 WebSocket 客户端。核心流程简化如下import asyncio from lark_oapi.websocket import * # 1. 初始化客户端传入 App ID 和 App Secret client WebSocketClient(APP_ID, APP_SECRET) # 2. 定义消息处理函数 async def handle_message(event): # 提取消息类型、聊天ID、用户ID、消息内容 chat_id event.event.message.chat_id user_id event.event.sender.sender_id.user_id text_content event.event.message.content # 需要解析JSON # 3. 路由到业务处理逻辑 reply_text await process_core_logic(chat_id, text_content) # 4. 调用飞书API将回复发回原聊天 await client.reply_message(event, reply_text) # 5. 注册处理器并启动客户端 client.register(handle_message) asyncio.run(client.start())长连接的优势与心跳维护 与 HTTP Webhook 的“飞书推给你”不同长连接是“你主动连接飞书服务器并保持在线”。lark-oapi的 SDK 内部封装了心跳包机制会自动定时发送ping以保持连接活跃防止被服务器断开。这比你自己去维护一个 HTTP 端点要省心得多。踩坑记录在早期测试中我曾遇到机器人偶尔“失联”的情况。后来发现是在处理复杂、耗时的 Claude 请求时阻塞了主事件循环导致无法及时响应飞书的心跳检测。解决方案是将耗时的同步 IO 操作如调用 Claude Code放到线程池中执行避免阻塞异步循环。代码中使用了asyncio.to_thread来包装对 Claude SDK 的调用确保了通信的流畅性。4.3 Claude Code 客户端封装src/claude_code/conversation.py是对claude-agent-sdk的轻量级封装目的是提供一个简单、统一的chat_sync接口。from claude_agent_sdk import Client class ClaudeCodeClient: def __init__(self): # 初始化SDK客户端默认会连接本地Claude Code守护进程 self.client Client() def chat_sync(self, message: str, session_id: str None) - tuple[str, str]: 核心对话方法。 参数 message: 用户输入 session_id: 可选的现有会话ID。如果为None则创建新会话。 返回 (Claude的回复文本, 本次使用的session_id) try: # 如果有session_id则继续该会话 if session_id: response self.client.send_message(session_idsession_id, messagemessage) else: # 否则创建新会话 response self.client.create_session(messagemessage) session_id response.session_id # 从响应中提取文本回复 reply_text response.content return reply_text, session_id except Exception as e: # 处理异常例如Claude Code未启动或网络错误 error_msg f调用Claude Code时出错{str(e)} return error_msg, session_id # 返回错误信息和原有的session_id如果有关键点错误处理网络波动或 Claude Code 进程异常可能导致调用失败。封装层进行了基本的异常捕获并返回友好的错误信息给飞书用户而不是让整个机器人崩溃。会话复用这是实现连续对话的关键。session_id就像一个对话房间的门牌号。只要传入相同的session_idClaude 就会记得这个房间里之前说过的所有话。5. 完整部署与运行流程5.1 启动服务与验证配置好一切后就可以启动机器人了。项目提供了几种方式方式一前台运行用于调试和查看实时日志python -m src.main_websocket如果一切正常控制台会输出连接飞书服务器成功的日志并显示“Bot is online”之类的信息。方式二后台运行用于生产环境./start.sh这个脚本本质上是用nohup或将进程放到后台并将日志输出到log.log文件。你可以用./stop.sh来停止服务。验证机器人是否在线在飞书里找到你添加的机器人发送一条消息比如“你好”。查看运行服务的终端或log.log文件。你应该能看到收到消息和发送回复的日志记录。如果机器人回复了恭喜你基础通道打通了。5.2 核心功能使用示例与解读让我们通过几个具体例子看看机器人如何理解并执行你的命令示例1文件操作你发送机器人 在桌面创建一个名为“计划.txt”的文件内容写“周一项目会议”机器人背后逻辑消息被路由到 Claude Code附带当前聊天对应的session_id。Claude Code 理解指令它知道“桌面”对应你系统的~/Desktop或C:\Users\YourName\Desktop。它在本地执行文件创建和写入操作。操作成功后Claude 生成回复“已在您的桌面创建‘计划.txt’文件。”回复被发回飞书。你的体验几秒后你的桌面上真的多了一个“计划.txt”文件。示例2执行系统命令你发送机器人 帮我看看当前目录下有哪些Python文件按大小排序机器人背后逻辑Claude Code 接收到指令。它在运行机器人服务的那个工作目录下执行类似find . -name *.py -exec ls -lh {} \; | sort -k5hr的 Shell 命令具体命令可能因 Claude 的理解而略有差异。捕获命令输出整理成易读的格式。将结果发回飞书。你的体验在飞书里收到一个清晰的列表列出了所有.py文件及其大小。示例3连续对话上下文保持第一轮你发送机器人 用Python写一个计算斐波那契数列的函数第二轮你发送机器人 修改一下加上函数注释关键点由于两轮消息的chat_id相同程序会使用同一个session_id调用 Claude Code。Claude 在第二轮时完全记得第一轮它生成的代码因此它会在原有代码的基础上添加注释而不是生成一个全新的、无关的函数。5.3 服务管理与运维要点一个长期运行的服务需要考虑稳定性。日志查看使用tail -f log.log可以实时跟踪日志这对于排查问题非常有用。进程监控可以写一个简单的监控脚本定期检查main_websocket.py的进程是否存在如果不存在则自动重启。或者使用更专业的进程管理工具如supervisor或systemd。资源清理Claude Code 的会话可能会占用内存。虽然 SDK 和本项目没有提供直接的会话清理接口但你可以通过定期重启机器人服务来释放资源。持久化的session_id映射关系不受重启影响。安全边界这是最重要的。请务必意识到这个机器人拥有运行它的系统用户的所有权限。避免在不受信任的群组中使用或者至少要通过飞书的消息权限设置限制只有特定成员才能机器人。不要在对话中要求它执行rm -rf /这类危险命令虽然 Claude 通常有安全机制拒绝但不要冒险。6. 扩展与自定义打造你自己的专属 Agent本项目的架构之美在于其“可拔插”的设计。Claude Code只是一个默认的后端实现。如果你想换成其他 AI 助手比如 OpenAI 的 GPT-4、开源的 Llama 通过 Ollama 部署、甚至是你自己训练的模型都非常简单。6.1 如何替换为其他 AI 后端假设你想用 OpenAI 的官方 API。创建新的 Agent 模块在src目录下新建一个文件夹例如openai_agent在里面创建client.py。实现统一的接口在client.py中实现一个同名的chat_sync函数。# src/openai_agent/client.py import openai from typing import Optional # 配置你的 OpenAI API Key (建议从环境变量读取) openai.api_key os.getenv(OPENAI_API_KEY) # 一个简单的全局会话存储生产环境建议用数据库 session_conversations {} def chat_sync(message: str, session_id: str None) - tuple[str, str]: 模拟与OpenAI API的交互。 为了保持上下文这里简单地将一个session_id下的所有对话内容拼接起来。 if session_id is None: session_id str(uuid.uuid4()) # 生成新会话ID session_conversations[session_id] [] # 将本次用户消息加入历史 session_conversations[session_id].append({role: user, content: message}) try: response openai.ChatCompletion.create( modelgpt-4, # 或 gpt-3.5-turbo messagessession_conversations[session_id], temperature0.7, ) assistant_reply response.choices[0].message.content # 将助手回复也加入历史以便下次对话保持上下文 session_conversations[session_id].append({role: assistant, content: assistant_reply}) return assistant_reply, session_id except Exception as e: return fOpenAI API调用失败{e}, session_id修改主程序的路由打开src/main_websocket.py找到导入claude_code的地方替换成你的新模块。# 注释掉原来的导入 # from src.claude_code import chat_sync # 改为导入你的 OpenAI Agent from src.openai_agent import chat_sync配置环境变量在.env文件中加入OPENAI_API_KEYsk-...。更新依赖在requirements.txt中加入openai库并重新安装。重启服务后你的机器人后端就从操作本地的 Claude Code变成了调用云端 OpenAI API 的对话助手。它失去了本地操作能力但获得了更强大的通用对话和推理能力。6.2 扩展思路混合代理与工具调用更进一步你可以打造一个更强大的“混合代理”路由分发在main_websocket.py的process_core_logic函数中根据消息内容决定使用哪个后端。例如如果消息以“执行”开头则路由到 Claude Code如果是普通问答则路由到 OpenAI。集成 LangChain利用 LangChain 丰富的工具链如网页搜索、数据库查询、自定义函数打造一个能处理复杂工作流的超级助手。你可以让飞书机器人调用 LangChain Agent由 Agent 去决定使用哪个工具来回答问题。接入本地模型使用ollama在本地运行 Llama 3 等开源模型然后将chat_sync函数指向本地的 Ollama API 端点。这样数据完全不出内网安全性更高。7. 常见问题与故障排查实录在实际搭建和运行过程中你可能会遇到以下问题。这里是我踩过坑后的解决方案汇总。7.1 机器人收不到消息症状在飞书里机器人说话服务日志没有任何反应。排查步骤检查飞书应用配置这是最常见的原因。请确保“事件订阅”中已正确添加im.message.receive_v1事件。“事件订阅”的“请求地址配置”选择的是“使用长连接接收事件”而不是 URL。应用已成功“发布”并“启用”。检查权限在“权限管理”中确认im:message等相关权限已“申请发布”并显示为“已生效”。检查机器人是否在会话中确认你已经在群聊或私聊中成功添加了该机器人应用。检查服务日志运行python -m src.main_websocket时启动成功后应该会打印连接成功的日志。如果连接失败日志会报错通常是 App ID/Secret 错误。检查环境变量确认.env文件中的APP_ID和APP_SECRET填写正确且没有多余的空格或换行。7.2 Claude Code 无响应或报错症状机器人收到消息但日志显示调用 Claude Code 超时或失败。排查步骤验证 Claude Code 本地安装在命令行直接运行claude hello看是否能正常回复。如果不能说明 Claude Code 安装或登录有问题。检查 Claude Code 守护进程在 macOS/Linux 上可以运行ps aux | grep claude查看相关进程是否在运行。有时守护进程可能意外退出。重新登录尝试运行claude login重新进行授权。查看详细错误项目代码中捕获了 Claude Code SDK 的异常并打印到日志。根据日志中的具体错误信息如连接拒绝、超时进行搜索。7.3 会话上下文混乱或丢失症状机器人好像失忆了不记得刚才的对话。排查步骤检查数据库文件确认data/sessions.db文件存在且可读写。服务启动时会在data目录创建它。检查会话存储逻辑确保SessionStore类正确地在每次对话后保存了chat_id和session_id的映射。你可以使用 SQLite 命令行工具查看sessions表的内容。确认 chat_id 提取正确在飞书事件回调中确保从事件对象中提取的chat_id是稳定且唯一的。私聊和群聊的chat_id格式不同但都应该是唯一的。7.4 服务运行一段时间后自动断开症状机器人运行几小时或几天后不再响应消息。排查步骤网络问题长连接对网络稳定性有要求。检查服务器或本地电脑的网络是否波动。飞书端主动断开飞书服务器可能会定期清理不活跃的长连接。lark-oapiSDK 应已实现心跳机制但极端网络情况下可能失效。查看日志是否有重连记录。进程被杀死如果是后台运行检查系统日志看是否因为内存、CPU 占用过高被系统或监控脚本终止。可以考虑使用supervisor来守护进程崩溃后自动重启。7.5 性能优化与安全建议异步化确保所有耗时的 IO 操作网络请求、文件读写、Claude 调用都使用异步方式或放入线程池不要阻塞主事件循环这是保证机器人响应速度的关键。指令白名单如果你对安全性要求极高可以在消息处理层增加一个过滤逻辑。例如只允许执行特定目录下的操作或者禁止包含rm、format、sudo等危险关键词的命令。这需要解析用户消息可以在调用 Claude Code 之前进行。访问控制飞书应用后台可以设置“可用范围”将机器人仅对特定部门或人员可见。这是最直接有效的安全控制。这个项目把强大的本地 AI 执行能力和便捷的企业 IM 结合了起来打开了很多自动化办公的新思路。从我自己的使用体验来看它最适合的场景是个人效率工具和小型技术团队的内网助手。对于更复杂的企业级流程可能需要在此基础上增加审批流、权限管理等模块。希望这份详细的拆解能帮你顺利搭建起自己的“数字同事”。如果在实践过程中遇到新的问题不妨多看看日志那通常是寻找答案的第一线索。