飞书智能助手:LLM驱动MCP工具调用实战

发布时间:2026/9/17 13:54:31

飞书智能助手: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/9/16 23:25:38

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

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

2026/9/13 23:29:13

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

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

2026/9/15 18:28:36

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

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

2026/9/17 13:49:57

基于Spark与Python的热门旅游景点数据分析与可视化大屏

简介:这是一份围绕大数据技术与旅游景点数据分析可视化撰写的完整论文文档,面向旅游管理、数据科学与计算机相关专业的本科生、研究生及项目实践者,可用于课程论文、毕业设计选题参考或系统开发方案借鉴。压缩包内共1个docx文件,约…

2026/9/17 13:49:56

芯片级EMI设计:从晶圆到Layout,源头降噪实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 13:49:56

低代码+大模型:从零搭建智能工单系统全实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 13:49:56

百度网盘OAuth2.0授权全流程:从授权码到token刷新实战指南

我接过一个需求:在自研系统里让用户绑定自己的百度网盘账号,然后授权我们读取用户空间信息、同步指定目录文件。听起来很常规,真做起来,光是第一步“授权”就卡了我一整天。不是没有文档,而是文档把流程拆得太碎&#…

2026/9/16 12:52:37

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/17 0:03:13

WiFi密码安全测试:从原理到实战的字典暴力破解指南

1. 写在前面:我为什么要研究WiFi密码这件事先交代一下背景。我身边有不少朋友,家里的WiFi密码常年是"12345678"或者"88888888",问就是"好记"。直到有一次,隔壁邻居蹭网蹭到我家路由器后台都进不去&…

2026/9/17 0:03:13

redis-py服务控制与监控函数实战:从ping到slowlog的巡检指南

我用 redis-py 写了快五年的业务代码,坦白说,真正让我觉得这个客户端“像一个成熟工具箱”的,不是 get/set 那套基本操作,而是它那批专门做服务控制与状态监控的辅助函数。日常开发里,大家把redis.Redis(host..., deco…

2026/9/17 0:03:13

SpringBoot+Vue3实现中小企业设备管理系统开发实践

1. 项目概述与核心价值中小企业设备管理系统是制造业、服务业等领域的基础信息化工具。传统设备管理往往依赖Excel表格或纸质记录,存在数据孤岛、流程混乱、维护成本高等痛点。这套基于Java SpringBootVue3MyBatis的技术方案,通过前后端分离架构实现了设…

2026/9/16 22:55:57

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/16 22:56:09

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/16 22:56:16

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码