mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
5.5 KiB
5.5 KiB
Chat Provider 架构文档
概述
本文档描述了 Memoh 项目中统一的 Chat Provider 架构,该架构被 chat 服务和 memory 服务共同使用。
架构设计
核心接口
Provider 接口
所有 LLM 提供商都实现 chat.Provider 接口:
type Provider interface {
Chat(ctx context.Context, req Request) (Result, error)
StreamChat(ctx context.Context, req Request) (<-chan StreamChunk, <-chan error)
}
Request 结构
Provider 请求支持多种配置选项:
type Request struct {
Messages []Message
Model string
Provider string
Temperature *float32 // 可选:温度参数
ResponseFormat *ResponseFormat // 可选:响应格式(JSON 模式)
MaxTokens *int // 可选:最大 token 数
}
type ResponseFormat struct {
Type string // "json_object" 或 "text"
}
支持的提供商
-
OpenAI (
openai/openai-compat)- 标准 OpenAI API
- 兼容 OpenAI 格式的自定义端点
-
Anthropic (
anthropic)- Claude 系列模型
-
Google (
google)- Gemini 系列模型
-
Ollama (
ollama)- 本地部署的开源模型
使用场景
1. Chat 服务
Chat 服务通过 chat.Resolver 使用 Provider:
chatResolver := chat.NewResolver(modelsService, queries, 30*time.Second)
response, err := chatResolver.Chat(ctx, ChatRequest{
Messages: messages,
Model: "gpt-4",
})
2. Memory 服务
Memory 服务通过 memory.ProviderLLMClient 使用 Provider:
// 创建 provider
provider, err := chat.NewOpenAIProvider(apiKey, baseURL, timeout)
// 创建 memory LLM 客户端
llmClient := memory.NewProviderLLMClient(provider, modelID)
// 使用 memory 服务
memoryService := memory.NewService(llmClient, embedder, store, resolver, ...)
Memory 服务需要两个核心功能:
- Extract: 从对话中提取事实信息
- Decide: 决定如何更新记忆(添加/更新/删除)
这两个操作都使用 JSON 模式来确保结构化输出。
配置示例
数据库配置
Provider 配置存储在 llm_providers 表:
CREATE TABLE llm_providers (
id UUID PRIMARY KEY,
name TEXT NOT NULL,
client_type TEXT NOT NULL, -- 'openai', 'anthropic', 'google', 'ollama'
base_url TEXT NOT NULL,
api_key TEXT NOT NULL,
metadata JSONB
);
模型配置存储在 models 表:
CREATE TABLE models (
id UUID PRIMARY KEY,
model_id TEXT NOT NULL,
name TEXT,
llm_provider_id UUID REFERENCES llm_providers(id),
type TEXT NOT NULL, -- 'chat' or 'embedding'
enable_as TEXT, -- 'chat', 'memory', 'embedding'
...
);
启动时初始化
在 cmd/agent/main.go 中:
// 1. 初始化 chat resolver(用于 chat 和 memory)
chatResolver := chat.NewResolver(modelsService, queries, 30*time.Second)
// 2. 为 memory 选择模型和创建 provider
memoryModel, memoryProvider, err := selectMemoryModel(ctx, modelsService, queries, cfg)
provider, err := createChatProvider(memoryProvider, 30*time.Second)
// 3. 创建 memory LLM 客户端
llmClient := memory.NewProviderLLMClient(provider, memoryModel.ModelID)
// 4. 创建 memory 服务
memoryService := memory.NewService(llmClient, embedder, store, resolver, ...)
模型选择策略
Memory 模型选择优先级
enable_as = 'memory'的模型(专用 memory 模型)enable_as = 'chat'的模型(通用 chat 模型)- 任何可用的 chat 类型模型
- 回退到配置文件中的 LLMClient(向后兼容)
Chat 模型选择优先级
- 请求中指定的模型
enable_as = 'chat'的模型- 任何可用的 chat 类型模型
优势
- 统一架构: Chat 和 Memory 使用相同的 Provider 接口
- 灵活配置: 支持多个提供商和模型
- 向后兼容: 保留旧的 LLMClient 作为回退选项
- 类型安全: 使用 Go 接口确保类型安全
- 易于扩展: 添加新的提供商只需实现 Provider 接口
扩展新提供商
要添加新的 LLM 提供商:
- 在
internal/chat/创建新文件(如newprovider.go) - 实现
Provider接口 - 在
resolver.go的createProvider()中添加新的 case - 在数据库的
llm_providers_client_type_check约束中添加新类型
示例:
// newprovider.go
type NewProvider struct {
apiKey string
timeout time.Duration
}
func NewNewProvider(apiKey string, timeout time.Duration) (*NewProvider, error) {
return &NewProvider{apiKey: apiKey, timeout: timeout}, nil
}
func (p *NewProvider) Chat(ctx context.Context, req Request) (Result, error) {
// 实现 chat 逻辑
}
func (p *NewProvider) StreamChat(ctx context.Context, req Request) (<-chan StreamChunk, <-chan error) {
// 实现流式 chat 逻辑
}
迁移指南
从旧的 TypeScript 后端迁移到 Go:
- ✅ 创建 Provider 接口和实现
- ✅ 实现 Chat Resolver
- ✅ 创建 Memory 的 Provider 适配器
- ✅ 更新主程序使用统一 Provider
- 🚧 实现各个 Provider 的具体逻辑(OpenAI, Anthropic, Google, Ollama)
- 🚧 添加流式响应支持
- 🚧 添加完整的错误处理和重试机制
注意事项
- JSON 模式: Memory 操作需要
ResponseFormat.Type = "json_object"来确保结构化输出 - 温度参数: Memory 操作使用
Temperature = 0确保确定性输出 - 超时设置: 不同操作可能需要不同的超时时间
- 错误处理: Provider 应该返回清晰的错误信息,包括 API 错误详情