Files
SuperBizAgent-java/handoff/handoff-chatmodel-abstraction.md
2026-05-31 21:45:14 +08:00

108 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<T>` 自检 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<T>` 自检 + 类名筛选比硬编码更稳
3. **base-url 不要带 `/v1`**: Spring AI 自动追加版本路径,会导致双重
4. **多 starter 并存需要 `@Primary`**: ModelRoutingConfig 集中路由
5. **`EmbeddingModel.embed()` 返回 `float[]`**: 不是 `List<Double>`
## 问题备忘
| 问题 | 状态 |
|---|---|
| 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 客户端重新启用 + 真实腾讯云日志查询验证