发布时间:2026/7/31 16:32:44
飞书智能助手:LLM驱动MCP工具调用实战 文章目录一、项目概述二、飞书应用创建与配置三、依赖配置四、核心代码实现五、完整配置清单六、测试与验证七、生产环境部署建议八、总结一、项目概述本文基于现有的MCP Client Demo项目(可以参考之前的博文Spring AI MCP Client 实战)详细介绍了如何将其与飞书群机器人集成实现飞书消息 → LLM 智能决策 → MCP 工具调用 → 结果回复的完整链路。1.1 核心设计理念工具选择由大模型LLM自主决策而非硬编码的命令匹配。Spring AI 的 ChatClient 已内置工具调用能力LLM 会根据工具描述自动选择调用哪个工具。1.2 整体架构1.3 数据流向1.4 集成目标在现有架构基础上增加飞书机器人模块实现功能说明事件接收接收飞书群聊 机器人消息 个人单聊消息智能决策LLM 根据工具描述自动选择调用哪个工具工具执行调用本地/MCP Server 工具结果回复将执行结果发回飞书群或单聊1.5 效果展示p2pgroup二、飞书应用创建与配置2.1 创建飞书应用登录 飞书开放平台点击「创建企业自建应用」填写应用名称如 “MCP 智能助手”和描述创建完成后在「凭证与基础信息」页面获取App ID和App Secret2.2 事件接收模式选型WebSocket vs Webhook飞书 SDK 支持两种事件接收模式对比如下对比项WebSocket 长连接Webhook 回调网络要求无需公网 IP/域名需要公网可访问的 HTTPS 地址部署复杂度低开箱即用高需配置域名、SSL、反向代理适用场景开发调试、内网部署、NAT 后服务生产环境、高可用集群实时性高服务端主动推送高飞书主动回调可靠性依赖长连接稳定性需处理断线重连依赖公网稳定性飞书有重试机制扩展性单实例受限可配合负载均衡多实例部署防火墙需允许出站 WebSocket需允许入站 HTTPS推荐方案开发/测试环境使用 WebSocket零配置即可接收事件生产环境使用 Webhook配合域名和负载均衡2.3 配置应用权限在飞书开放平台的应用管理页面进入「权限管理」添加以下权限权限名称权限标识用途必须获取与发送单聊、群组消息im:message发送消息到群/单聊是读取消息im:message:readonly接收消息事件是获取群组信息im:chat:readonly获取群信息否获取用户信息contact:user.id:readonly识别消息发送者否2.4 配置事件订阅WebSocket 模式开发环境进入「事件订阅」页面选择「使用长连接接收事件」添加事件im.message.receive_v1接收消息勾选以下子事件取群组中其他机器人和用户当前机器人的消息读取用户发给机器人的单聊消息无需配置回调地址Webhook 模式生产环境进入「事件订阅」页面选择「将事件发送至开发者服务器」配置请求地址https://your-domain.com/feishu/event/callback记录Encrypt Key和Verification Token添加事件im.message.receive_v1接收消息勾选以下子事件取群组中其他机器人和用户当前机器人的消息读取用户发给机器人的单聊消息飞书会发送验证请求需确保服务已启动并能正确响应2.5 发布应用与添加到群在「版本管理与发布」中创建版本并提交审核审核通过后将机器人添加到目标飞书群打开目标群 → 群设置 → 群机器人 → 添加机器人选择刚创建的应用机器人2.6 配置飞书机器人能力在应用管理页面进入「应用能力」→「机器人」开启机器人能力配置机器人名称和头像设置机器人描述三、依赖配置3.1 添加飞书 SDK 依赖在pom.xml中添加飞书官方 SDKdependencygroupIdcom.larksuite.oapi/groupIdartifactIdoapi-sdk/artifactIdversion2.4.4/version/dependency3.2 配置飞书应用信息在application.yml中添加飞书相关配置# 飞书配置 feishu:app-id: {FEISHU_APP_SECRET}event-mode: websocket # 开发环境用 websocket生产用 webhook# Webhook 模式配置生产环境encrypt-key: {FEISHU_VERIFICATION_TOKEN:}callback-path: /feishu/event/callbacklogging:level:io.modelcontextprotocol: debugcom.example.mcpclient: debug四、核心代码实现创建FeishuMessage.java封装飞书消息package com.example.mcpclient.feishu.model;import com.lark.oapi.service.im.v1.model.P2MessageReceiveV1;import lombok.Data;Datapublic class FeishuMessage {private String messageId;private String chatId;private String chatType; // p2p 单聊, group 群聊private String userId;private String text;private boolean mentionBot;public static FeishuMessage from(P2MessageReceiveV1 event) {FeishuMessage msg new FeishuMessage();msg.setMessageId(event.getEvent().getMessage().getMessageId());msg.setChatId(event.getEvent().getMessage().getChatId());msg.setChatType(event.getEvent().getMessage().getChatType());msg.setUserId(event.getEvent().getSender().getSenderId().getOpenId());// 解析消息内容String content event.getEvent().getMessage().getContent();msg.setText(parseTextContent(content));// 判断是否 了机器人群聊需要 单聊不需要msg.setMentionBot(isMentionBot(event));return msg;}private static String parseTextContent(String content) {try {com.fasterxml.jackson.databind.ObjectMapper mapper new com.fasterxml.jackson.databind.ObjectMapper();var node mapper.readTree(content);return node.get(text).asText();} catch (Exception e) {return content;}}private static boolean isMentionBot(P2MessageReceiveV1 event) {var mentions event.getEvent().getMessage().getMentions();return mentions ! null mentions.length 0;}public String getTextWithoutMention() {if (!mentionBot) return text;return text.replaceAll(\\S\\s*, ).trim();}public boolean isP2P() {return p2p.equals(chatType);}}4.2 飞书事件监听器创建FeishuEventListener.java处理飞书消息事件package com.example.mcpclient.feishu.listener;import com.example.mcpclient.feishu.executor.McpExecutor;import com.example.mcpclient.feishu.model.FeishuMessage;import com.lark.oapi.event.EventDispatcher;import com.lark.oapi.service.im.ImService;import com.lark.oapi.service.im.v1.model.P2MessageReceiveV1;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;ConfigurationRequiredArgsConstructorSlf4jpublic class FeishuEventListener {private final McpExecutor mcpExecutor;Value({feishu.encrypt-key:})private String encryptKey;Beanpublic EventDispatcher eventDispatcher() {return EventDispatcher.newBuilder(verificationToken, encryptKey).onP2MessageReceiveV1(new ImService.P2MessageReceiveV1Handler() {Overridepublic void handle(P2MessageReceiveV1 event) {handleMessage(event);}}).build();}private void handleMessage(P2MessageReceiveV1 event) {try {FeishuMessage msg FeishuMessage.from(event);// 单聊消息直接处理群聊消息需要 机器人才处理if (!msg.isP2P() !msg.isMentionBot()) {log.debug(群聊消息未 机器人忽略: {}, msg.getText());return;}log.info(收到飞书消息 - chatType: {}, chatId: {}, userId: {}, text: {},msg.getChatType(), msg.getChatId(), msg.getUserId(), msg.getText());// 直接将用户消息发给 LLM由 LLM 自主决策工具调用String userText msg.getTextWithoutMention();mcpExecutor.execute(msg.getChatId(), msg.getUserId(), userText);} catch (Exception e) {log.error(处理飞书消息异常, e);}}}4.3 MCP 执行器创建McpExecutor.java协调工具调用流程package com.example.mcpclient.feishu.executor;import com.example.mcpclient.feishu.sender.FeishuMessageSender;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.ai.chat.client.ChatClient;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component;ComponentRequiredArgsConstructorSlf4jpublic class McpExecutor {private final ChatClient chatClient;private final FeishuMessageSender messageSender;Asyncpublic void execute(String chatId, String userId, String userMessage) {long startTime System.currentTimeMillis();try {log.info(开始处理消息 - chatId: {}, userId: {}, message: {},chatId, userId, userMessage);// 直接调用 ChatClientLLM 会自动决策是否调用工具String result chatClient.prompt().user(userMessage).call().content();// 发送结果回飞书messageSender.sendTextMessage(chatId, result);log.info(消息处理完成 - chatId: {}, 耗时: {}ms,chatId, System.currentTimeMillis() - startTime);} catch (Exception e) {log.error(消息处理失败 - chatId: {}, chatId, e);messageSender.sendTextMessage(chatId, 抱歉服务暂时出现问题请稍后重试。);}}}4.4 飞书消息发送器创建FeishuMessageSender.java封装消息发送package com.example.mcpclient.feishu.sender;import com.lark.oapi.Client;import com.lark.oapi.service.im.v1.enums.CreateMessageReceiveIdTypeEnum;import com.lark.oapi.service.im.v1.enums.MsgTypeEnum;import com.lark.oapi.service.im.v1.model.*;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;ComponentRequiredArgsConstructorSlf4jpublic class FeishuMessageSender {private final Client feishuClient;public void sendTextMessage(String chatId, String text) {try {String safeText escapeJson(text);String truncatedText truncateToLimit(safeText, 7900);String content String.format({\text\:\%s\}, truncatedText);CreateMessageReq req CreateMessageReq.newBuilder().receiveIdType(CreateMessageReceiveIdTypeEnum.CHAT_ID).createMessageReqBody(CreateMessageReqBody.newBuilder().receiveId(chatId).msgType(MsgTypeEnum.MSG_TYPE_TEXT.getValue()).content(content).build()).build();var resp feishuClient.im().message().create(req);if (resp.success()) {log.info(消息发送成功 - chatId: {}, messageId: {},chatId, resp.getData().getMessageId());} else {log.error(消息发送失败 - code: {}, msg: {},resp.getCode(), resp.getMsg());}} catch (Exception e) {log.error(发送飞书消息异常, e);}}private String escapeJson(String text) {return text.replace(\\, \\\\).replace(\, \\\).replace(\n, \\n).replace(\r, \\r).replace(\t, \\t);}private String truncateToLimit(String text, int limit) {return text.length() limit ? text : text.substring(0, limit - 10) \n...内容已截断;}}4.5 飞书客户端配置创建FeishuClientConfig.java配置飞书客户端package com.example.mcpclient.feishu.config;import com.lark.oapi.Client;import com.lark.oapi.event.EventDispatcher;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;ConfigurationRequiredArgsConstructorSlf4jpublic class FeishuClientConfig {Value({feishu.app-secret})private String appSecret;Beanpublic Client feishuClient() {return Client.newBuilder(appId, appSecret).build();}Bean(initMethod start)ConditionalOnProperty(name feishu.event-mode, havingValue websocket)public FeishuWebSocketClient feishuWebSocketClient(EventDispatcher eventDispatcher) {log.info(初始化飞书 WebSocket 客户端);return new FeishuWebSocketClient(appId, appSecret, eventDispatcher);}}4.6 WebSocket 客户端实现飞书 SDK 内置了com.lark.oapi.ws.Client封装了 WSS 握手鉴权、心跳保活和断线重连package com.example.mcpclient.feishu.config;import com.lark.oapi.event.EventDispatcher;import com.lark.oapi.ws.Client;import lombok.extern.slf4j.Slf4j;Slf4jpublic class FeishuWebSocketClient {private final Client wsClient;public FeishuWebSocketClient(String appId, String appSecret, EventDispatcher eventDispatcher) {this.wsClient new Client.Builder(appId, appSecret).eventHandler(eventDispatcher).autoReconnect(true).build();}public void start() {log.info(飞书 WebSocket 客户端启动发起 WSS 连接...);wsClient.start();}}关键点wsClient.start()会发起 WSS 握手鉴权SDK 内部自动处理心跳和断线重连。如果不调用start()WebSocket 连接不会建立事件也无法接收。4.7 Webhook 回调控制器生产环境当使用 Webhook 模式时需要创建回调控制器接收飞书事件推送package com.example.mcpclient.feishu.controller;import com.fasterxml.jackson.databind.ObjectMapper;import com.lark.oapi.core.request.EventReq;import com.lark.oapi.event.EventDispatcher;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.web.bind.annotation.*;import java.util.Map;RestControllerConditionalOnProperty(name feishu.event-mode, havingValue webhook)RequiredArgsConstructorSlf4jpublic class FeishuWebhookController {private final EventDispatcher eventDispatcher;private final ObjectMapper objectMapper new ObjectMapper();Value({feishu.callback-path:/feishu/event/callback})public MapString, String handleEvent(RequestBody String body) {log.debug(收到飞书 Webhook 回调: {}, body);try {// 处理 URL 验证挑战var node objectMapper.readTree(body);if (node.has(type) url_verification.equals(node.get(type).asText())) {return Map.of(challenge, node.get(challenge).asText());}// 使用 EventDispatcher 解析并分发事件EventReq eventReq new EventReq();eventReq.setBody(body.getBytes());eventReq.setHttpPath(callbackPath);eventDispatcher.parseReq(eventReq);} catch (Exception e) {log.error(事件处理失败, e);}return Map.of(code, 0);}}4.8 启用异步支持在启动类中添加EnableAsync注解package com.example.mcpclient;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.scheduling.annotation.EnableAsync;SpringBootApplicationEnableAsyncpublic class McpClientApplication {public static void main(String[] args) {SpringApplication.run(McpClientApplication.class, args);}}五、完整配置清单5.1 application.yml 最终配置feishu:app-id: ${FEISHU_APP_ID}app-secret: ${FEISHU_APP_SECRET}event-mode: websocketencrypt-key: ${FEISHU_ENCRYPT_KEY:}verification-token: ${FEISHU_VERIFICATION_TOKEN:}callback-path: /feishu/event/callbacklogging:level:io.modelcontextprotocol: debugcom.example.mcpclient: debugcom.lark.oapi: debug5.2 环境变量说明变量名说明示例FEISHU_APP_ID飞书应用 IDcli_xxxxxFEISHU_APP_SECRET飞书应用密钥xxxxxxFEISHU_ENCRYPT_KEY消息加密密钥Webhook 模式xxxxxxFEISHU_VERIFICATION_TOKEN事件校验令牌Webhook 模式xxxxxxMINIMAX_API_KEYMiniMax API 密钥sk-xxxxx六、测试与验证6.1 测试流程启动 MCP Server确保http://localhost:8080/mcp可访问启动本服务mvn spring-boot:run在飞书群中 机器人机器人 查询北京今天的天气机器人 帮我查一下订单 ORDER-123456机器人 你好6.2 预期结果用户消息LLM 决策预期回复机器人 查询北京今天的天气自动调用local_weather_query(city北京)北京当前天气信息机器人 帮我查一下订单 ORDER-123456自动调用 MCP Server 的订单查询工具订单详情机器人 你好不调用工具直接回复AI 闲聊回复七、生产部署建议7.1 切换到 Webhook 模式修改配置feishu: event-mode: webhook callback-path: /feishu/event/callback7.2 配置 Nginx 反向代理server { listen 80; server_name your-domain.com; location /feishu/event/callback { proxy_pass http://localhost:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }7.3 配置飞书事件订阅在飞书开放平台配置事件订阅地址https://your-domain.com/feishu/event/callback加密密钥与配置文件一致校验令牌与配置文件一致八、总结本文完成了从 0 到 1 的飞书机器人 MCP Client 集成核心流程如下飞书消息 → FeishuEventListener → McpExecutor→ ChatClientLLM 自主决策工具调用→ 工具执行→ FeishuMessageSender → 飞书群回复8.1 项目结构src/main/java/com/example/mcpclient/└── feishu/ # 飞书模块新增├── config/│ ├── FeishuClientConfig.java│ └── FeishuWebSocketClient.java├── controller/│ └── FeishuWebhookController.java # Webhook 回调生产环境├── listener/│ └── FeishuEventListener.java├── executor/│ └── McpExecutor.java├── sender/│ └── FeishuMessageSender.java└── model/└── FeishuMessage.java8.2 核心优势智能决策LLM 根据工具描述自动选择调用哪个工具无需硬编码命令匹配低延迟直接桥接模式端到端延迟 5s高可用支持 WebSocket/Webhook 双模式易扩展通过配置即可新增 MCP Server 连接LLM 自动发现新工具优雅降级完善的异常处理和友好提示

