# ai-middle-platform **Repository Path**: wenbina/ai-middle-platform ## Basic Information - **Project Name**: ai-middle-platform - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-13 - **Last Updated**: 2026-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI 中间平台(ai-middle-platform) 统一封装多模型调用的 AI 中台:**模型注册 → 路由 → 降级 → 调用审计**,并内置 **MCP(Model Context Protocol)** 能力,既是 MCP Server(对外暴露平台工具),也是 MCP Client(接入外部 MCP Server 供模型调用)。 ## 技术栈 | 组件 | 版本 | |---|---| | Spring Boot | 4.0.7 | | Spring AI | 2.0.0(DeepSeek + MCP) | | Java | 21 | | 构建 | Maven(项目级配置见 `.mvn/`) | ## 构建与运行 > **为什么有 `.mvn/settings.xml`?** 本机 HTTPS/TLS 不可用(schannel 报 `SEC_E_NO_CREDENTIALS`), > 官方/阿里云 Maven 仓库无法访问,因此项目级配置把镜像切到华为云**纯 HTTP** 地址, > 并把本地仓库放到项目内 `.m2-repo/`(已加入 .gitignore)。网络恢复后可删除 `.mvn/` 恢复默认。 ```bash # 需要 JDK 21(本机位于 E:\java\jdk21,设置 JAVA_HOME 后运行) set JAVA_HOME=E:\java\jdk21 mvn compile mvn spring-boot:run ``` 应用默认端口 `8080`,默认激活 **dev** 环境。 ## 前端控制台(Vue MVP) 同仓 [`frontend/`](frontend/):对话(SSE)+ 知识入库/检索 + 登录注册。开发时 Vite 把 `/api`、`/v1` 代理到 `http://localhost:8080`,后端另开了 CORS 允许 `localhost:5173`。 ```bash # 终端 1:后端(需先执行 sql/mysql-init.sql,含 sys_user) mvn spring-boot:run # 终端 2:前端 cd frontend npm install npm run dev ``` 浏览器打开 `http://localhost:5173`。首次可用默认管理员 `admin / admin123`,或自行注册。 导航:对话 / 知识库;侧栏可退出登录。 ## 分环境配置 | Profile | 文件 | 说明 | |---|---|---| | `dev` | `application-dev.yml` | 本地开发,DB/Redis 有本地默认值,业务日志 DEBUG | | `test` | `application-test.yml` | 测试环境,密码等建议用环境变量 | | `prod` | `application-prod.yml` | 生产环境,`DB_*` / `REDIS_HOST` / `MCP_*` 等必填 | 切换方式任选其一: ```bash # 1) 环境变量 set SPRING_PROFILES_ACTIVE=prod # 2) 启动参数 mvn spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=test # 3) IDEA Active profiles: dev / test / prod ``` 本地可覆盖敏感项:复制 `.env.example` 为环境变量,或新建 gitignore 的 `application-local.yml` 并在 `application.yml` 中 `include: local`(按需)。 ## 核心接口 | 接口 | 说明 | |---|---| | `POST /api/ai/chat` | 普通对话,Body: `{"message": "...", "conversationId": 1, "agentId": 1, "modelCode": "deepseek-chat"}` | | `POST /api/ai/embedding` | 文本向量化,Body: `{"text": "...", "modelCode": "text-embedding-v3"}`(modelCode 可空) | | `POST /api/ai/knowledge/upload` | 上传文档入库(txt/md/pdf/docx),自动解析切分 Embedding | | `POST /api/ai/knowledge/index` | 知识入库(JSON chunks,调试用) | | `POST /api/ai/knowledge/search` | 相似度检索(仅当前用户文档) | | `GET /api/ai/knowledge/documents` | 当前用户文档列表 | | `GET /api/ai/knowledge/documents/{id}/chunks` | 文档分片 | | `DELETE /api/ai/knowledge/documents/{id}` | 删除文档及向量 | | `POST /mcp` | **MCP Server** 端点(Streamable HTTP 传输),暴露平台工具 | ## MCP Server(对外提供工具) 启动后 `/mcp` 暴露平台工具。Spring AI 的 MCP Server starter 会自动收集容器内 **所有** `ToolCallbackProvider` bean 并转换为 MCP 工具: - 平台 MCP 工具:`runtime/mcp/PlatformMcpTools`(经 `config/McpConfig` 注册为 `MethodToolCallbackProvider`) - 本地工具注册表:`runtime/tool/provider/RegistryToolCallbackProvider`(实现 Spring AI 接口后自动加入,如 `date_time`) | 工具 | 说明 | |---|---| | `platform_list_models` | 列出平台注册的所有模型(key/provider/优先级/启用状态) | | `platform_chat` | 通过平台调用大模型,**主模型失败自动降级**到备用模型 | | `date_time` | 本地注册表示例工具:查当前日期 / 日期加天数 | `platform_chat` 内部走 `ModelRouter` + `ModelExecutor`(含 `FallbackPolicy` 降级链), 即外部通过 MCP 调用平台时同样享受中台的路由/降级能力。 ### 手动验证(curl 走一遍 Streamable HTTP 握手) ```bash # 1. initialize(拿到 Mcp-Session-Id) curl -i -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' # 2. 通知 initialized(带 Session-Id) curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -H "Mcp-Session-Id: " \ -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' # 3. 列出工具 curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -H "Mcp-Session-Id: " \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' # 4. 调用工具 curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -H "Mcp-Session-Id: " \ -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"platform_list_models","arguments":{}}}' ``` > 注意:Windows 下 curl 的 JSON 中文要用 UTF-8 文件 + `--data-binary @file`,避免命令行编码问题。 ## MCP Client(接入外部 MCP Server) 拿到外部 MCP Server 地址后,编辑 `src/main/resources/application.yml`, 在 `spring.ai.mcp.client` 下取消注释并填写(两种传输任选其一): ```yaml spring: ai: mcp: client: sse: connections: my-server: url: http://your-mcp-server:port/sse # streamable-http: # connections: # my-server: # url: http://your-mcp-server:port/mcp ``` 重启后,外部 Server 的工具会以 ToolCallback 形式被 `ChatService` 汇总, 在 `/api/ai/chat` 对话中模型即可按需调用。 ## 工具治理框架(平台本地工具) 平台内置一套工具治理框架(`domain/tool` + `runtime/tool`), 提供 **注册 → 治理 → 执行 → 适配 Spring AI** 的完整链路: | 组件 | 说明 | |---|---| | `AiTool` | 平台工具接口:`definition()`(Spring AI 定义)、`execute(ToolRequest)`、`governance()`(来源/分类/权限/审计/超时) | | `ToolRegistry` | 工具注册中心(`ToolRegistryInitializer` 启动时自动注册所有 `AiTool` bean) | | `ToolExecutor` | 统一执行入口,失败时记录日志并返回结构化错误 | | `SpringAiToolAdapter` | 把 `AiTool` 适配为 Spring AI `ToolCallback`(schema、结果序列化、错误处理) | | `RegistryToolCallbackProvider` | 实现 Spring AI `ToolCallbackProvider` 接口,从注册表批量生成 `ToolCallback` 列表(ChatClient 与 MCP Server 两处生效) | ### 如何新增一个工具 ```java @Component public class MyTool implements AiTool { @Override public Class inputType() { return MyRequest.class; // 请求参数 DTO(用于生成 JSON Schema) } @Override public ToolDefinition definition() { return ToolDefinition.builder() .name("my_tool") // 模型看到的工具名 .description("工具说明,让模型知道何时使用") .inputSchema(ToolSchemaGenerator.generate(MyRequest.class)) .build(); } @Override public ToolGovernance governance() { return ToolGovernance.builder() .source(ToolSource.LOCAL.name()) .category(ToolCategory.SYSTEM.name()) .audit(true) .timeout(3000) .build(); } @Override public ToolResult execute(ToolRequest request) { MyRequest params = new ObjectMapper().convertValue( request.getArguments(), MyRequest.class); // 业务逻辑... return ToolResult.success(result); } } ``` 无需其他配置——启动时自动注册,`ChatService` 会自动把它带给模型。 ### 对话中可用的工具(实测) | 工具 | 来源 | 说明 | |---|---|---| | `date_time` | 本地注册表(示例) | 查当前日期 / 日期加天数 | | `platform_list_models` | 平台 MCP 工具 | 列出注册模型 | | `platform_chat` | 平台 MCP 工具 | 通过平台调模型(带降级) | | 外部 MCP Server 工具 | MCP Client | 配置连接后自动加入 | ## 架构速览 ``` api/ AI 对外 HTTP(chat / embedding / knowledge) application/ 用例编排(ChatService、EmbeddingService、AiContext) runtime/ AI 引擎(不依赖 api;无 REST / CRUD) ├ model/domain/ 运行时对象 ModelDefinition / ModelRoute ├ model/client/ ChatClientFactory ├ model/registry|factory|routing|execution|fallback ├ tool/ ToolCallback 适配与护栏 ├ memory/ 调用期缓冲记忆 ├ advisor/ Spring AI Advisors └ mcp/ MCP Client / 平台工具 业务域(可有自己的 Controller / Mapper) ├ model/ ai_model_config 持久化 + ModelType/Feature ├ tool/ 工具定义 CRUD + 执行日志 ├ memory/ 会话/消息落库 ├ vector/ pgvector 入库与检索 └ contract/ 电子签(独立域,不进 runtime) common / config / auth / event / observability ``` ### 包边界硬规则 1. 依赖方向:`api → application → runtime`,application/runtime 可读业务域;**业务域 ↛ runtime**,**runtime ↛ api** 2. 同名概念拆开:`memory`=落库,`runtime.memory`=调用期记忆;`tool`=元数据,`runtime.tool`=执行适配;`model`=DB 配置,`runtime.model.domain`=运行时定义 3. 新能力优先加 `runtime` Strategy/Adapter,而不是改 Registry 结构或往业务域塞引擎代码 4. `contract` 保持独立业务域,不进入 AI runtime 模型配置源:**数据库 `ai_model_config`**。`capabilities` 首项为主类型(CHAT/EMBEDDING/...),其后为特性标签。 ## 已知问题 / 待办 - 模型 `api_key` 存数据库,建议后续加密或接入密钥管理 - 流式对话暂未做 fallback 切换 - 模型配置热更新(上下架后刷新 Registry)尚未接入,目前需重启 - 项目同时存在两套 Jackson(Boot 4 Jackson 3 + 部分 Jackson 2),后续可统一 - 告警规则需在 Prometheus/Grafana(或贵司监控平台)侧配置,本仓库只导出指标 ## 观测指标 | 指标 | 说明 | 常用标签 | |---|---|---| | `ai.chat.calls` | 对话调用次数 | `success`, `model` | | `ai.chat.latency` | 对话耗时 | `success`, `model` | | `ai.chat.tokens` | Token 用量 | `type=input\|output\|total`, `model` | | `ai.tool.calls` | 工具调用次数 | `success`, `tool` | | `ai.tool.latency` | 工具耗时 | `success`, `tool` | ```bash # Prometheus 抓取 curl http://localhost:8080/actuator/prometheus | findstr ai_chat # 或 Actuator metrics curl http://localhost:8080/actuator/metrics/ai.chat.calls ```