# 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