相关新闻

2026/7/31 16:32:44

Subfinder字幕查找器:3种方法帮你轻松找到完美字幕

Subfinder字幕查找器:3种方法帮你轻松找到完美字幕 【免费下载链接】subfinder 字幕查找器 项目地址: https://gitcode.com/gh_mirrors/subfi/subfinder 想象一下,你刚下载了一部精彩的电影,准备享受周末的观影时光,却发现…

2026/7/31 16:32:44

Java AI智能体开发实战指南:从零构建企业级智能应用

Java AI智能体开发实战指南:从零构建企业级智能应用 一、什么是Java AI智能体?如何落地开发? AI智能体(Agent)是一个能够感知环境、自主决策并执行任务的智能程序。在Java生态中,开发AI智能体通常指利用Spr…

2026/7/31 16:32:44

B站数据分析实战:从采集到可视化的全流程解析

1. 项目背景与核心价值 这个B站数据分析项目源于我在毕业设计阶段的真实需求——如何从海量视频数据中挖掘出有价值的规律。作为国内最大的年轻人文化社区,B站每天产生数百万条弹幕、评论和视频互动数据,这些数据背后隐藏着用户行为、内容趋势和社区生态…

2026/7/31 17:22:47

ncmppGui:3分钟掌握网易云音乐NCM格式解密转换技巧

