资讯详情 基于MCP协议的轻量级团队智能体操作系统
📅 2026/10/8 10:46:49
1. 项目概述这不是一个 Slack 插件而是一套“团队智能体操作系统”的最小可行原型“GitHub 今日推荐company-brain在 Slack 里给团队装个会主动干活的 AI”——这个标题里藏着三个关键信号GitHub是它的开源载体和信任背书Slack是它扎根的真实协作场景而“会主动干活”这五个字才是它区别于市面上99%所谓“AI助手”的本质分水岭。我拆过不下二十个标榜“AISlack”的项目绝大多数只是把 ChatGPT 的 API 封装成 /ask 命令用户不问它就不动用户一问它就胡说。而 company-brain 的设计哲学恰恰相反它默认是静默的、观察的、预判的。它不等你发指令而是自己判断“现在该做什么”然后把结果推到你该看到的地方。比如销售晨会刚结束它自动汇总昨日所有客户沟通记录标出三个高风险流失线索并把对应客户的最新财报摘要、竞品动态和内部CRM备注打包发到销售主管的私聊窗口——整个过程你甚至没打一个字。这背后不是简单的 webhook 触发而是基于MCPModel Control Protocol构建的轻量级智能体调度层在 Cloudflare Workers 上运行把 Slack 从一个通讯工具变成了团队任务流的神经中枢。它不替代人做决策但把人从信息搬运工的角色里彻底解放出来。适合正在被“消息爆炸信息孤岛”压得喘不过气的中小型产品/运营/销售团队尤其适合那些已经用上 Notion、Linear、Jira 但数据散落各处、每天花两小时手动同步的团队。如果你的 Slack 频道里还充斥着“谁有XX文档”“上次讨论的结论在哪”“这个需求状态更新了吗”那 company-brain 就不是锦上添花而是止血绷带。2. 整体架构设计与核心思路拆解为什么放弃“大模型直连”选择 MCP Workers 的轻量组合2.1 拒绝“大模型直连 Slack”的三大硬伤我最早试跑 company-brain 时第一反应是“这玩意儿肯定要配 GPT-4 或 Claude 3 的 API Key 吧”结果发现它根本没走 OpenAI 官方接口。原因很实在延迟、成本、可控性三座大山。我们来算一笔账。假设一个 50 人的团队平均每人每天触发 3 次 AI 查询每次查询平均消耗 800 token含 system prompt context按 GPT-4 Turbo 当前 $0.01/1K input tokens 计算单日成本就是 50 × 3 × 0.8 × 0.01 $1.2一个月就是 $36。这还只是输入如果加上输出 token通常为输入的 1.5 倍成本直接翻倍。更致命的是延迟——Slack 的 slash command 有 3 秒超时限制而大模型生成一次复杂分析网络抖动排队推理很容易卡在 2.8 秒用户看到的就是“应用超时”。最后是可控性你无法精确控制大模型在什么条件下调用哪个工具、以什么格式返回结果。它可能把 CRM 数据用 Markdown 表格返回而 Slack 的 block kit 渲染器根本不认这种格式结果就是一团乱码。所以 company-brain 的第一道设计铁律就是绝不让大模型成为 Slack 消息流的直接生产者。2.2 MCP给 AI 装上“操作手册”和“执行引擎”MCPModel Control Protocol在这里不是玄学概念而是一个极简的、面向任务的协议层。你可以把它理解成给 AI 下达指令的“军令状”。它规定了三件事能调用什么工具Tools、工具返回什么格式Schema、下一步怎么走Routing Logic。举个真实例子当检测到 #sales 频道出现 “客户 A 可能要砍单” 这类关键词时company-brain 不是让大模型去“分析客户 A 的风险”而是按 MCP 协议依次执行调用crm_fetch_contact工具传入参数contact_id: A123要求返回 JSON 格式必须包含last_contact_date,contract_renewal_date,open_opportunities_count字段调用notion_query_db工具传入database_id: sales_notes,filter: {property: Contact ID, equals: A123}要求返回最近 3 条笔记的content和created_time将两个工具返回的结构化数据喂给一个轻量级本地 LLM如 Phi-3-mini仅 3.8B 参数可在 Workers 上跑让它只做一件事根据预设规则模板生成一段不超过 200 字的、带明确行动项的摘要。这个模板长这样“客户 {{contact.name}}ID: {{contact.id}}存在流失风险。最后联系时间{{contact.last_contact_date}}距今 {{days_since_last}} 天合同到期日{{contact.contract_renewal_date}}当前未关闭商机{{contact.open_opportunities_count}} 个最新内部记录{{notes[0].content}}{{notes[0].created_time}}▶️ 建议动作今日内由 {{owner}} 主动联系重点确认 {{focus_point}}。”你看大模型在这里只负责“填空”和“润色”所有关键数据、逻辑判断、格式约束都由 MCP 协议和上游工具严格定义。这带来的好处是结果可预测、可审计、可回滚。今天换用 Llama3明天换用 Gemma2只要它们能按 MCP 的 schema 输出整个流程完全不受影响。2.3 Cloudflare Workers为什么选它而不是 AWS Lambda 或 Vercel选 Workers 的理由非常务实冷启动零延迟、全球边缘节点、免费额度够用、与 Slack 的 webhook 天然契合。Slack 的事件推送如新消息、频道创建是 HTTP POST 请求Workers 的fetch事件监听器天生就是为这种短时、高并发、无状态的请求设计的。AWS Lambda 虽然强大但冷启动平均 200-500ms对于需要秒级响应的 Slack 交互来说就是“卡顿”的代名词。Vercel 的 Edge Functions 也不错但它对第三方服务如 Notion API的 outbound 连接有更严格的速率限制而 company-brain 的核心工作流往往需要在 1 秒内串行调用 CRM、Notion、邮件系统三个 API。Workers 的免费计划每月提供 10 万次请求和 10 万 CPU 秒对于中小团队完全够用。更重要的是它的 KV 存储键值对和 D1 数据库SQLite on edge可以无缝集成用来缓存用户偏好、存储任务执行日志、维护一个轻量级的“团队知识图谱”比如自动记录“张三负责客户 A 的续约谈判”这些能力在 Lambda 上要么得额外搭 Redis要么得忍受 S3 的高延迟。一句话Workers 不是炫技而是用最省事的方式把“实时响应”这件事做成了一件确定性极高的事情。3. 核心模块解析与实操要点从 Slack 事件捕获到智能体决策的完整链路3.1 Slack 事件订阅与意图识别如何让 AI “听懂”团队在聊什么company-brain 的入口是 Slack 的 Events API。它不像传统 bot 那样只监听/command而是订阅了message.channels,reaction_added,user_status_changed等十余种事件。但关键不在于“听得多”而在于“听得准”。它采用两级过滤机制第一级规则引擎Rule Engine这是纯代码逻辑不依赖任何模型。它扫描每条消息的文本、发送者、频道、时间戳用正则和简单条件判断是否值得进入 AI 流程。例如在 #dev 频道消息包含deployprodfailed→ 触发告警分析流程在 #marketing 频道消息包含channeldeadlinetomorrow→ 触发任务提醒流程用户状态变为away且其所属频道有未读 mention 消息超过 3 条 → 触发代理响应流程。这套规则写在rules.ts里用 TypeScript 的switch语句实现执行速度在微秒级。它的价值是把 95% 的无效消息比如“收到”、“好的”、“哈哈”直接挡在门外避免无谓的 AI 调用。第二级轻量级分类模型Tiny Classifier只有通过规则引擎的消息才会被送入一个 12MB 的 ONNX 格式小模型基于 DistilBERT 微调。它不生成文字只做三分类Urgent,Informational,Procedural。训练数据来自团队过去三个月的 Slack 归档标注标准很朴素Urgent是需要 2 小时内响应的如故障、客户投诉Informational是需要归档或同步的如会议纪要、政策更新Procedural是需要执行某个步骤的如审批、发布、同步。这个模型部署在 Workers 的aibinding 上推理耗时稳定在 80ms 内。分类结果决定了后续 MCP 的路由路径Urgent走高优先级队列Procedural走自动化执行流Informational则进入知识库索引流程。这里有个实操心得不要试图用一个大模型解决所有问题。把“判断该不该做”和“具体怎么做”拆开前者用规则小模型后者用 MCP大模型效率和稳定性天差地别。3.2 MCP 工具注册与编排如何让 AI “知道”自己能调用哪些服务MCP 的核心是tools.json文件它定义了所有可用工具的元数据。company-brain 默认内置了 7 个工具全部遵循统一 schema{ name: jira_search_issues, description: Search Jira issues by JQL query. Returns issue key, summary, status, and assignee., parameters: { type: object, properties: { jql: { type: string, description: Valid Jira Query Language string } }, required: [jql] }, return_schema: { type: array, items: { type: object, properties: { key: {type: string}, summary: {type: string}, status: {type: string}, assignee: {type: string} } } } }关键点在于return_schema。它强制规定了工具返回的数据结构确保下游的 LLM 永远不会拿到一个“字段名不一致”或“类型错误”的 JSON。比如jira_search_issues返回的assignee必须是字符串如果 Jira API 实际返回的是一个嵌套对象{displayName: 张三, email: zhangxxx.com}那么 tool wrapper 层jira_tool.ts就必须在返回前做清洗只取displayName。这个清洗逻辑是硬编码的不是靠大模型“猜”。实操中我遇到的最大坑是 Notion API 的rich_text字段——它返回的是一个数组每个元素是{type: text, text: {content: xxx}}但我们的 schema 要求content是字符串。解决方案是在notion_tool.ts里加一行content: richText.map((t) t.text?.content || ).join()。记住MCP 的力量不在于它多智能而在于它多“死板”。越死板越可靠。3.3 智能体决策与执行如何让 AI “决定”下一步该做什么决策层是agent.ts它接收经过分类和工具调用后的上下文生成最终的 Slack 消息。这里的关键不是“生成”而是“决策”。company-brain 使用了一个叫ReActReasoning Acting的轻量框架但做了大幅简化Reason推理LLM 接收当前上下文如 CRM 数据、Notion 笔记、Slack 原始消息输出一个 JSON 对象只包含两个字段{ action: send_message_to_user, params: { user_id: U123456, blocks: [/* Slack block kit array */] } }注意它不生成自然语言只输出结构化的 action 和 params。这个 JSON 的 schema 是固定的由 MCP 的agent_schema.json定义。Act执行Workers 的 runtime 解析这个 JSON调用对应的send_message_to_user函数把blocks数组直接 POST 到 Slack 的chat.postMessageAPI。如果action是create_task_in_linear就调用 Linear 的 GraphQL API 创建任务。这个设计的好处是完全规避了“幻觉”。LLM 永远不会编造一个不存在的user_id也不会把blocks数组错写成字符串。它的唯一任务就是在几个预设的action中选出最合适的一个并填好参数。我们测试过用 Phi-3-mini 在这个任务上的准确率是 99.2%而用 GPT-4 做同样任务因为自由度太高反而会出现 3% 的格式错误比如少了个逗号导致 JSON 解析失败。在工程落地中“能力边界清晰”比“能力上限高”重要一百倍。4. 实操部署与配置详解从零开始30 分钟完成你的团队 AI 大脑4.1 前置准备Slack App 创建与权限配置这不是点几下鼠标就能完的事关键权限必须一步到位否则后面全白搭。登录 Slack API 管理后台 点击 “Create New App”选择 “From scratch”App Name 填company-brainDevelopment Slack Workspace 选你的团队 workspace。最关键的四步权限设置缺一不可Bot Token Scopes添加chat:write,channels:read,groups:read,im:read,mpim:read,reactions:read,users:read,users:read.email。其中chat:write是发送消息的权限users:read.email是为了后续做邮箱匹配比如把 Slack 用户和 CRM 用户关联。Event Subscriptions开启 Events APIRequest URL 填你 Workers 的地址如https://your-app.pages.dev/api/slack然后订阅以下事件message.channelsmessage.groupsreaction_addeduser_status_changedteam_join用于新成员欢迎流程OAuth Permissions在 “Install to Workspace” 页面点击 “Install App to Workspace”授权。安装后你会得到一个Bot User OAuth Token以xoxb-开头这是你的 bot 的身份凭证务必保存。Interactive Components在 “Interactivity Shortcuts” 页面开启 “Interactivity”Request URL 同样填 Workers 地址。这是为了支持按钮点击、下拉菜单等交互比如在 AI 发送的汇总消息里放一个 “查看详情” 按钮。提示Slack 的权限模型很细粒度。如果你漏了im:readbot 就看不到私聊消息漏了reactions:read它就无法感知用户对某条消息点了 也就无法触发“这条建议被认可自动归档”的逻辑。我第一次部署时就卡在这一步花了 40 分钟才排查出来。4.2 Cloudflare Workers 配置环境变量与 KV 初始化克隆官方仓库后进入项目根目录运行npm install npm run build。构建产物在dist/目录。接下来是 Workers 的核心配置环境变量Environment Variables在 Cloudflare Dashboard 的 Workers 页面找到你的 Worker进入 “Variables” 标签页添加以下键值对SLACK_BOT_TOKEN: 你上一步拿到的xoxb-开头的 tokenSLACK_SIGNING_SECRET: 在 Slack App 的 “Basic Information” “App Credentials” 里找到这是验证 Slack 请求真伪的密钥防止恶意伪造NOTION_INTEGRATION_TOKEN: Notion 的 integration token需在 Notion 的 “Settings Members” “Integrations” 中创建CRM_API_KEY: 你公司 CRM如 HubSpot、Salesforce的 API KeyMCP_TOOLS_CONFIG: 这是一个 JSON 字符串内容就是你修改过的tools.json确保所有工具的 endpoint 和 auth 都已正确填写KV 存储初始化在 “Storage” “KV” 标签页创建一个新的 KV namespace命名为COMPANY_BRAIN_KV。然后在wrangler.toml文件里将kv_namespaces配置指向它kv_namespaces [ { binding COMPANY_BRAIN_KV, id your-kv-id-here } ]这个 KV 用来存三类东西user_preferences用户个性化设置比如张三不想接收非紧急通知、task_execution_log每次 AI 执行任务的 timestamp 和 result、knowledge_graph团队成员技能标签如{U123456: [python, sql, aws]}。初始化时你可以用一个简单的curl命令批量写入curl -X PUT https://api.cloudflare.com/client/v4/accounts/YOUR_ACCOUNT_ID/storage/kv/namespaces/YOUR_KV_ID/values/user_preferences \ -H Authorization: Bearer YOUR_API_TOKEN \ -H Content-Type: application/json \ -d {U123456: {notify_urgent_only: true}}4.3 MCP 工具连接与调试如何验证每个工具都能正常工作部署完 Workers 后别急着用先做工具连通性测试。company-brain 提供了一个/api/debug/tool的端点你可以用 curl 直接调用curl -X POST https://your-app.pages.dev/api/debug/tool \ -H Content-Type: application/json \ -d { tool_name: jira_search_issues, params: {jql: project PROD AND status \In Progress\ ORDER BY updated DESC LIMIT 1} }预期返回应该是一个长度为 1 的数组包含key,summary,status,assignee四个字段。如果返回{error: Unauthorized}说明你的 Jira API Key 没配对如果返回空数组说明 JQL 语法有误。我强烈建议你为每个工具都写一个这样的测试脚本放在scripts/test_tools.sh里每次更新工具配置后都跑一遍。这个习惯帮你省下了至少 20 小时的线上排查时间。另外注意工具的 rate limit。Jira Cloud 默认是 1000 次/小时而 company-brain 的 MCP 调度器会在每次调用前检查 KV 里的rate_limit_counter如果 1 小时内已调用 950 次它会自动降级改用缓存数据或返回提示“Jira 查询已达上限稍后再试”。这个降级策略写在mcp_scheduler.ts的throttleToolCall函数里你可以根据自己的服务调整阈值。5. 常见问题与排查技巧实录那些官方文档里永远不会写的坑5.1 Slack 消息“发出去了但用户没收到”90% 是时区和权限问题现象你在 Logs 里看到chat.postMessage返回ok: true但目标用户就是收不到消息。别怀疑代码先查两件事用户是否开启了“免打扰”Do Not DisturbSlack 的 API 文档里有一句不起眼的话“Messages sent to users in DnD mode will be delivered when DnD ends.” 意思是bot 发的消息也会被 DnD 挡住。解决方案是在发送前先调用users.getPresenceAPI 获取用户状态如果presence是away或offline就改发到其imchannel私聊而不是直接chat.postMessage到用户 ID。这个逻辑在send_message_to_user.ts的ensureDelivery函数里有实现但默认是关闭的你需要把ENABLE_DND_FALLBACK环境变量设为true。用户是否在 Slack 的 “Notifications” 设置里关闭了 “Direct messages from apps”这个开关藏得很深Settings PreferencesNotificationsApps integrationsDirect messages from apps。很多用户为了清净会把它关掉。此时bot 发的私聊消息只会出现在聊天列表里但不会有弹窗、声音、手机推送。解决办法是在首次安装 bot 时发送一条引导消息用blocks的button元素链接到 Slack 的通知设置页面{ type: section, text: {type: mrkdwn, text: 请开启应用通知确保不错过重要提醒}, accessory: { type: button, text: {type: plain_text, text: 立即设置}, url: https://slack.com/intl/zh-cn/help/articles/206810008-Notification-settings } }5.2 MCP 工具返回数据“字段缺失”不是 API 问题是 schema 定义太松现象crm_fetch_contact工具有时返回last_contact_date字段为空导致 LLM 生成的摘要里出现 “最后联系时间undefined”。你查 CRM API 文档发现这个字段确实是可选的。问题根源在于tools.json里你把last_contact_date的 schema 写成了type: string但没加nullable: true。MCP 的 validator 在解析时会认为这个字段必须存在于是把整个返回体丢弃用一个空对象代替。解决方案有两个推荐方案在tools.json里为所有可能为空的字段显式声明nullable: true并确保 tool wrapper 层crm_tool.ts在构造返回对象时对空值做兜底const lastContactDate contact.lastActivityDate || 从未联系; return { last_contact_date: lastContactDate, ... };备选方案在 MCP 的validateToolResponse函数里加入容错逻辑如果某个 required 字段缺失就用预设的默认值如N/A填充而不是直接报错。这个修改在mcp_core.ts的第 237 行把throw new Error(...)改成result[field] N/A;。注意这个坑我踩了三次。第一次以为是 CRM 数据问题花了两天查数据库第二次以为是 Workers 缓存问题清了十几次 cache第三次才意识到是 schema 定义和 validator 的耦合逻辑。永远假设你的工具返回是“脏”的MCP 的职责不是相信它而是驯服它。5.3 Workers CPU 超时“Execution exceeded 10ms” 错误的终极解法现象在处理一条包含大量附件的 Slack 消息时Workers 报错Execution exceeded 10ms。这不是代码慢而是 Cloudflare 的 CPU 时间限制。Workers 的免费计划单次请求 CPU 时间上限是 10msPro 计划是 50ms。而解析一个 5MB 的 PDF 附件用pdf-lib库轻松就超时。官方文档建议你用 R2 存储但这会增加架构复杂度。我的实操解法是用 WebAssemblyWASM重写耗时操作。company-brain 的pdf_extractor.wasm模块就是一个用 Rust 编译的 WASM 二进制文件专门做 PDF 文本提取。它体积只有 120KB加载快执行快CPU 占用极低。在pdf_tool.ts里调用方式如下const wasmModule await import(../wasm/pdf_extractor); const text await wasmModule.extractText(pdfBytes);Rust 的pdf-extractcrate 在 WASM 下提取一页 PDF 的文本平均耗时 1.2ms。而 JavaScript 版本的pdfjs-dist同样操作要 8ms。对于所有 CPU 密集型任务图像 OCR、音频转录、大文件解析WASM 不是可选项而是必选项。我把团队所有类似的工具都重写了 WASM 版本CPU 超时错误从此消失。6. 进阶扩展与定制化如何让你的 company-brain 真正长出“公司大脑”的神经突触6.1 构建团队专属知识图谱让 AI 记住“谁最懂什么”company-brain 默认的知识库是静态的docs/目录下的 Markdown 文件。但这远远不够。真正的“公司大脑”应该能动态学习。我们在knowledge_graph.ts里实现了一个轻量级图谱构建器它有三个数据源Slack 互动数据扫描所有user的消息统计“A 经常 B 问技术问题”就给边A-B加一个expertise: backend的权重Git 提交记录通过 GitHub API获取团队成员的 PR 关联的文件路径如果张三 80% 的 PR 都修改src/services/payment/目录就给他打上payment_system标签Notion 个人主页很多团队在 Notion 里有员工技能表我们用一个定时 Worker每小时跑一次同步这些字段到 KV 的knowledge_graphnamespace。图谱构建后AI 在决策时就能调用find_expert工具。比如当检测到一条消息 “支付回调失败怎么排查”MCP 的路由逻辑就会调用find_expert参数topic: payment_callback工具返回[U123456, U789012]张三和李四LLM 生成消息“支付回调问题请 张三 李四 协助排查。”这个图谱不是完美的但它在持续进化。上线三个月后它的推荐准确率从最初的 62% 提升到了 89%。知识图谱的价值不在于它有多精确而在于它让 AI 的每一次“指派”都带着团队真实的协作记忆。6.2 多 AI 协作流水线让不同模型各司其职而非“一模到底”标题里的“多 AI 协作”热词不是噱头。company-brain 的 MCP 层天然支持多模型协同。我们目前的流水线是Phi-3-mini负责所有决策ReAct、摘要生成、模板填充。它小、快、便宜是整个流水线的“交通指挥员”Whisper.cppWASM负责语音消息转文字。Slack 的语音消息上传后Workers 调用它100ms 内返回文字稿Stable Diffusion XLvia Replicate API负责生成报告封面图。当 AI 自动产出一份周报时它会调用此工具用提示词professional tech report cover, blue theme, clean layout生成一张图作为 Slack 消息的附件。关键点在于每个模型只做它最擅长的一件事且它们之间通过 MCP 的 JSON schema 通信完全解耦。你想把 Whisper 换成 Google Speech-to-Text只需改whisper_tool.ts里的 endpoint 和 response parser其他模块完全不用动。这种“乐高式”架构让升级和替换变得像换电池一样简单。我上周刚把 Phi-3-mini 换成了 Qwen2-1.5B只改了 3 行代码整个系统就完成了平滑升级。6.3 安全审计与合规红线如何确保你的 AI 不越界最后也是最重要的。AI 再聪明也必须守规矩。company-brain 内置了三层安全网输入过滤层Input Sanitization所有 Slack 消息在进入 MCP 前都会经过sanitizeMessage函数。它用正则删除所有script标签、javascript:协议、以及超过 1000 字符的 base64 编码字符串防 XSS 和大 payload 攻击工具沙箱层Tool Sandboxing每个工具的调用都在一个独立的fetch调用中完成且 timeout 设为 5s。如果 Jira API 卡死不会拖垮整个 Worker输出审查层Output ModerationLLM 生成的最终消息在发送前会经过一个基于规则的审查器。它检查是否包含敏感词如password,token,secret是否引用了未授权的外部链接只允许公司域名和预设白名单是否有超过 3 个连续感叹号防情绪化表达。这个审查器不是用大模型做的而是用一个 50 行的正则和字符串匹配函数。它快、准、无副作用。在 AI 应用里最可靠的风控永远是“简单粗暴”的规则而不是“聪明复杂”的模型。我在实际使用中发现真正让 team 成员从怀疑到依赖的不是它多能干而是它多“守规矩”。当大家知道AI 永远不会把 CRM 里的手机号发到公开频道也不会把未发布的财报截图发给实习生信任感就建立了。这个过程比任何技术指标都重要。