从零开始搭建飞书智能助手——第一步:引入SpringBoot、FastApi实现与助手对话
用 Spring Boot + FastAPI 搭建飞书 AI 个人助理
整体架构
当前版本已完成 「飞书 ↔ LLM 自由对话」 闭环。提醒、MySQL、重复闹钟等能力在下一期迭代。
项目结构
personal-butler(Java)
personal_butler/
├── pom.xml
├── Dockerfile # Maven 多阶段构建 → JRE 17 Alpine
├── docker-compose.yml # 编排 personal-butler + butler-agent
├── .env # 飞书凭证(不入库)
└── src/main/java/com/liaoyx/personalbutler/
├── PersonalButlerApplication.java
├── web/ # HTTP 入口
│ ├── FeishuCallbackController.java # POST /feishu/callback
│ └── HealthController.java # GET /health
├── agent/
│ └── ButlerAgentClient.java # 调用 butler-agent
├── config/
│ ├── AsyncConfig.java # @Async 线程池
│ ├── RestClientConfig.java # HTTP/1.1 RestClient
│ └── properties/
│ ├── FeishuProperties.java
│ └── ButlerAgentProperties.java
└── feishu/
├── callback/ # Webhook:验签、解密、事件分发
├── client/ # 飞书 Open API:发消息、取 token
└── message/ # 消息解析 → 调 agent → 回复
包设计原则:
web/— 控制器,只做 HTTP 适配feishu/callback/— 入站(安全 + 事件路由)feishu/client/— 出站(Open API)feishu/message/— 业务编排agent/— AI 服务边界,以后换模型不影响飞书层
butler-agent(Python)
butler_agent/
├── Dockerfile
├── requirements.txt
├── .env # DEEPSEEK_API_KEY(不入库)
└── app/
├── main.py # FastAPI:/health, /api/chat
├── config.py # pydantic-settings
└── llm.py # DeepSeek 调用
结构刻意保持极简:一个文件负责路由,一个文件负责 LLM,后续再加 agent.py(Function Calling)和 reminder_client.py(调 personal-butler API)。
飞书接入:三个关键点
1. Webhook 必须快速返回
飞书事件订阅要求 1 秒内响应,否则重试。消息处理(调 LLM)可能耗时数秒,因此:
@Async
public void handleMessageReceive(FeishuCallbackPayload payload) {
// 解析文本 → 调 agent → 飞书回复
}
Controller 收到 im.message.receive_v1 后立即返回空字符串,实际逻辑在异步线程执行。
2. 安全:验签 + 可选加密
FeishuCallbackService 处理两层安全:
- Verification Token — 校验请求来源
- Encrypt Key — AES 解密 payload(飞书开启加密时)
- Signature — 校验
X-Lark-Signature请求头
feishuCallbackService.verifySignature(timestamp, nonce, signature, rawBody);
return feishuCallbackService.handleCallback(rawBody);
URL 验证(首次配置回调地址)返回 challenge:
{"challenge": "xxx"}
3. 发消息:Tenant Access Token 缓存
FeishuApiClient 负责:
- 用
app_id+app_secret换tenant_access_token - 内存缓存 token,过期前 60 秒刷新
- 调用
im/v1/messages发文本消息
飞书开放平台配置步骤
- 登录 飞书开放平台,创建企业自建应用
- 开启机器人能力
- 事件订阅 → 添加
im.message.receive_v1(接收消息) - 请求地址填写:
https://你的域名/feishu/callback - 权限管理 → 开通「获取与发送单聊、群组消息」
- 把凭证写入
personal_butler/.env:
FEISHU_VERIFICATION_TOKEN=xxx
FEISHU_ENCRYPT_KEY=xxx # 若开启加密
FEISHU_APP_ID=cli_xxx
FEISHU_APP_SECRET=xxx
BUTLER_AGENT_BASE_URL=http://butler-agent:8000
本地调试时,用 ngrok 或 frp 把 8080 暴露到公网。
消息处理全流程
以用户发送「你好」为例:
FeishuCallbackController
→ FeishuCallbackService.handleCallback()
→ im.message.receive_v1 ?
→ FeishuMessageEventHandler.handleMessageReceive() [@Async]
→ 跳过机器人自身消息
→ FeishuMessageParser 解析文本
→ ButlerAgentClient.chat(prompt)
→ POST http://butler-agent:8000/api/chat
→ FeishuMessageService.reply(chatId, reply)
→ FeishuApiClient.sendTextMessage()
服务间 HTTP 契约:
POST /api/chat
Content-Type: application/json
{"prompt": "用户说的话"}
→ {"reply": "AI 回复"}
butler-agent:DeepSeek 代理
butler-agent 目前是一个LLM 代理,核心代码:
def chat(prompt: str) -> str:
client = OpenAI(
api_key=settings.deepseek_api_key,
base_url=settings.deepseek_base_url,
)
response = client.chat.completions.create(
model=settings.deepseek_model,
messages=[{"role": "user", "content": prompt}],
)
return response.choices[0].message.content or ""
配置项(.env):
DEEPSEEK_API_KEY=sk-xxx
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-chat
使用 OpenAI 兼容 SDK 调 DeepSeek,换模型只需改环境变量。
踩坑记录
RestClient 与 uvicorn 的 HTTP/2 问题
Spring Boot 3 默认 RestClient 可能走 JDK HttpClient 并尝试 HTTP/2 升级,与 uvicorn 存在兼容问题。解决方式:显式使用 HTTP/1.1:
@Bean
RestClient restClient() {
return RestClient.builder()
.requestFactory(new SimpleClientHttpRequestFactory())
.build();
}
避免回复循环
飞书机器人在群里发消息后,也会触发 im.message.receive_v1。需要在 handler 里过滤 sender_type == "app" 的消息,否则会无限对话。
密钥不要入库
.env 已在 .gitignore 中。团队协作用 .env.example 提供模板,不含真实密钥。
Docker 部署
personal_butler/docker-compose.yml 一键启动两个服务:
services:
personal-butler:
build: .
ports: ["8080:8080"]
env_file: [.env]
environment:
BUTLER_AGENT_BASE_URL: http://butler-agent:8000
depends_on: [butler-agent]
butler-agent:
build: ../../butler_agent
ports: ["8000:8000"]
env_file: [../../butler_agent/.env]
启动:
cd personal_butler
docker compose up --build
| 容器 | 端口 | 健康检查 |
|---|---|---|
| personal-butler | 8080 | GET /health |
| butler-agent | 8000 | GET /health |
personal-butler 使用多阶段 Dockerfile:Maven 编译 → 拷贝 jar 到 Alpine JRE 镜像,最终镜像体积小、无构建工具残留。
技术栈汇总
| 层级 | personal-butler | butler-agent |
|---|---|---|
| 语言 | Java 17 | Python 3.11 |
| 框架 | Spring Boot 3.3.5 | FastAPI 0.115 |
| HTTP 客户端 | RestClient | — |
| LLM | — | OpenAI SDK → DeepSeek |
| 构建/运行 | Maven + Docker | pip + uvicorn |
| 测试 | JUnit 5 + MockMvc | — |
当前能力 vs 演进路线
| 能力 | 现状 | 下一期 |
|---|---|---|
| 飞书收发消息 | ✅ | — |
| LLM 自由对话 | ✅ 单轮 | 多轮记忆 |
| 自然语言建提醒 | ❌ | butler-agent Function Calling |
| 单次 / 重复闹钟 | ❌ | personal-butler + MySQL + MyBatis |
| 提前 N 分钟提醒 | ❌ | advance_minutes + 定时调度 |
| 到点飞书推送 | ❌ | @Scheduled 扫描 + 主动发消息 |
下一期目标架构:
总结
personal-butler + butler-agent 的组合,用最小可行架构跑通了飞书 AI 助理的核心链路:
- personal-butler 负责「脏活累活」— 飞书安全、异步、发消息
- butler-agent 负责「聪明的事」— LLM 理解与决策
- Docker Compose 让两个服务一键联调
这种拆分让每一层职责清晰,后续加提醒、加数据库、加更多 Agent 工具,都不需要动飞书接入层。
附录:API 速查
personal-butler
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health |
健康检查 |
| POST | /feishu/callback |
飞书 Webhook |
butler-agent
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health |
健康检查 |
| POST | /api/chat |
LLM 对话 |
更多推荐
所有评论(0)