用 Spring Boot + FastAPI 搭建飞书 AI 个人助理

整体架构

DeepSeek API butler-agent :8000 personal-butler :8080 飞书开放平台 飞书用户 DeepSeek API butler-agent :8000 personal-butler :8080 飞书开放平台 飞书用户 发送文本消息 POST /feishu/callback 验签 / 解密 / 异步处理 200 OK(立即返回) POST /api/chat {prompt} chat.completions.create 回复文本 {reply} 发送消息 API 机器人在会话中回复

当前版本已完成 「飞书 ↔ 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 负责:

  1. app_id + app_secrettenant_access_token
  2. 内存缓存 token,过期前 60 秒刷新
  3. 调用 im/v1/messages 发文本消息

飞书开放平台配置步骤

  1. 登录 飞书开放平台,创建企业自建应用
  2. 开启机器人能力
  3. 事件订阅 → 添加 im.message.receive_v1(接收消息)
  4. 请求地址填写:https://你的域名/feishu/callback
  5. 权限管理 → 开通「获取与发送单聊、群组消息」
  6. 把凭证写入 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 扫描 + 主动发消息

下一期目标架构:

MySQL butler-agent personal-butler 飞书用户 MySQL butler-agent personal-butler 飞书用户 到点 提醒我今天下午2点有面试 /api/chat(带 userId, chatId) LLM 识别 create_reminder POST /api/reminders MyBatis 写入 已设置提醒 查到期提醒 ⏰ 提醒:面试

总结

personal-butler + butler-agent 的组合,用最小可行架构跑通了飞书 AI 助理的核心链路:

  1. personal-butler 负责「脏活累活」— 飞书安全、异步、发消息
  2. butler-agent 负责「聪明的事」— LLM 理解与决策
  3. Docker Compose 让两个服务一键联调

这种拆分让每一层职责清晰,后续加提醒、加数据库、加更多 Agent 工具,都不需要动飞书接入层。


附录:API 速查

personal-butler

方法 路径 说明
GET /health 健康检查
POST /feishu/callback 飞书 Webhook

butler-agent

方法 路径 说明
GET /health 健康检查
POST /api/chat LLM 对话

更多推荐