langgraph-plugin-wecom
v1.0.0
Published
LangGraph adaptor plugin for Enterprise WeChat (WeCom) AI Bot / Agent API / Webhook integration
Maintainers
Readme
langgraph-plugin-wecom
把企业微信(WeCom)的通信能力无缝接入你的 LangGraph Agent。支持智能机器人 WebSocket 长连接、自建应用 Agent API、群机器人 Webhook 三大通信方式,零业务耦合,即插即用。
本项目从 openclaw-plugin-wecom 复用其企业微信通信实现,剥离了 OpenClaw 的 channel/runtime/plugin SDK 耦合,作为 LangGraph Agent 的企业微信传输层 独立发布。
特性
- AI Bot 智能机器人 — 基于
wss://openws.work.weixin.qq.com长连接,自动重连 / 心跳维持 / 消息去重 - 流式推理输出 — 支持
sendReasoningStream,在企微端实时展示模型的思考过程(reasoning)和最终回复 - Agent API 自建应用 — 主动发送文本 / markdown / 图片 / 语音 / 视频 / 文件
- Webhook 群机器人 — 群通知 / 监控告警一键发送
- LangGraph Tool 封装 —
createWecomTools()一行生成 LLM 可调用的企微工具集 - 零业务耦合 — 不绑定任何 Agent 框架,StateGraph / MessageGraph / 自定义 Agent 均可使用
- TypeScript 优先 — 完整类型定义,开发体验友好
- 代理友好 — 配置
proxyUrl即可走 HTTP 代理出向(需安装undici) - 完整测试覆盖 — 基于 Node.js 内置
node:test,62 个测试用例全部通过
安装
npm install langgraph-plugin-wecom
# 可选:HTTP 代理支持
npm install undiciNode.js 要求:>= 18(使用原生 fetch / AbortSignal)
快速开始
准备工作
在企业微信管理后台获取以下凭证:
| 凭证 | 用途 | 必需 |
| --- | --- | --- |
| 智能机器人 botId | 智能机器人 WebSocket 连接 | ✅ |
| 智能机器人 secret | 智能机器人 WebSocket 连接 | ✅ |
| 自建应用 corpId / corpSecret / agentId | Agent API 主动发送 | ❌(按需) |
| 群机器人 webhook URL | 群消息推送 | ❌(按需) |
基础示例:接入 DeepSeek 智能体
import { WecomPlugin } from "langgraph-plugin-wecom";
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph, MessagesAnnotation, START } from "@langchain/langgraph";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
// 1. 创建 DeepSeek LLM
const model = new ChatOpenAI({
model: "deepseek-chat",
apiKey: process.env.DEEPSEEK_API_KEY,
configuration: { baseURL: "https://api.deepseek.com" },
});
// 2. 构建 LangGraph Agent
const agent = new StateGraph(MessagesAnnotation)
.addNode("call_model", async (state) => ({
messages: [await model.invoke([
new SystemMessage("你是一个智能助手。"),
...state.messages,
])],
}))
.addEdge(START, "call_model")
.compile();
// 3. 接入企业微信
const plugin = new WecomPlugin({
botId: process.env.WECOM_BOT_ID!,
secret: process.env.WECOM_SECRET!,
});
plugin.onMessage(async (message, reply) => {
if (!message.text) return;
const result = await agent.invoke({
messages: [new HumanMessage(message.text)],
});
const content = result.messages[result.messages.length - 1].content;
await reply.sendText(message.fromUserid, String(content));
});
await plugin.connect();运行:
DEEPSEEK_API_KEY=sk-xxx WECOM_BOT_ID=xxx WECOM_SECRET=xxx npx tsx my-bot.ts流式示例:逐 token 展示思考和回复
plugin.onMessage(async (message, reply) => {
if (!message.text || !message.fromUserid) return;
const streamId = plugin.newStreamId();
const stream = await model.stream([
new SystemMessage("你是一个智能助手。"),
new HumanMessage(message.text),
]);
let fullContent = "";
for await (const chunk of stream) {
const content = chunk.content;
if (typeof content === "string" && content) {
fullContent += content;
// 每 1.5 秒推送一次到企微
await reply.sendReasoningStream(streamId, {
visibleText: fullContent,
finish: false,
});
}
}
await reply.sendReasoningStream(streamId, {
visibleText: fullContent,
finish: true,
});
});关于思考过程:DeepSeek 的推理模型(如
deepseek-reasoner)通过 API 的reasoning_content字段返回思考过程,本插件的sendReasoningStream会自动用<think>标签包装推理内容,在企微端呈现思考→回答的完整流程。详见 examples/deepseek-agent.ts。
主动发送(Agent API)
const plugin = new WecomPlugin({
botId: process.env.WECOM_BOT_ID,
secret: process.env.WECOM_SECRET,
agent: {
corpId: process.env.AGENT_CORP_ID,
corpSecret: process.env.AGENT_CORP_SECRET,
agentId: process.env.AGENT_ID,
},
});
// 给用户发送 markdown 消息
await plugin.agentApi.sendText({
toUser: "UserID",
text: "**Hello** from LangGraph",
format: "markdown",
});Webhook 群通知
await plugin.webhook.sendMarkdown({
url: process.env.WECOM_WEBHOOK_URL,
content: "**告警**:服务异常,请及时处理。",
});LangGraph Tool 集成(LLM 自主调用)
import { WecomPlugin, createWecomTools } from "langgraph-plugin-wecom";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
const plugin = new WecomPlugin({ botId, secret, agent: { ... } });
const tools = createWecomTools({ plugin });
const agent = createReactAgent({ llm, tools });LLM 现在可以自主调用 wecom_active_send_text、wecom_send_text、wecom_webhook_send_markdown 等工具。
API 参考
new WecomPlugin(config)
| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| botId | string | ✅ | 智能机器人 Bot ID |
| secret | string | ✅ | 智能机器人 Secret |
| agent | { corpId, corpSecret, agentId } | ❌ | 自建应用凭证 |
| apiBaseUrl | string | ❌ | Agent API 基础地址(默认 https://qyapi.weixin.qq.com) |
| proxyUrl | string | ❌ | HTTP 代理地址(需安装 undici) |
| wsUrl | string | ❌ | 自定义 WebSocket 地址 |
| autoReconnect | boolean | ❌ | 自动重连(默认 true) |
| maxReconnectAttempts | number | ❌ | 最大重连次数 |
| debug | boolean | ❌ | 调试日志 |
plugin.onMessage(handler)
plugin.onMessage(async (message, reply) => {
// message: { msgid, chatid, chattype, fromUserid, msgtype, text, image, ... }
// reply: { sendText, sendStream, sendReasoningStream, sendMessage }
});reply.sendText(userid, content)
发送 markdown 文本回复。
reply.sendStream(streamId, text, finish)
发送流式消息帧(需要自行组装 <think> 标签)。
reply.sendReasoningStream(streamId, options)
发送推理流式消息,自动处理 <think> 标签封装:
// 推理中(<think> 不闭合)
await reply.sendReasoningStream(streamId, {
reasoningText: "正在分析问题...",
finish: false,
});
// 推理完成 + 最终回复(<think> 自动闭合)
await reply.sendReasoningStream(streamId, {
reasoningText: "分析完成",
visibleText: "这是最终回复",
finish: true,
});reply.sendMessage(chatId, content)
同 sendText,语义别名。
plugin.sendText(userid, content)
智能机器人主动发送 markdown 消息。
plugin.sendStream(frame, streamId, text, finish)
主动发送流式消息帧。
plugin.newStreamId(prefix?)
生成流式消息 ID。
plugin.agentApi.*
| 方法 | 说明 |
| --- | --- |
| sendText({ toUser?, toParty?, toTag?, chatId?, text, format? }) | 发送文本 / markdown |
| sendImage({ ... , mediaId }) | 发送图片 |
| sendFile({ ... , mediaId }) | 发送文件 |
| sendVoice({ ... , mediaId }) | 发送语音 |
| sendVideo({ ... , mediaId }) | 发送视频 |
| uploadMedia({ type, buffer, filename }) | 上传临时素材 |
| downloadMedia({ mediaId }) | 下载临时素材 |
plugin.webhook.*
| 方法 | 说明 |
| --- | --- |
| sendText({ url, content, mentionedList?, mentionedMobileList? }) | 群机器人发文本 |
| sendMarkdown({ url, content }) | 群机器人发 markdown |
| sendImage({ url, base64, md5 }) | 群机器人发图片 |
| sendFile({ url, mediaId }) | 群机器人发文件 |
| uploadFile({ url, buffer, filename }) | 上传文件获取 media_id |
buildStreamContent(options)
独立工具函数,用于自行组装推理内容:
import { buildStreamContent } from "langgraph-plugin-wecom";
buildStreamContent({ reasoningText: "思考", visibleText: "回答", finish: true });
// → "<think>思考</think>\n回答"createWecomTools({ plugin, enable? })
| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| plugin | WecomPlugin | — | 插件实例 |
| enable.agentApi | boolean | true | 是否生成 Agent API 工具 |
| enable.webhook | boolean | true | 是否生成 Webhook 工具 |
| enable.activeSend | boolean | true | 是否生成主动发送工具 |
| enable.uploadMedia | boolean | true | 是否生成上传素材工具 |
生成的 Tool 列表:wecom_active_send_text、wecom_send_text、wecom_send_media_file、wecom_webhook_send_text、wecom_webhook_send_markdown、wecom_webhook_send_image。
testAIBotConnection(credentials)
测试智能机器人连接是否正常:
const result = await testAIBotConnection({ botId: "xxx", secret: "yyy" });
console.log(result.success, result.message);环境变量
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| WECOM_LOG_LEVEL | info | 日志级别:debug / info / warn / error / silent |
| WECOM_EGRESS_PROXY_URL | — | HTTP 代理地址(需安装 undici) |
| WECOM_API_BASE_URL | https://qyapi.weixin.qq.com | Agent API 自定义基础地址 |
| LANGGRAPH_WECOM_STATE_DIR | ~/.langgraph-wecom | 持久化 reqId 存储目录 |
示例
所有示例位于 examples/ 目录:
| 示例 | 说明 | | --- | --- | | basic-bot.ts | 基础示例:连接测试、Agent API、Webhook、Tool 生成 | | thinking-test.ts | 思考过程模拟测试(无需 LLM,纯测流式) | | deepseek-agent.ts | DeepSeek 智能体完整示例(流式推理 + 思考过程) |
运行:
# 基础示例
BOT_ID=xxx SECRET=yyy npx tsx examples/basic-bot.ts
# 思考过程测试
WECOM_BOT_ID=xxx WECOM_SECRET=xxx npx tsx examples/thinking-test.ts
# DeepSeek 智能体
DEEPSEEK_API_KEY=sk-xxx WECOM_BOT_ID=xxx WECOM_SECRET=xxx npx tsx examples/deepseek-agent.ts项目结构
langgraph-plugin-wecom/
├── index.ts # 根入口
├── package.json
├── tsconfig.json
├── src/
│ ├── index.ts # 主入口
│ ├── wecom/
│ │ ├── constants.ts # API 端点 / 限制 / 常量
│ │ ├── http.ts # 统一 HTTP 客户端(代理 / 超时)
│ │ ├── ai-bot.ts # 智能机器人 WebSocket 客户端
│ │ ├── agent-api.ts # 自建应用 Agent API
│ │ ├── webhook-bot.ts # 群机器人 Webhook
│ │ ├── think-parser.ts # <think> 标签解析
│ │ ├── dynamic-agent.ts # 动态 Agent ID
│ │ ├── reqid-store.ts # 持久化 reqId 存储
│ │ └── index.ts
│ ├── plugin/
│ │ ├── wecom-plugin.ts # WecomPlugin 主类
│ │ ├── tools.ts # LangGraph Tool 封装
│ │ └── index.ts
│ ├── utils/
│ │ ├── logger.ts # 结构化日志
│ │ ├── cache.ts # TTLCache / 消息去重 / 文本切分
│ │ └── index.ts
│ └── langgraph/ # @deprecated 旧版兼容
│ ├── agent.ts
│ └── index.ts
├── examples/ # 可运行示例
│ ├── basic-bot.ts
│ ├── thinking-test.ts
│ └── deepseek-agent.ts
├── tests/ # 测试(62 个)
│ ├── ai-bot.test.ts
│ ├── agent-api.test.ts
│ ├── webhook-bot.test.ts
│ ├── http.test.ts
│ ├── cache.test.ts
│ ├── think-parser.test.ts
│ └── dynamic-agent.test.ts
├── scripts/
│ └── build.ts # 构建脚本(esbuild + tsc)
├── dist/ # 构建产物
└── .trae/
├── project_rules.md # 项目约束
└── documents/ # 计划文档开发
# 安装依赖
npm install
# 类型检查
npm run typecheck
# 运行测试(62 个)
npm test
# 构建
npm run build发布到 npm
# 1. 登录 npm
npm login
# 2. 构建
npm run build
# 3. 发布
npm publish注意:发布前请确保
npm login已登录,且包名langgraph-plugin-wecom在 npm 上未被占用。
License
MIT
致谢
本项目的企业微信通信实现复用了 openclaw-plugin-wecom 的相关模块,感谢其对企微通信的细致封装。
相关项目
- openclaw-plugin-wecom — 企业微信 OpenClaw Channel 插件(本项目的前身)
- @wecom/aibot-node-sdk — 企业微信智能机器人 Node.js SDK
- LangGraph — 构建有状态、多步骤 AI Agent 的框架
