Files
Memoh/REFACTORING_SUMMARY.md
T
2026-01-26 23:06:54 +08:00

274 lines
7.3 KiB
Markdown
Raw 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.
# Provider 重构总结
## 重构目标
将 Memory 服务和 Chat 服务统一使用 `internal/chat` 中的 Provider 接口,实现代码复用和架构统一。
## 主要变更
### 1. 扩展 Chat Provider 接口
**文件**: `internal/chat/types.go`
- 扩展 `Request` 结构,添加了:
- `Temperature *float32` - 温度参数
- `ResponseFormat *ResponseFormat` - 响应格式(支持 JSON 模式)
- `MaxTokens *int` - 最大 token 数
- 新增 `ResponseFormat` 结构:
```go
type ResponseFormat struct {
Type string // "json_object" 或 "text"
}
```
### 2. 创建 Memory 的 LLM 接口
**文件**: `internal/memory/types.go`
- 定义了 `LLM` 接口:
```go
type LLM interface {
Extract(ctx context.Context, req ExtractRequest) (ExtractResponse, error)
Decide(ctx context.Context, req DecideRequest) (DecideResponse, error)
}
```
- 这个接口被以下两个实现:
- `LLMClient` (旧实现,向后兼容)
- `ProviderLLMClient` (新实现,使用 chat.Provider)
### 3. 创建 Provider-based LLM 客户端
**文件**: `internal/memory/llm_provider_client.go` (新文件)
- 实现了 `ProviderLLMClient` 结构
- 使用 `chat.Provider` 来执行 LLM 调用
- 支持 JSON 模式输出,确保结构化响应
- 重用了 `internal/memory` 包中的辅助函数
关键代码:
```go
type ProviderLLMClient struct {
provider chat.Provider
model string
}
func NewProviderLLMClient(provider chat.Provider, model string) *ProviderLLMClient
func (c *ProviderLLMClient) Extract(ctx context.Context, req ExtractRequest) (ExtractResponse, error)
func (c *ProviderLLMClient) Decide(ctx context.Context, req DecideRequest) (DecideResponse, error)
```
### 4. 更新 Memory 服务
**文件**: `internal/memory/service.go`
- 将 `llm *LLMClient` 改为 `llm LLM`
- 现在接受任何实现 `LLM` 接口的类型
- 保持了所有现有功能
### 5. 更新主程序
**文件**: `cmd/agent/main.go`
#### 添加的函数:
1. **selectMemoryModel** - 选择用于 memory 操作的模型
- 优先级:memory 模型 → chat 模型 → 任何 chat 类型模型
2. **fetchProviderByID** - 根据 ID 获取 provider 配置
3. **createChatProvider** - 根据配置创建 provider 实例
- 支持 OpenAI、Anthropic、Google、Ollama
#### 初始化流程更新:
```go
// 1. 初始化 chat resolver(用于 chat 和 memory
chatResolver := chat.NewResolver(modelsService, queries, 30*time.Second)
// 2. 尝试为 memory 创建 provider-based 客户端
memoryModel, memoryProvider, err := selectMemoryModel(ctx, modelsService, queries, &cfg)
if err != nil {
// 回退到旧的 LLMClient
llmClient = memory.NewLLMClient(cfg.Memory.BaseURL, cfg.Memory.APIKey, ...)
} else {
// 使用新的 provider-based 客户端
provider, _ := createChatProvider(memoryProvider, 30*time.Second)
llmClient = memory.NewProviderLLMClient(provider, memoryModel.ModelID)
}
// 3. 创建 memory 服务
memoryService = memory.NewService(llmClient, embedder, store, resolver, ...)
```
## 架构优势
### 1. 统一的 Provider 管理
- Chat 和 Memory 服务共享相同的 Provider 实现
- 减少代码重复
- 统一的配置和管理
### 2. 灵活的模型选择
- 可以为不同功能配置不同的模型
- 支持 `enable_as` 字段来指定模型用途
- 自动回退机制
### 3. 向后兼容
- 保留了旧的 `LLMClient` 实现
- 如果数据库中没有配置模型,自动回退到配置文件
- 平滑迁移路径
### 4. 类型安全
- 使用 Go 接口而不是运行时类型判断
- 编译时类型检查
- 更好的 IDE 支持
### 5. 易于扩展
- 添加新 Provider 只需实现 `Provider` 接口
- 添加新 LLM 客户端只需实现 `LLM` 接口
- 模块化设计
## 配置说明
### 数据库配置
为 Memory 操作配置模型:
```sql
-- 方式 1: 使用专用的 memory 模型
UPDATE models SET enable_as = 'memory'
WHERE model_id = 'gpt-4-turbo-preview';
-- 方式 2: 使用 chat 模型(如果没有专用 memory 模型)
UPDATE models SET enable_as = 'chat'
WHERE model_id = 'gpt-4';
```
### 环境变量(回退配置)
如果数据库中没有配置模型,系统会使用这些配置:
```toml
[memory]
base_url = "https://api.openai.com/v1"
api_key = "sk-..."
model = "gpt-4.1-nano"
timeout_seconds = 10
```
## 测试建议
### 1. Memory 操作测试
```bash
# 测试 Extract(提取事实)
curl -X POST http://localhost:8080/api/memory/add \
-H "Authorization: Bearer $TOKEN" \
-d '{
"messages": [
{"role": "user", "content": "My name is Alice and I like pizza"}
],
"user_id": "user-123"
}'
# 测试 Search(搜索记忆)
curl -X POST http://localhost:8080/api/memory/search \
-H "Authorization: Bearer $TOKEN" \
-d '{
"query": "What food do I like?",
"user_id": "user-123"
}'
```
### 2. Chat 操作测试
```bash
# 测试普通聊天
curl -X POST http://localhost:8080/api/chat \
-H "Authorization: Bearer $TOKEN" \
-d '{
"messages": [
{"role": "user", "content": "Hello, how are you?"}
]
}'
```
### 3. 验证日志
启动时应该看到:
```
Using memory model: gpt-4-turbo-preview (provider: openai)
```
或者(如果回退):
```
WARNING: No memory model configured, using fallback LLMClient: ...
```
## 后续工作
### 短期(必须)
- [ ] 实现各个 Provider 的具体逻辑(目前大部分返回 "not yet implemented"
- [ ] 添加流式响应支持
- [ ] 完善错误处理
### 中期(建议)
- [ ] 添加 Provider 的单元测试
- [ ] 添加 Memory 集成测试
- [ ] 实现 Provider 连接池
- [ ] 添加请求重试机制
### 长期(优化)
- [ ] Provider 性能监控
- [ ] 自动模型选择和负载均衡
- [ ] 模型响应缓存
- [ ] 支持更多 Provider(如 Cohere、HuggingFace
## 迁移检查清单
- [x] 扩展 Request 结构支持 JSON 模式
- [x] 创建 LLM 接口
- [x] 实现 ProviderLLMClient
- [x] 更新 Memory Service 使用接口
- [x] 更新主程序初始化流程
- [x] 添加模型选择逻辑
- [x] 添加 Provider 创建逻辑
- [x] 保持向后兼容
- [x] 添加架构文档
- [ ] 添加单元测试
- [ ] 添加集成测试
- [ ] 更新部署文档
## 文件清单
### 新增文件
- `internal/memory/llm_provider_client.go` - Provider-based LLM 客户端
- `internal/chat/ARCHITECTURE.md` - 架构文档
- `REFACTORING_SUMMARY.md` - 本文件
### 修改文件
- `internal/chat/types.go` - 扩展 Request 结构
- `internal/memory/types.go` - 添加 LLM 接口
- `internal/memory/service.go` - 使用 LLM 接口
- `cmd/agent/main.go` - 更新初始化流程
### 保留文件(向后兼容)
- `internal/memory/llm_client.go` - 旧的 HTTP 客户端实现
## 注意事项
1. **JSON 模式兼容性**: 不是所有模型都支持 JSON 模式,需要在实现 Provider 时处理
2. **错误处理**: 当前错误处理较简单,生产环境需要更详细的错误信息
3. **超时设置**: 不同操作可能需要不同的超时时间,可以考虑配置化
4. **并发安全**: Provider 实例应该是并发安全的
5. **资源清理**: 确保 Provider 的资源(如 HTTP 连接)正确释放
## 问题反馈
如果遇到问题,请检查:
1. 数据库中是否有配置的 chat 模型
2. Provider 配置是否正确(API key、base URL
3. 日志中的错误信息
4. 是否正确初始化了 chat resolver
详细架构说明请参考:`internal/chat/ARCHITECTURE.md`