# LLM Provider 管理与模型工厂 **本文引用的文件** - [pom.xml](file://pom.xml) - [ai-server/pom.xml](file://ai-server/pom.xml) - [AI_AGENT.md](file://AI_AGENT.md) - [AgentEntity.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentEntity.java) - [AgentModel.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModel.java) - [AgentModelProvider.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModelProvider.java) - [ModelTypeEnum.java](file://ai-server/src/main/java/com/zsjz/ai/common/enums/ModelTypeEnum.java) - [AgentModelService.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelService.java) - [AgentModelServiceImpl.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java) - [AgentModelFactory.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java) - [ModelConnectivityTester.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java) - [EmbeddingModelFactory.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/rag/EmbeddingModelFactory.java) - [AgentModelController.java](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java) - [application-dev.yaml](file://ai-server/src/main/resources/application-dev.yaml) ## 目录 1. [引言](#引言) 2. [项目结构与定位](#项目结构与定位) 3. [依赖与扩展边界](#依赖与扩展边界) 4. [核心组件总览](#核心组件总览) 5. [架构与数据流](#架构与数据流) 6. [详细组件分析](#详细组件分析) 7. [模型配置项与枚举](#模型配置项与枚举) 8. [可用性与健康检查策略](#可用性与健康检查策略) 9. [操作示例](#操作示例) 10. [依赖关系分析](#依赖关系分析) 11. [性能与可观测性](#性能与可观测性) 12. [问题排查指南](#问题排查指南) 13. [结论](#结论) ## 引言 本文围绕 `ai-server` 模块中的 **LLM Provider 管理** 与 **模型工厂** 展开,目标是帮助开发者理解: - Maven 依赖中 `agentscope-harness`、DashScope/Ollama/OpenAI 扩展、mem0、redis 等扩展的作用; - 数据库实体 `AgentModel`、服务 `AgentModelService`、工厂 `AgentModelFactory` 如何协作; - 如何通过 AgentScope SPI 将 DB 中的模型配置注册为 DashScope、Ollama、OpenAI 三种 Provider; - 模型配置项、用途类型枚举、默认模型隔离规则; - 连通性测试与健康检查机制; - 新增一个 LLM Provider、切换默认模型、通过控制器查询模型列表和状态的完整流程。 本项目的 Agent 子系统基于 **AgentScope 2.0.4**,后端是 Spring Boot 3 + MyBatis-Plus,模型配置不写 YAML,而是持久化到平台库的 `agent_model` / `agent_model_provider` 表,由前端「模型管理」界面维护。 **章节来源** - [AI_AGENT.md:1-60](file://AI_AGENT.md#L1-L60) - [AI_AGENT.md:160-180](file://AI_AGENT.md#L160-L180) ## 项目结构与定位 仓库根 `pom.xml` 是一个聚合 POM,打包类型为 `pom`,包含 `ai-server` 与 `ai-gateway` 两个子模块。LLM Provider 管理与模型工厂全部位于 `ai-server` 模块的 `com.zsjz.ai.module.agent` 包下。 ```mermaid graph TB Root["根 POM
zsjz-ai"] --> Server["ai-server
Spring Boot 3 + AgentScope"] Root --> Gateway["ai-gateway
Spring Cloud Gateway"] Server --> Controller["AgentModelController
/models/*"] Server --> Service["AgentModelService
模型配置服务"] Server --> Factory["AgentModelFactory
AgentScope Model 工厂"] Server --> Tester["ModelConnectivityTester
连通性测试"] Server --> Entities["AgentModel / AgentModelProvider"] Server --> EnumType["ModelTypeEnum
llm / embedding"] ``` **图表来源** - [pom.xml:1-23](file://pom.xml#L1-L23) - [AgentModelController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L1-L91) - [AgentModelService.java:1-70](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelService.java#L1-L70) - [AgentModelFactory.java:1-131](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L1-L131) - [AgentModel.java:1-63](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModel.java#L1-L63) - [AgentModelProvider.java:1-43](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModelProvider.java#L1-L43) - [ModelTypeEnum.java:1-54](file://ai-server/src/main/java/com/zsjz/ai/common/enums/ModelTypeEnum.java#L1-L54) **章节来源** - [pom.xml:1-23](file://pom.xml#L1-L23) - [AI_AGENT.md:20-60](file://AI_AGENT.md#L20-L60) ## 依赖与扩展边界 `ai-server/pom.xml` 集中声明了 AgentScope 及其扩展依赖。它们不是业务逻辑代码,而是让 Spring Boot 应用能接入不同大模型厂商或本地推理服务的运行时扩展。 | 依赖 | 作用 | 在本项目中的意义 | |---|---|---| | `agentscope-harness` | AgentScope 运行期入口,提供 HarnessAgent、Toolkit、中间件等基础设施 | 本项目不引入外部 MCP SDK(显式排除 `io.modelcontextprotocol.sdk:mcp`),只使用其 Agent 编排能力 | | `agentscope-extensions-model-dashscope` | DashScope 原生协议的 LLM 与 Embedding 扩展 | 用于阿里通义 DashScope 对话与向量模型 | | `agentscope-extensions-model-ollama` | Ollama 本地推理服务的扩展 | 用于本地 llama.cpp / Ollama 兼容端点 | | `agentscope-extensions-model-openai` | OpenAI 兼容协议的扩展 | 作为兜底协议前缀,覆盖 OpenAI、DeepSeek、GLM、Kimi、Ollama 兼容服务等 | | `agentscope-extensions-mem0` | Agent 记忆扩展 | 与 Redis 配合实现会话级或用户级记忆存储 | | `agentscope-extensions-rag-simple` | 简易 RAG 扩展 | 配合 pgvector 做模板元数据语义检索 | | `agentscope-extensions-redis` | Redis 扩展 | 用于 mem0、缓存与会话相关能力 | 注意:**Provider 名称不等于模型用途类型**。 - `agent_model.type` 表示用途:`llm`(对话)或 `embedding`(向量)。 - `agent_model_provider.type` 表示厂商协议:如 `OLLAMA`、`OPENAI`、`DASHSCOPE`、`DEEPSEEK`。 **章节来源** - [ai-server/pom.xml:1-200](file://ai-server/pom.xml#L1-L200) - [AI_AGENT.md:140-180](file://AI_AGENT.md#L140-L180) - [ModelTypeEnum.java:1-54](file://ai-server/src/main/java/com/zsjz/ai/common/enums/ModelTypeEnum.java#L1-L54) ## 核心组件总览 | 组件 | 职责 | 关键行为 | |---|---|---| | `AgentModel` | 持久化单个模型配置 | 保存 provider、modelId、apiKey、baseUrl、type、config、headersJson、extraJson、status | | `AgentModelProvider` | 持久化厂商元信息 | 保存 name、baseUrl、config、status、modelsEndpoint、headersJson、extraJson、type | | `AgentModelService` | 模型配置的 CRUD、默认模型、连通性测试 | 按 type 隔离默认模型,删除采用软删除 | | `AgentModelFactory` | DB 配置 → AgentScope `Model` 实例 | 根据 provider 归一化为 `dashscope:` 或 `openai:` 前缀,构造 `ModelCreationContext` | | `ModelConnectivityTester` | 最小化真实调用验证连通性 | 对话模型 ping 首个流式分片,向量模型做一次嵌入 | | `EmbeddingModelFactory` | RAG 用向量模型构建 | 按 provider 选择 Ollama / DashScope / OpenAI 嵌入实现 | | `AgentModelController` | REST API 暴露模型管理能力 | `/models/providers`、`/models`、`/{id}/default`、`/{id}/test` | | `AgentEntity` | 用户自定义 Agent 定义 | 携带 model、tools、skills、sandboxMode、sandboxScope 等业务字段,但不直接承载 LLM Provider 配置 | ```mermaid classDiagram class AgentModel { +Long id +String provider +String modelId +String name +String type +String apiKey +String baseUrl +Boolean defaultModel +String config +String headersJson +String extraJson +StatusEnum status } class AgentModelProvider { +Long id +String name +String baseUrl +String config +String status +String modelsEndpoint +String headersJson +String extraJson +String type } class AgentModelService { +listActiveModels() +createModel(dto) +updateModel(id, dto) +deleteModel(id) +setDefaultModel(id) +testModel(id) +getDefaultModelId() +getDefaultModelId(type) } class AgentModelFactory { +create(config) +create(config, enableThinkingOverride) -resolvePrefix(provider) -readEnableThinking(config) } class ModelConnectivityTester { +test(config) -testChat(config) -testEmbedding(config) -describeFailure(e) } class EmbeddingModelFactory { +create(config) -buildOllama(...) -buildDashScope(...) -buildOpenAI(...) } class AgentModelController { +listProviders() +listModels() +createModel(dto) +updateModel(id, dto) +deleteModel(id) +setDefaultModel(id) +testModel(id) } AgentModelService --> AgentModel : "读写" AgentModelService --> ModelConnectivityTester : "委托测试" ModelConnectivityTester --> AgentModelFactory : "创建 LLM Model" ModelConnectivityTester --> EmbeddingModelFactory : "创建向量模型" AgentModelController --> AgentModelService : "调用" AgentModelController --> AgentModelProviderService : "读取厂商" ``` **图表来源** - [AgentModel.java:1-63](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModel.java#L1-L63) - [AgentModelProvider.java:1-43](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModelProvider.java#L1-L43) - [AgentModelService.java:1-70](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelService.java#L1-L70) - [AgentModelServiceImpl.java:1-198](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L1-L198) - [AgentModelFactory.java:1-131](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L1-L131) - [ModelConnectivityTester.java:1-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L1-L139) - [EmbeddingModelFactory.java:1-144](file://ai-server/src/main/java/com/zsjz/ai/module/agent/rag/EmbeddingModelFactory.java#L1-L144) - [AgentModelController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L1-L91) **章节来源** - [AgentModel.java:1-63](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModel.java#L1-L63) - [AgentModelProvider.java:1-43](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModelProvider.java#L1-L43) - [AgentModelService.java:1-70](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelService.java#L1-L70) - [AgentModelController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L1-L91) ## 架构与数据流 模型管理的整体流程可以拆成两条主线: 1. **配置主线**:前端 → `AgentModelController` → `AgentModelService` → `AgentModelMapper` → PostgreSQL `agent_model` 表。 2. **推理主线**:上层业务 → `AgentModelFactory` → AgentScope `ModelRegistry.resolve(...)` → 具体 Provider(DashScope / OpenAI 兼容 / Ollama)。 ```mermaid sequenceDiagram participant FE as "前端" participant C as "AgentModelController" participant S as "AgentModelService" participant M as "AgentModelMapper" participant DB as "PostgreSQL" participant F as "AgentModelFactory" participant MS as "ModelRegistry" participant P as "DashScope / OpenAI / Ollama" FE->>C: GET /models C->>S: listActiveModels() S->>M: 查询未删除模型 M->>DB: SELECT agent_model DB-->>M: 模型列表 M-->>S: 实体列表 S-->>C: VO 列表 C-->>FE: Result> FE->>C: GET /models/{id}/test C->>S: testModel(id) S->>F: create(config) F->>MS: resolve("dashscope:"|"openai:"+modelId, ctx) MS->>P: 初始化 Provider P-->>MS: Model 实例 MS-->>F: Model F-->>S: Model S->>S: 发起最小化调用 S-->>C: ModelTestResultVO C-->>FE: Result ``` **图表来源** - [AgentModelController.java:32-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L32-L91) - [AgentModelServiceImpl.java:40-198](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L40-L198) - [AgentModelFactory.java:56-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L56-L113) - [ModelConnectivityTester.java:66-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L66-L139) **章节来源** - [AgentModelController.java:32-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L32-L91) - [AgentModelServiceImpl.java:40-198](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L40-L198) - [AgentModelFactory.java:56-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L56-L113) ## 详细组件分析 ### AgentEntity:Agent 定义实体 `AgentEntity` 是用户自定义 Agent 的持久化实体,对应数据库 `agent` 表。它负责保存 Agent 的业务配置,例如: - `ownerId`、`userId`:用户归属; - `isBuiltin`:是否内置专家; - `workspacePath`:Agent 工作区路径; - `name`、`description`、`sysPrompt`:名称、描述、系统提示词; - `model`:绑定的模型标识; - `maxIters`:最大迭代次数; - `toolsAllowJson`、`toolsDenyJson`:工具白名单/黑名单; - `toolPacksJson`:方案绑定的工具包; - `knowledgeBaseId`:知识库 ID; - `skillsAllowJson`、`skillsDenyJson`:技能白名单/黑名单; - `runAs`、`forkOf`:执行身份与 Fork 来源; - `skillRepositoriesJson`:技能仓库配置; - `sandboxMode`、`sandboxScope`:沙箱模式与隔离范围。 需要特别注意:**`AgentEntity` 并不等同于 LLM Provider 配置**。它只是引用某个模型,真正的 Provider、API Key、Base URL、模型 ID 等保存在 `AgentModel` 表中。 **章节来源** - [AgentEntity.java:1-137](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentEntity.java#L1-L137) ### AgentModel:模型配置实体 `AgentModel` 是 LLM Provider 管理的核心数据对象,对应 `agent_model` 表。关键字段如下: | 字段 | 含义 | 约束与行为 | |---|---|---| | `provider` | 用户填写的 Provider 名称 | 会被 `AgentModelFactory` 归一化为 DashScope 或 OpenAI 兼容前缀 | | `modelId` | 模型 ID | 必填;缺失时工厂抛 400 | | `name` | 模型展示名 | 日志与调试用 | | `type` | 用途类型 | `llm` 或 `embedding`;历史空值按 `llm` 处理 | | `apiKey` | API 密钥 | 必填;缺失时工厂抛 400 | | `baseUrl` | 基础地址 | 可选;DashScope 通常为空,OpenAI 兼容或 Ollama 常填本地或代理地址 | | `defaultModel` | 是否默认模型 | 同类型互斥,设置时清除其他同类型默认 | | `config` | JSON 扩展配置 | 支持 `enableThinking` 控制思考模式 | | `headersJson` | 请求头扩展 | JSON 字符串 | | `extraJson` | 扩展字段 | JSON 字符串 | | `status` | 状态 | 使用 `StatusEnum`,删除为软删除 | **章节来源** - [AgentModel.java:1-63](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModel.java#L1-L63) ### AgentModelProvider:厂商实体 `AgentModelProvider` 对应 `agent_model_provider` 表,主要用于前端展示可用的厂商选项。它不是 AgentScope 的 Provider 注册中心,而是平台侧的厂商元数据表。 | 字段 | 含义 | |---|---| | `name` | 厂商名称 | | `baseUrl` | 厂商默认 Base URL | | `config` | 厂商级 JSON 配置 | | `status` | 启用状态,查询时使用 `"active"` | | `modelsEndpoint` | 厂商模型列表接口地址 | | `headersJson` | 厂商级请求头 | | `extraJson` | 厂商级扩展字段 | | `type` | 厂商协议类型,如 `OLLAMA`、`OPENAI`、`DASHSCOPE`、`DEEPSEEK` | **章节来源** - [AgentModelProvider.java:1-43](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModelProvider.java#L1-L43) - [AgentModelProviderServiceImpl.java:23-42](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelProviderServiceImpl.java#L23-L42) ### AgentModelService:模型配置服务 `AgentModelService` 是模型管理的业务门面,提供以下能力: 1. **列出启用模型**:过滤已删除记录,按默认模型和创建人排序。 2. **创建模型**:生成 ID、填充默认值、写入 DB;若设为默认,则清除同类型其它默认。 3. **更新模型**:按 ID 更新;若表单标记为默认,同样清理同类型其它默认。 4. **删除模型**:软删除,将状态改为 `DELETED`。 5. **设置默认模型**:按 `type` 隔离,`llm` 与 `embedding` 各自独立默认。 6. **测试连通性**:委托 `ModelConnectivityTester` 对已保存配置发起真实调用。 7. **获取默认模型 ID**:优先返回 `llm` 类型默认;也可按传入 type 查询。 默认模型的隔离规则非常关键: - 历史数据中 `type` 可能为空; - 空值在查询和清除默认时都视为 `llm`; - 因此设置一个 `embedding` 默认不会清掉 `llm` 默认,反之亦然。 **章节来源** - [AgentModelService.java:1-70](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelService.java#L1-L70) - [AgentModelServiceImpl.java:40-198](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L40-L198) ### AgentModelFactory:AgentScope 模型工厂 `AgentModelFactory` 是连接平台模型配置与 AgentScope 运行时的重要桥梁。它的核心职责是: 1. 校验 `AgentModel` 配置是否完整; 2. 将 `provider` 归一化为 AgentScope SPI 前缀; 3. 构造 `ModelCreationContext`,携带 apiKey、baseUrl、stream、结构化输出选项、思考模式; 4. 调用 `ModelRegistry.resolve(modelRef, ctx)` 创建实际 Model 实例。 Provider 归一化规则: | 输入 provider | 结果前缀 | 说明 | |---|---|---| | 包含 `dashscope` 或 `阿里`(忽略大小写) | `dashscope:` | 使用 DashScope 原生协议 | | 其余任何值 | `openai:` | 使用 OpenAI 兼容协议,通过 baseUrl 指向各厂商 | 这意味着: - DashScope 走原生 Provider; - OpenAI、DeepSeek、GLM、Kimi、Ollama 兼容端点都可以通过 `openai:` 前缀 + baseUrl 接入; - 不需要为每个兼容厂商新增 Java 类,只要其 API 符合 OpenAI 兼容协议即可。 思考模式开关: - 如果调用方显式传 `enableThinkingOverride`,优先使用该值; - 否则从 `config` JSON 中读取 `enableThinking`; - 都没有则交给各 Provider 的默认行为; - 该设计避免推理模型 reasoning 吃光 maxTokens,导致正文为空。 **章节来源** - [AgentModelFactory.java:1-131](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L1-L131) ### ModelConnectivityTester:连通性测试器 `ModelConnectivityTester` 不负责业务推理,只负责“这个模型配置能不能用”。 测试策略: | 模型类型 | 测试方式 | 超时 | 失败处理 | |---|---|---|---| | `llm` | 发送文本 `"ping"`,取第一个流式分片后取消 | 20 秒 | 返回 `success=false`,不抛异常 | | `embedding` | 对固定短文本做一次向量化 | 20 秒 | 返回空向量时报错 | 重要特性: - 失败是预期结果,HTTP 返回 200,错误信息放在返回值中; - 超时异常被识别并翻译为“连接超时(N s),请检查网络与 Base URL”; - 非超时异常会提取根因消息,并限制最大长度; - 向量模型返回空向量被视为失败; - 测试耗时会记录到 `latencyMs`。 **章节来源** - [ModelConnectivityTester.java:1-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L1-L139) ### EmbeddingModelFactory:RAG 向量模型工厂 `EmbeddingModelFactory` 面向 RAG 场景,根据 provider 选择具体嵌入实现: | provider | 嵌入实现 | 特点 | |---|---|---| | `ollama` | `OllamaTextEmbedding` | 本地端点,无需 apiKey | | `dashscope` | `DashScopeTextEmbedding` | 原生 DashScope 向量模型 | | 其他 | `OpenAITextEmbedding` | OpenAI 兼容协议,靠 baseUrl 指向 | 它与 `AgentModelFactory` 的区别在于: - `AgentModelFactory` 用于 LLM 对话; - `EmbeddingModelFactory` 用于向量嵌入; - 两者共用 `AgentModel` 配置,但构造的对象不同。 **章节来源** - [EmbeddingModelFactory.java:1-144](file://ai-server/src/main/java/com/zsjz/ai/module/agent/rag/EmbeddingModelFactory.java#L1-L144) ### AgentModelController:REST API 控制器暴露 `/models` 下的模型管理能力: | 方法 | 路径 | 功能 | 返回 | |---|---|---|---| | `GET` | `/models/providers` | 查询启用的厂商列表 | `Result>` | | `GET` | `/models` | 查询启用的模型列表 | `Result>` | | `POST` | `/models` | 创建模型 | `Result` | | `PUT` | `/models/{id}` | 更新模型 | `Result` | | `GET` | `/models/{id}` | 删除模型(软删除) | `Result` | | `GET` | `/models/{id}/default` | 设置默认模型 | `Result` | | `GET` | `/models/{id}/test` | 测试模型连通性 | `Result` | **章节来源** - [AgentModelController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L1-L91) ## 模型配置项与枚举 ### 模型配置项 | 配置项 | 所属实体 | 是否必填 | 说明 | |---|---|---:|---| | `provider` | `AgentModel` | 建议填写 | 用户自由填写;工厂按规则归一化为 DashScope 或 OpenAI 兼容 | | `modelId` | `AgentModel` | 是 | 模型 ID;缺失时 `AgentModelFactory` 抛 400 | | `name` | `AgentModel` | 否 | 展示名称 | | `type` | `AgentModel` | 建议填写 | `llm` 或 `embedding`;空值按 `llm` 处理 | | `apiKey` | `AgentModel` | 是 | API Key;缺失时 `AgentModelFactory` 抛 400 | | `baseUrl` | `AgentModel` | 否 | Base URL;DashScope 通常留空,OpenAI 兼容或 Ollama 常用 | | `defaultModel` | `AgentModel` | 否 | 默认模型开关,同类型唯一 | | `config` | `AgentModel` | 否 | JSON;支持 `enableThinking` | | `headersJson` | `AgentModel` | 否 | JSON;额外请求头 | | `extraJson` | `AgentModel` | 否 | JSON;扩展字段 | | `status` | `AgentModel` | 自动 | 使用 `StatusEnum`,删除为软删除 | ### 用途类型枚举 `ModelTypeEnum` 描述模型用途,不是厂商协议: | 枚举值 | code | 含义 | |---|---|---| | `LLM` | `llm` | 对话模型,可用于 AI 对话与分析推理 | | `EMBEDDING` | `embedding` | 向量模型,仅供 RAG 表结构语义检索,不能用于对话 | `fromCode` 的容错规则: - null、空串、未知值一律按 `LLM` 处理; - 只有严格等于 `embedding`(忽略大小写)才按向量模型处理; - 这是为了兼容历史数据和前端把空 type 视为对话模型的约定。 **章节来源** - [ModelTypeEnum.java:1-54](file://ai-server/src/main/java/com/zsjz/ai/common/enums/ModelTypeEnum.java#L1-L54) - [AgentModel.java:1-63](file://ai-server/src/main/java/com/zsjz/ai/module/agent/entity/AgentModel.java#L1-L63) - [AgentModelServiceImpl.java:114-198](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L114-L198) ## 可用性与健康检查策略 系统没有全局 Provider 启动时自动探测,也没有硬编码的 Provider 白名单。可用性保障主要通过三层完成: 1. **配置完整性校验**:`AgentModelFactory.create` 要求 `modelId` 和 `apiKey` 非空。 2. **默认模型隔离**:`AgentModelService` 保证同一用途类型只有一个默认模型。 3. **连通性测试**:`ModelConnectivityTester` 对已保存配置发起真实最小调用。 ```mermaid flowchart TD Start(["开始测试"]) --> CheckType{"模型类型"} CheckType -->|llm| CreateLLM["AgentModelFactory.create(config)"] CheckType -->|embedding| CreateEmbed["EmbeddingModelFactory.create(config)"] CreateLLM --> Ping["发送 'ping',取首个流式分片"] CreateEmbed --> Embed["对固定文本做向量化"] Ping --> Timeout{"是否超时?"} Embed --> VectorValid{"是否返回非空向量?"} Timeout -->|是| FailTimeout["返回超时原因"] Timeout -->|否| Success["返回 success=true"] VectorValid -->|否| FailEmpty["返回嵌入为空"] VectorValid -->|是| Success FailTimeout --> End(["结束"]) FailEmpty --> End Success --> End ``` **图表来源** - [ModelConnectivityTester.java:66-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L66-L139) - [AgentModelFactory.java:56-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L56-L113) - [EmbeddingModelFactory.java:108-144](file://ai-server/src/main/java/com/zsjz/ai/module/agent/rag/EmbeddingModelFactory.java#L108-L144) **章节来源** - [ModelConnectivityTester.java:1-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L1-L139) - [AgentModelFactory.java:56-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L56-L113) ## 操作示例 ### 示例一:新增一个 LLM Provider 步骤: 1. 确保后端已引入对应 AgentScope 扩展(DashScope、Ollama、OpenAI 已引入)。 2. 打开前端「模型管理」页面或通过 `/models` 接口创建模型。 3. 填写 `provider`、`modelId`、`name`、`type=llm`、`apiKey`。 4. 如果是 DashScope,`baseUrl` 通常留空;如果是 Ollama 或 OpenAI 兼容代理,填写对应 Base URL。 5. 点击“测试”,确认 `ModelTestResultVO.success=true`。 6. 如需设为默认,调用 `/models/{id}/default`。 注意事项: - `provider` 只要包含 `dashscope` 或 `阿里`,就会走 DashScope 原生协议; - 其它值都会走 `openai:` 兼容协议; - 新增模型默认就是启用状态; - 如果同时勾选“设为默认”,系统会清除同类型其它默认。 **章节来源** - [AgentModelController.java:32-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L32-L91) - [AgentModelServiceImpl.java:60-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L60-L113) - [AgentModelFactory.java:56-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L56-L113) ### 示例二:切换默认模型 调用: ``` GET /models/{id}/default ``` 行为: - 先根据当前模型记录的 `type` 确定用途类型; - 清除该类型下其它模型的 `defaultModel=true`; - 将目标模型设为默认; - 另一个用途类型的默认不受影响。 典型场景: - 将 `embedding` 默认模型切换到新向量模型; - 将 `llm` 默认模型切换到新对话模型; - 两者互不干扰。 **章节来源** - [AgentModelServiceImpl.java:114-150](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L114-L150) - [ModelTypeEnum.java:20-54](file://ai-server/src/main/java/com/zsjz/ai/common/enums/ModelTypeEnum.java#L20-L54) ### 示例三:通过 AgentModelController 查询模型列表和状态 查询模型列表: ``` GET /models ``` 返回所有未被软删除的模型,并按默认模型和创建人排序。 查询厂商列表: ``` GET /models/providers ``` 返回 `agent_model_provider` 表中 `status="active"` 的厂商。 测试模型连通性: ``` GET /models/{id}/test ``` 返回结构体包含: | 字段 | 含义 | |---|---| | `success` | 是否连通 | | `latencyMs` | 测试耗时毫秒 | | `message` | 失败原因摘要 | **章节来源** - [AgentModelController.java:32-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L32-L91) - [AgentModelServiceImpl.java:40-58](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L40-L58) - [ModelConnectivityTester.java:66-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L66-L139) ## 依赖关系分析 ```mermaid graph LR POM["ai-server/pom.xml"] --> Harness["agentscope-harness"] POM --> DashScope["agentscope-extensions-model-dashscope"] POM --> OllamaExt["agentscope-extensions-model-ollama"] POM --> OpenAIExt["agentscope-extensions-model-openai"] POM --> Mem0["agentscope-extensions-mem0"] POM --> RAGSimple["agentscope-extensions-rag-simple"] POM --> RedisExt["agentscope-extensions-redis"] Controller["AgentModelController"] --> Service["AgentModelService"] Controller --> ProviderService["AgentModelProviderService"] Service --> Mapper["AgentModelMapper"] Service --> Tester["ModelConnectivityTester"] Tester --> Factory["AgentModelFactory"] Tester --> EmbedFactory["EmbeddingModelFactory"] Factory --> Registry["AgentScope ModelRegistry"] ``` **图表来源** - [ai-server/pom.xml:1-200](file://ai-server/pom.xml#L1-L200) - [AgentModelController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L1-L91) - [AgentModelService.java:1-70](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelService.java#L1-L70) - [ModelConnectivityTester.java:1-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L1-L139) - [AgentModelFactory.java:1-131](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L1-L131) - [EmbeddingModelFactory.java:1-144](file://ai-server/src/main/java/com/zsjz/ai/module/agent/rag/EmbeddingModelFactory.java#L1-L144) **章节来源** - [ai-server/pom.xml:1-200](file://ai-server/pom.xml#L1-L200) - [AgentModelController.java:1-91](file://ai-server/src/main/java/com/zsjz/ai/module/agent/controller/AgentModelController.java#L1-L91) - [AgentModelFactory.java:1-131](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L1-L131) ## 性能与可观测性 ### 性能特征 - 每次调用 `AgentModelFactory.create` 都会新建轻量 Model 配置对象,不做跨请求缓存; - 连通性测试对话模型只取首个流式分片,不等完整回复,减少 token 消耗和网络等待; - 连通性测试向量模型对固定短文本做一次嵌入; - 测试超时统一为 20 秒,既允许 Ollama 冷启动加载模型,又避免长时间阻塞; - 测试失败的消息长度被限制为 200 字符,便于前端展示。 ### 可观测性 - 模型创建成功、更新成功、删除成功、设置默认成功均有日志; - 模型实例构造成功记录 modelRef、name、enableThinking; - 连通性测试成功记录 name 和耗时; - 连通性测试失败记录 name、耗时、原因; - 思考模式 JSON 解析失败会记录警告日志,不影响主流程。 **章节来源** - [AgentModelServiceImpl.java:60-198](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L60-L198) - [AgentModelFactory.java:56-131](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L56-L131) - [ModelConnectivityTester.java:66-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L66-L139) ## 问题排查指南 ### 问题一:DashScope Key 无效 现象: - 模型创建成功; - 连通性测试返回 `success=false`; - 消息中包含鉴权失败、Key 无效、免费额度不足等底层 Provider 错误。 排查步骤: 1. 确认 `agent_model.api_key` 是否正确复制,无多余空格; 2. 确认 `agent_model.provider` 包含 `dashscope` 或 `阿里`,否则会误走 `openai:` 前缀; 3. 确认 DashScope 账号权限和免费额度; 4. 使用 `/models/{id}/test` 查看失败原因; 5. 检查后端日志中“模型连通性测试失败”的 message。 根因映射: - `AgentModelFactory.create` 会把 apiKey 放入 `ModelCreationContext`; - DashScope Provider 会在首次调用时校验 Key; - 连通性测试捕获异常并转为结构化失败结果。 **章节来源** - [AgentModelFactory.java:56-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L56-L113) - [ModelConnectivityTester.java:66-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L66-L139) ### 问题二:Ollama 服务不可达 现象: - baseUrl 指向本地 Ollama; - 连通性测试超时或连接失败; - 本地 Ollama 进程未启动,或端口不一致。 排查步骤: 1. 确认 Ollama 进程正在运行; 2. 确认 `baseUrl` 指向正确的 Ollama HTTP 地址; 3. 确认防火墙或容器网络允许访问该端口; 4. 首次加载本地模型可能较慢,20 秒超时可能不够长; 5. 使用 `/models/{id}/test` 观察耗时和错误信息。 根因映射: - Ollama 通过 `openai:` 兼容协议接入; - 连通性测试对 Ollama 也使用 20 秒超时; - 超时异常会被识别为“连接超时(20s),请检查网络与 Base URL”。 **章节来源** - [ModelConnectivityTester.java:30-45](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L30-L45) - [ModelConnectivityTester.java:90-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L90-L139) - [AgentModelFactory.java:98-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L98-L113) ### 问题三:OpenAI 代理超时 现象: - baseUrl 指向第三方代理; - 连通性测试返回超时; - 网络延迟高或代理限流。 排查步骤: 1. 确认代理地址可达; 2. 确认代理未限流或拒绝请求; 3. 确认代理支持 OpenAI 兼容协议; 4. 如果代理响应慢,考虑调整连通性测试超时逻辑; 5. 检查底层异常链中是否有 `TimeoutException`。 根因映射: - 非 DashScope provider 统一走 `openai:`; - 连通性测试使用 20 秒超时; - `describeFailure` 会遍历异常链查找 `TimeoutException`。 **章节来源** - [AgentModelFactory.java:98-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L98-L113) - [ModelConnectivityTester.java:108-139](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/ModelConnectivityTester.java#L108-L139) ### 问题四:模型枚举不生效 现象: - 设置了一个 `embedding` 默认模型,却影响了 `llm` 默认; - 或者反过来; - 旧数据 `type` 为空,行为不符合预期。 排查步骤: 1. 确认 `agent_model.type` 是 `llm` 还是 `embedding`; 2. 不要混淆 `type` 与 `agent_model_provider.type`; 3. 检查历史数据是否为空; 4. 使用 `ModelTypeEnum.fromCode` 判断:只有严格 `embedding` 才按向量模型处理; 5. 默认模型清除逻辑会根据 type 决定是否包含 `isNull(type)` 分支。 根因映射: - `ModelTypeEnum.fromCode` 将空值和未知值归一为 `LLM`; - `clearDefaultOfType` 对 `LLM` 包含 `isNull(type)`,对 `EMBEDDING` 严格按 type 匹配; - `buildDefaultQueryWrapper` 也有相同区分。 **章节来源** - [ModelTypeEnum.java:20-54](file://ai-server/src/main/java/com/zsjz/ai/common/enums/ModelTypeEnum.java#L20-L54) - [AgentModelServiceImpl.java:114-198](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java#L114-L198) ### 问题五:新增 Provider 后模型无法创建 现象: - 创建模型时抛 400; - 提示缺少模型 ID 或 API Key; - 或构造 Model 时抛 500。 排查步骤: 1. 确认 `modelId` 非空; 2. 确认 `apiKey` 非空; 3. 确认 `provider` 能被正确归一化; 4. 确认 AgentScope 扩展已引入; 5. 查看 `AgentModelFactory.create` 日志和异常信息。 根因映射: - 配置不完整直接抛 `ServerException(400, ...)`; - ModelRegistry 解析失败抛 `ServerException(500, ...)`; - pom 中必须引入对应 extension,否则 SPI 找不到前缀。 **章节来源** - [AgentModelFactory.java:56-113](file://ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelFactory.java#L56-L113) - [ai-server/pom.xml:1-200](file://ai-server/pom.xml#L1-L200) ### 问题六:Redis 不可用导致登录或会话异常 虽然 Redis 不属于 LLM Provider 本身,但文档中明确说明: - Redis 是 Sa-Token 的硬依赖; - Redis 不可用则所有 `StpUtil.*` 抛异常; - 本项目没有降级实现; - mem0 也依赖 Redis。 排查步骤: 1. 检查 `spring.data.redis.host` 和 `port`; 2. 确认 Redis 服务可连接; 3. 检查网络、密码、ACL; 4. 不要误以为 LLM 不通是 Redis 问题——LLM 不通主要看 `/models/{id}/test`。 **章节来源** - [application-dev.yaml:1-87](file://ai-server/src/main/resources/application-dev.yaml#L1-L87) - [AI_AGENT.md:120-160](file://AI_AGENT.md#L120-L160) ## 结论 本项目将 LLM Provider 管理分为两层: - **平台层**:`AgentModel`、`AgentModelProvider`、`AgentModelService`、`AgentModelController` 负责持久化、CRUD、默认模型隔离和连通性测试; - **运行时层**:`AgentModelFactory` 借助 AgentScope SPI 将 DB 配置转换为 DashScope、OpenAI 兼容、Ollama 三类 Provider 实例。 这种设计的好处是: - Provider 扩展集中在 pom 中,新增 Provider 主要依赖 AgentScope 扩展,而不必大量改动业务代码; - 模型配置可动态管理,不需要重启服务; - 默认模型按用途隔离,避免对话模型和向量模型互相污染; - 连通性测试以结构化结果返回,适合前端展示诊断信息。 扩展建议: - 新增 LLM Provider 时,优先确认其是否遵循 OpenAI 兼容协议;若是,可通过 `openai:` + baseUrl 接入; - 若需 DashScope 原生能力,则确保 provider 名称命中 `dashscope` 或 `阿里`; - 对本地 Ollama 场景,适当评估连通性测试超时是否足够; - 对生产环境,建议补充更细粒度的 Provider 健康检查定时任务,而不仅依赖用户手动触发 `/models/{id}/test`。