# agent-demo
**Repository Path**: Jay1114/agent-demo
## Basic Information
- **Project Name**: agent-demo
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-10
- **Last Updated**: 2026-09-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Agent-Demo | Spring AI 2.0 综合 Agent 学习示例
> 基于 **Spring Boot 4 + Spring AI 2.0** 构建的代表性 Agent 项目,覆盖 **Tool Calling、MCP、Advisors、RAG、ReAct、Skills、安全认证、弹性容错** 等前沿技术,使用 DeepSeek 作为 LLM 提供商。模块化设计,既可作学习资源,也可作生产级脚手架。
> [English Documentation](README.en.md)
## 项目简介
Agent-Demo 是基于 **Spring Boot 4.0.8 + Spring AI 2.0.1** 构建的综合 AI Agent 学习项目,使用 **DeepSeek** 作为 LLM 提供商。在单一、结构清晰的代码库中覆盖了 Spring AI 的全部核心能力,并补充了生产化必备的安全认证、API 文档、弹性容错、持久化与分布式追踪——既可作为学习资源,也可作为生产级脚手架。
## 技术栈
| 类别 | 技术 | 版本/说明 |
|---|---|---|
| 框架 | Spring Boot | 4.0.8 |
| AI 框架 | Spring AI | 2.0.1 |
| 大语言模型 | DeepSeek | deepseek-chat / deepseek-reasoner |
| Embedding 模型 | Transformers ONNX (all-MiniLM-L6-v2) | ~90MB,首次运行自动下载 |
| 向量库 | dev: SimpleVectorStore(内存)/ prod: PgVector(持久化,HNSW) | PostgreSQL |
| 通信协议 | MCP(Model Context Protocol) | SSE 传输,Server + Client 双端 |
| 安全认证 | Spring Security + API Key(X-API-Key) | 常量时间比对 + 暴力破解防护 |
| API 文档 | springdoc-openapi (Swagger UI) | 2.8.0 |
| 弹性容错 | Resilience4j | 2.2.0(限流/重试/熔断) |
| 输入校验 | Spring Validation (`@Valid`/`@NotBlank`/`@Size`) | — |
| 本地缓存 | Caffeine | 暴力破解计数器 TTL 驱逐 |
| 分布式追踪 | Micrometer Tracing (Brave) + Zipkin Reporter | 采样率可配置 |
| 构建工具 | Maven | 3.9+ |
| JDK | Java | 21+ |
| 集成测试 | Testcontainers (PostgreSQL + PgVector) | 1.20.4 |
## 分层架构
```mermaid
flowchart TB
subgraph Controller["Controller 层"]
A1["AgentController
(@Tag/@Operation Swagger 注解)"]
A2["McpController"]
A3["SkillController"]
end
subgraph Service["Service 层"]
B1["ChatService
(@RateLimiter/@Retry/@CircuitBreaker)"]
B2["AgentService"]
B3["RagService"]
B4["McpClientService"]
end
subgraph ConfigAdvisor["Config + Advisor 层"]
C1["AiConfig · ChatMemoryConfig · VectorStoreConfig"]
C2["SecurityConfig(prod API Key 认证)
DevSecurityConfig(dev 放行)· WebConfig(CORS)"]
C3["OpenApiConfig · AdvisorConfig"]
C4["LoggingAdvisor · SafeGuardAdvisor · SkillAdvisor"]
end
subgraph ToolInfra["Tool + Infra 层"]
D1["WeatherTools · CalculatorTools · KnowledgeBaseTools"]
D2["SkillTools · McpServerTools"]
D3["SkillRegistry · KnowledgeLoader"]
D4["GlobalExceptionHandler"]
end
subgraph Domain["Domain 层"]
E1["ChatRequest · ChatResponse · AgentRequest"]
E2["WeatherForecast · Skill · ApiResult"]
end
Controller --> Service --> ConfigAdvisor --> ToolInfra --> Domain
```
## 核心技术点
### ① Tool Calling(工具调用)
- `WeatherTools`、`CalculatorTools`、`KnowledgeBaseTools` 使用 `@Tool` 注解
- LLM 在 ReAct 循环中自主决定何时调用哪个工具
- `ToolCallingAdvisor` 由 `DefaultChatClient` 自动注入链尾
### ② MCP(Model Context Protocol)
- **Server 端**:通过 SSE 端点 `/sse` 暴露 `serverTime` / `randomString` 工具
- **Client 端**:消费远程 MCP Server 的工具,注入为 `ToolCallbackProvider` bean
- 外部 MCP 客户端连接地址:`http://localhost:8087/sse`
### ③ Advisors(可组合拦截器链)
- `SafeGuardAdvisor`(HIGHEST_PRECEDENCE + 50):最外层,拦截越狱/敏感关键词
- `LoggingAdvisor`(HIGHEST_PRECEDENCE + 100):次外层,记录全链路入参/耗时
- `MessageChatMemoryAdvisor`:多轮会话记忆(20 条消息滑动窗口)
- `RetrievalAugmentationAdvisor`:每次请求前自动检索相关文档注入上下文
- `ToolCallingAdvisor`:驱动 ReAct 工具调用循环(自动加入)
### ④ RAG(检索增强生成)
- **被动 RAG**:`RetrievalAugmentationAdvisor` 每次请求自动检索向量库
- **主动 RAG**:`KnowledgeBaseTools` 由模型按需显式调用检索
- 启动时通过 `KnowledgeLoader` 从 `classpath:data/*.md` 加载文档到向量库
- **双模式向量库**:dev 用内存 `SimpleVectorStore`(`@Profile("!prod")`),prod 自动配置 PgVector(HNSW 索引 + cosine 距离 + 384 维)
### ⑤ ReAct(推理-行动循环)
- 系统提示(`prompts/react-system.st`)引导模型遵循:Thought -> Action -> Observation -> Final Answer
- `AgentService` 合并本地工具 + 远程 MCP 工具
- `ToolCallingAdvisor` 自动驱动循环,直到模型不再请求工具
### ⑥ 结构化输出
- `.entity(WeatherForecast.class)` 将 LLM 返回的 JSON 反序列化为 Java record
- 演示从非结构化文本中提取类型安全的结构化数据
### ⑦ SSE 流式输出
- `Flux` 实现打字机效果
- 端点:`GET /api/stream?message=...&sessionId=...`
### ⑧ 安全认证(Spring Security + API Key)
- **dev 环境**:`DevSecurityConfig`(`@Profile("dev")` + `app.security.enabled=false`)放行全部 API 便于调试
- **prod 环境**:`SecurityConfig`(`app.security.enabled=true`)启用 `X-API-Key` 请求头认证
- 认证过滤器特性:
- **常量时间比对**(`MessageDigest.isEqual`)防时序攻击(CWE-208)
- **IP 级暴力破解防护**:同一 IP 连续失败 10 次返回 429,Caffeine TTL(5 分钟)自动驱逐
- 真实 IP 提取,兼容反向代理(`X-Forwarded-For` / `X-Real-IP`)
- 无状态会话(STATELESS)+ 安全响应头(HSTS / CSP / X-Frame-Options DENY)
- 启动校验:API Key 为默认占位值时拒绝启动
- 放行路径可配置(`app.security.permit-paths`:Actuator / Swagger / MCP SSE)
### ⑨ API 文档(Swagger/OpenAPI)
- springdoc-openapi 自动生成,Controller 使用 `@Tag` / `@Operation` 注解
- Swagger UI:`http://localhost:8087/swagger-ui.html`
- OpenAPI JSON:`http://localhost:8087/v3/api-docs`
- 内置 `X-API-Key` SecurityScheme 定义(生产环境可在 UI 中直接调试)
### ⑩ 弹性容错(Resilience4j)
`ChatService` 的 LLM 调用方法上叠加三个注解:
- `@RateLimiter`:限流 20 次/秒(`llm-rate-limit`)
- `@Retry`:失败重试 3 次,指数退避(1s -> 2s -> 4s),仅对网络/超时异常重试
- `@CircuitBreaker`:熔断器(失败率 50% 阈值,滑动窗口 20 次调用,开启 30s 后半开恢复)
### ⑪ 输入校验
- `@Valid` + `@NotBlank` / `@Size` 校验请求体与请求参数
- 校验失败由 `GlobalExceptionHandler` 统一转为 `ApiResult` 错误响应
### ⑫ CORS 跨域
- `WebConfig` 对 `/api/**` 配置跨域
- dev 默认通配符 `*`(关闭凭证);prod 通过 `app.cors.allowed-origins` 配置白名单
### ⑬ 可观察性
- Spring Boot Actuator:`/actuator/health`(含 K8s 健康探针)、`/actuator/metrics`、`/actuator/env`
- Micrometer 指标按应用名打标
- **分布式追踪**:Micrometer Tracing (Brave) + Zipkin Reporter,采样率 `TRACING_SAMPLING`(默认 0.1)
- **结构化日志**:logback-spring.xml,prod 输出 JSON 格式
- Advisor 链 DEBUG 级别日志
### ⑭ 多环境 Profile
| Profile | 认证 | 向量库 | 日志 | 用途 |
|---|---|---|---|---|
| **dev**(默认) | 关闭 | SimpleVectorStore(内存) | DEBUG | 本地学习调试 |
| **prod** | API Key 必需 | PgVector(PostgreSQL 持久化) | INFO + JSON | 生产部署 |
### ⑮ Skills(技能:按需加载的程序性知识)
与 Tool(可执行动作)、RAG(静态知识)互补的第三种能力——**程序性知识**(工作流程指令),提供"被动注入 + 主动读取"双路径:
- **技能定义**:`resources/skills//SKILL.md`(YAML frontmatter:`name`/`description`/`keywords` + 正文指令),新增技能只需加文件,无需改代码
- **被动注入**:`SkillAdvisor`(HIGHEST_PRECEDENCE + 150)对用户输入做关键词匹配,命中后把技能指令追加到系统提示(单次最多注入 2 个),`/api/chat`、`/api/agent` 自动生效
- **主动读取**:`SkillTools.readSkill(name)` 暴露为工具,模型在 ReAct 循环中按名称加载技能指令
- **注册中心**:`SkillRegistry` 启动时扫描解析 SKILL.md(frontmatter 不依赖 YAML 库),提供 `list`/`findByName`/`match`
- **管理接口**:`/api/skills` 列表与详情,`/api/skills/match` 关键词匹配演示(不调用 LLM,无需 API Key 即可验证)
- 内置示例技能:`code-review`(代码审查)、`sql-optimization`(SQL 优化)
> 注意:Spring AI 2.0.1 的 `Prompt.augmentSystemMessage(String)` 是**替换**语义(`mutate().text(...)`),追加系统提示必须使用 `Function` 重载读取原文本后拼接——`SkillAdvisor` 中有详细注释说明。
## API 接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/api/chat` | 多轮对话(含记忆,body: `{message, sessionId}`) |
| POST | `/api/agent` | ReAct Agent 工具调用(body: `{task, sessionId}`) |
| POST | `/api/rag` | RAG 知识库问答(body: `{message, sessionId}`) |
| GET | `/api/structured?city=北京` | 结构化输出(返回 WeatherForecast 实体) |
| GET | `/api/stream?message=你好&sessionId=s1` | SSE 流式输出 |
| GET | `/api/skills` | 技能列表(name/description/keywords) |
| GET | `/api/skills/{name}` | 技能详情(含完整指令正文) |
| POST | `/api/skills/match` | 关键词匹配演示(body: `{message}`,不调用 LLM) |
| GET | `/api/mcp/tools` | 列出远程 MCP 工具(名->描述) |
| POST | `/api/mcp/call?tool=serverTime` | 显式调用远程 MCP 工具 |
| GET | `/swagger-ui.html` | Swagger UI 在线文档 |
| GET | `/v3/api-docs` | OpenAPI 3.0 JSON |
| GET | `/actuator/health` | 健康检查(含 liveness/readiness 探针) |
> prod 环境下除放行路径(Actuator/Swagger/MCP SSE)外,所有请求需携带 `X-API-Key` 请求头。
## 环境要求
1. **JDK 21+**(已在 Java 25 上测试通过)
2. **Maven 3.9+**
3. **DeepSeek API Key** — 从 [https://platform.deepseek.com/api_keys](https://platform.deepseek.com/api_keys) 获取
4. **PostgreSQL**(仅 prod profile 需要,dev 用内存实现)
5. 首次运行需联网(下载 ~90MB Embedding 模型)
## 快速开始
### 开发环境(默认,无需 PostgreSQL)
```bash
# 1. 设置 API Key(Windows PowerShell)
$env:DEEPSEEK_API_KEY="sk-你的key"
# Linux/Mac
export DEEPSEEK_API_KEY="sk-你的key"
# 2. 启动(默认激活 dev profile)
mvn spring-boot:run
# 3. 测试
curl http://localhost:8087/actuator/health
curl -X POST http://localhost:8087/api/chat -H "Content-Type: application/json" -d '{"message":"你好"}'
```
### 生产环境
```bash
# 1. 准备 PostgreSQL(含 pgvector 扩展)
# 2. 设置环境变量
export DEEPSEEK_API_KEY="sk-你的key"
export APP_API_KEY="$(openssl rand -hex 24)" # 必须为安全随机值,默认占位值会拒绝启动
export POSTGRES_URL="jdbc:postgresql://localhost:5432/agentdb"
export POSTGRES_USER="agent"
export POSTGRES_PASSWORD="agent123"
export CORS_ORIGINS="https://your-domain.com"
# 3. 打包并启动
mvn package
SPRING_PROFILES_ACTIVE=prod java -jar target/agent-demo-0.0.1-SNAPSHOT.jar
# 4. 带 API Key 调用
curl -H "X-API-Key: $APP_API_KEY" -X POST http://localhost:8087/api/chat \
-H "Content-Type: application/json" -d '{"message":"你好"}'
```
## 配置说明
`application.yml` 关键配置:
```yaml
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev} # 默认 dev
ai:
deepseek:
api-key: ${DEEPSEEK_API_KEY} # 必填:设置环境变量
chat:
model: deepseek-chat # 或 deepseek-reasoner(带思维链)
timeout: ${DEEPSEEK_TIMEOUT:30000} # P0: 超时防 LLM 卡死
mcp:
client:
enabled: false # 设为 true 消费远程 MCP 工具
server:
port: 8087
springdoc:
swagger-ui:
path: /swagger-ui.html
resilience4j:
ratelimiter:
instances:
llm-rate-limit: { } # 20 req/s
retry:
instances:
llm-retry: { } # 3 次指数退避
circuitbreaker:
instances:
llm-circuit-breaker: { } # 50% 失败率熔断
management:
tracing:
sampling:
probability: ${TRACING_SAMPLING:0.1} # Zipkin 采样率
```
`application-prod.yml` 额外项:
```yaml
app:
security:
enabled: true
api-key: ${APP_API_KEY} # 生产必填
permit-paths: /actuator/**,/swagger-ui/**,/sse/** # 免认证路径
cors:
allowed-origins: ${CORS_ORIGINS} # 生产 CORS 白名单
spring:
ai:
vectorstore:
pgvector:
index-type: hnsw
dimensions: 384
datasource:
url: ${POSTGRES_URL:jdbc:postgresql://localhost:5432/agentdb}
```
## 单元测试
共 115 个测试(含 5 个集成测试),覆盖全部分层:
```bash
mvn test # 纯单元测试(无需 Docker)
mvn verify # 含 Testcontainers 集成测试(需 Docker)
```
| 层 | 测试类 | 测试数 |
|---|---|---|
| domain | ChatRequest/ChatResponse/AgentRequest、WeatherForecast、Skill、ApiResult | 17 |
| tool | WeatherTools、CalculatorTools、KnowledgeBaseTools、SkillTools | 18 |
| advisor | SafeGuardAdvisor、LoggingAdvisor、SkillAdvisor | 26 |
| service | ChatService、AgentService、RagService、McpClientService | 11 |
| controller | AgentController、McpController、SkillController | 15 |
| infra | GlobalExceptionHandler、SkillRegistry | 15 |
| config | ChatMemoryConfig、SecurityConfig | 8 |
| integration | AgentDemoIntegrationTest、TestcontainersPgVectorTest(需 Docker) | 5 |
## 项目结构
```
src/main/java/com/example/agentdemo/
├── AgentDemoApplication.java # 启动类
├── config/
│ ├── AiConfig.java # ChatClient + Advisor 链 + 默认工具装配
│ ├── AdvisorConfig.java # 注册 LoggingAdvisor、SafeGuardAdvisor bean
│ ├── ChatMemoryConfig.java # 内存仓库 + 20 条滑动窗口记忆
│ ├── VectorStoreConfig.java # dev: SimpleVectorStore(@Profile("!prod"))
│ ├── SecurityConfig.java # prod: API Key 认证 + 暴力破解防护
│ ├── DevSecurityConfig.java # dev: 关闭认证(@Profile("dev"))
│ ├── OpenApiConfig.java # Swagger/OpenAPI 元数据 + SecurityScheme
│ └── WebConfig.java # CORS 跨域配置
├── advisor/
│ ├── LoggingAdvisor.java # 全链路日志 + 耗时记录
│ ├── SafeGuardAdvisor.java # 输入防护(越狱关键词检测)
│ └── SkillAdvisor.java # Skills 关键词命中后注入系统提示
├── tool/
│ ├── WeatherTools.java # 天气查询(模拟数据)
│ ├── CalculatorTools.java # 四则运算计算器
│ ├── KnowledgeBaseTools.java # 向量库检索
│ └── SkillTools.java # read_skill 技能读取工具
├── service/
│ ├── ChatService.java # 对话 + 结构化输出 + 流式(Resilience4j 注解)
│ ├── AgentService.java # ReAct Agent(本地 + MCP 工具)
│ ├── RagService.java # RAG 问答
│ └── McpClientService.java # MCP 客户端工具消费
├── controller/
│ ├── AgentController.java # /api/chat、/api/agent、/api/rag 等(Swagger 注解)
│ ├── McpController.java # /api/mcp/tools、/api/mcp/call
│ └── SkillController.java # /api/skills 列表/详情/匹配
├── infra/
│ ├── McpServerTools.java # MCP Server 暴露的工具
│ ├── SkillRegistry.java # Skills 注册中心(扫描/解析/关键词匹配)
│ ├── KnowledgeLoader.java # 启动时加载 markdown 到向量库
│ └── GlobalExceptionHandler.java # 全局异常 + 校验失败 -> ApiResult
├── domain/
│ ├── dto/ # ChatRequest、ChatResponse、AgentRequest
│ └── model/ # WeatherForecast(结构化输出)、Skill(技能)
└── support/
└── ApiResult.java # 统一响应包装
src/main/resources/
├── application.yml # 主配置(DeepSeek/MCP/Actuator/Resilience4j/springdoc)
├── application-dev.yml # dev profile(内存向量库、关闭认证)
├── application-prod.yml # prod profile(PgVector、API Key 认证、CORS)
├── logback-spring.xml # 日志(prod JSON 结构化)
├── data/*.md # RAG 知识库文档(3 篇)
├── skills/*/SKILL.md # Skills 技能定义(code-review、sql-optimization)
└── prompts/ # 提示词模板(react-system.st、rag-system.st)
```
## License
MIT