# Handoff: ChatModel + Embedding 解耦 (chatmodel-abstraction) **日期**: 2026-05-30 **分支**: `refactor/rag-chunking-strategy` **状态**: ✅ 实现完成,测试通过,文档已回填 --- ## 做了什么 将项目从 DashScope 硬编码解耦为 Spring AI 抽象接口,支持跨厂商模型切换。 ### 代码改动 (9 tasks) | Task | 文件 | 改动 | |---|---|---| | T1 | `MilvusProperties.java` + `application.yml` | `vectorDim` 字段 + `milvus.vector-dim` 配置 | | T2 | `MilvusClientFactory.java` | `VECTOR_DIM` 常量 → `milvusProperties.getVectorDim()` | | T3 | `ChatService.java` | 删除工厂方法,`@Autowired ChatModel` | | T4 | `ChatController.java` | 删除 3 处 DashScope 手动构建 | | T5 | `AiOpsService.java` | `DashScopeChatModel` → `ChatModel` | | T6 | `VectorEmbeddingService.java` | DashScope SDK → `EmbeddingModel.embed()` | | T7 | `RagService.java` | `Generation` + `Flowable` → `ChatModel.stream()` + `Flux` | | T8 | `ModelRoutingConfig.java` (新增) | `@Primary` 集中路由,`List` 自检 Bean | | T9 | `SiliconFlowEmbeddingConfig.java` (新增) | `OpenAiApi` → SiliconFlow, BGE-M3 1024维 | | — | `pom.xml` | `spring-ai-starter-model-deepseek` + `spring-ai-starter-model-openai`,移除 DashScope/Ollama | | — | `application.yml` | DeepSeek 原生配置 + SiliconFlow embedding | | — | `MilvusClientFactory.java` | 启动时 `loadCollection()` | ### 新增文件 - `src/main/java/org/example/config/ModelRoutingConfig.java` - `src/main/java/org/example/config/SiliconFlowEmbeddingConfig.java` - `src/test/java/org/example/service/ChatAndEmbeddingSmokeTest.java` - `src/test/java/org/example/service/FullPipelineSmokeTest.java` ## 当前架构 | 层 | 厂商 | 实现 | Bean 名 | |---|---|---|---| | Chat | DeepSeek V4 Flash | `DeepSeekChatModel` (Spring AI 原生) | `deepSeekChatModel` | | Embedding | SiliconFlow BGE-M3 | `OpenAiEmbeddingModel` (OpenAI 兼容) | `siliconFlowEmbeddingModel` | | 向量存储 | Milvus (Zilliz Cloud) | `MilvusServiceClient` | — | | 路由 | — | `ModelRoutingConfig` | `chatModel` + `embeddingModel` @Primary | ## 测试结果 ``` ChatAndEmbeddingSmokeTest: 5/5 ✅ FullPipelineSmokeTest: 5/5 ✅ (Chat + Embedding + Milvus 全链路) mvn spring-boot:run : ✅ 4.5s 启动, 端口 9900 ``` ### 启动条件 - DeepSeek / SiliconFlow API Key 已配在 yml - Milvus Zilliz Cloud 已配置 - MCP 禁用、Prometheus+CLS Mock 模式 - `ToolCallbackProvider` 改为 `@Autowired(required = false)` + null 兜底 运行测试前需要: - DeepSeek API Key 在 yml 中配置(`spring.ai.deepseek.api-key`) - SiliconFlow API Key 在 yml 中配置(`siliconflow.api-key`) - Milvus 连接已配置(Zilliz Cloud token 在 yml 中) - MCP 客户端已禁用(`spring.ai.mcp.client.enabled: false`) - 测试中 ToolCallbackProvider 由 mock 提供 ## 关键经验教训 详见 `devflow/projects/2026-05-29-chatmodel-abstraction/decisions.md`: 1. **Spring AI version → 模型兼容性**: 1.1.0 的 OpenAI 兼容模式不兼容 DeepSeek V4(2026年4月发布),升级到 1.1.7 + 原生 DeepSeekChatModel 才解决 2. **`@Qualifier` Bean 名不要猜**: 用 `List` 自检 + 类名筛选比硬编码更稳 3. **base-url 不要带 `/v1`**: Spring AI 自动追加版本路径,会导致双重 4. **多 starter 并存需要 `@Primary`**: ModelRoutingConfig 集中路由 5. **`EmbeddingModel.embed()` 返回 `float[]`**: 不是 `List` ## 问题备忘 | 问题 | 状态 | |---|---| | DashScope SDK 全部清除 | ✅ | | DeepSeek V4 兼容性 | ✅ 用原生 starter 解决 | | SiliconFlow 404 | ✅ base-url 修复 | | Milvus collection not loaded | ✅ 加 loadCollection() | | MCP ToolCallbackProvider 缺失 | ✅ 测试中 mock | ## 有效文档 - OpenSpec: `openspec/changes/chatmodel-abstraction/` (proposal/design/specs/tasks) - devflow: `devflow/projects/2026-05-29-chatmodel-abstraction/decisions.md` - 词汇表: `devflow/glossary/CONTEXT.md` - 索引: `devflow/index.md` - 项目 rules: `CLAUDE.md`, `AGENTS.md` ## Suggested Skills 下一个 agent 应加载: - **sm-flow**: 如需继续推进(archive 归档、新需求变更) - **openspec-apply-change**: 如需实现额外 task - **openspec-archive-change**: 如需归档 OpenSpec change - **gitnexus**: 如需分析影响范围、pre-commit 检查 ## 可能的后续工作 1. 运行 `npx openspec` 归档当前 change(archive 阶段) 2. 真实 Milvus 数据灌入验证(当前 collection 为空) 3. RagService SSE 端到端测试(需要启动应用) 4. Ollama 本地 embedding 替代(如果 SiliconFlow 不可用) 5. MCP 客户端重新启用 + 真实腾讯云日志查询验证