ncmppGui:3分钟掌握网易云音乐NCM格式解密转换技巧 【免费下载链接】ncmppGui 一个使用C编写的极速ncm转换GUI工具 项目地址: https://gitcode.com/gh_mirrors/nc/ncmppGui ncmppGui是一款专为网易云音乐NCM加密格式设计的极速转换工具,采用C编写…

2026/7/31 17:22:47

APK Installer:在Windows上直接安装Android应用的完整指南

APK Installer:在Windows上直接安装Android应用的完整指南 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否曾想在Windows电脑上运行Android应用&…

2026/7/31 17:22:47

乐高漫威抽抽乐摸盒技巧:阿加莎女巫识别全攻略

最近在逛乐高店时,发现很多玩家对漫威抽抽乐系列又爱又恨——想要集齐心仪角色,却总担心重复购买。特别是最新推出的阿加莎女巫人仔,作为《旺达幻视》中的高人气反派,成为了不少收藏家的目标。但你真的了解如何高效收集这类热门人…

2026/7/31 17:17:47

语音转文字不限时长免费!通义千问Qwen3-ASR Pro离线懒人包。彻底告别录音上传云端隐私泄露风险,小白双击一键运行,打工人会议纪要、字幕制作必备本地AI工具,附详细安装教程

最近逛技术论坛和贴吧,看到很多人在找好用的语音转文字工具。有网友发帖抱怨,平时开会录音、做访谈记录,用在线转写平台要花钱,免费的平台又限制时长,把录音传到云端还怕泄露公司机密。懂技术的人去折腾阿里开源的Qwen…

2026/7/29 22:32:30

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

一、背景与测试方案 在实际项目交付中,PDF文件合并与版权保护水印的叠加是一个高频但容易被低估的技术需求。典型的处理链路涉及:多源PDF的文件流合并、页面级水印渲染(含透明度混合与图层叠加)、输出文件体积控制。看似简单的操作…

2026/7/31 0:01:11

物理复制比逻辑复制好在哪?数据库复制原理详解

数据库复制是把主库数据同步到备库的机制,分为逻辑复制和物理复制两种。逻辑复制传输的是 SQL 语句或行变更事件,物理复制传输的是存储引擎底层的物理日志。阿里云 PolarDB(云原生数据库)采用物理复制,在同步延迟、数据…

2026/7/31 0:01:11

BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/Bilib…

2026/7/31 0:01:11

有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

当前,游戏行业的“DataAI融合”已从概念验证进入价值落地阶段。根据IDC 2025年数据,中国AI游戏云市场规模已达18.6亿元;同时,游戏研发环节AI渗透率高达86%,生成式AI内容普及率超过50%。面对庞大的市场,游戏…

2026/7/31 0:38:56

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…