104 lines
3.6 KiB
Go
104 lines
3.6 KiB
Go
package kb
|
||
|
||
import "context"
|
||
|
||
// ========================================================================
|
||
// Embedder 接口(V2 预留)
|
||
// ========================================================================
|
||
// V1 只实现 NoopEmbedder(默认),不调任何向量化服务
|
||
//
|
||
// V2 接入 BGE-M3 / 阿里 / OpenAI 时只需:
|
||
// 1. 新增文件 embedder_bge.go 实现 Embedder 接口
|
||
// 2. 在 NewEmbedder 工厂函数里加 case
|
||
// 3. 业务代码(Searcher / Importer)零改动
|
||
// ========================================================================
|
||
|
||
// Embedder 向量化接口
|
||
//
|
||
// 各实现的差异主要在:
|
||
// - Name():返回 provider 标识(noop/bge_m3/aliyun/openai)
|
||
// - Embed():批量把文本转成向量(V1 noop 直接返回 nil)
|
||
// - Dim():向量维度(V1 noop 返回 0)
|
||
// - Available():是否能用(V1 noop 返回 false)
|
||
type Embedder interface {
|
||
// Name 返回 provider 名(用于日志/前端展示)
|
||
Name() string
|
||
|
||
// Embed 批量向量化
|
||
//
|
||
// 入参:texts 一段或多段文本
|
||
// 出参:与 texts 等长的向量数组;每个向量是 []float32(维度由实现决定)
|
||
// V1 NoopEmbedder 返回 nil, nil(不报错,调用方判断 Available() 后再调用)
|
||
Embed(ctx context.Context, texts []string) ([][]float32, error)
|
||
|
||
// Dim 返回向量维度
|
||
// NoopEmbedder 返回 0
|
||
// BGE-M3 返回 1024,阿里 text-embedding-v3 返回 1024
|
||
Dim() int
|
||
|
||
// Available 是否可用
|
||
// NoopEmbedder 返回 false(明确告知调用方"我不做向量化")
|
||
// 真实 provider 返回 true
|
||
Available() bool
|
||
}
|
||
|
||
// ========================================================================
|
||
// NoopEmbedder —— V1 默认实现(不做任何向量化)
|
||
// ========================================================================
|
||
|
||
// NoopEmbedder 空实现,所有方法返回零值
|
||
//
|
||
// 设计意图:
|
||
// - 让 V1 的 Importer/Searcher 代码结构里就有 Embedder 接口位置
|
||
// - 通过判断 Available() 自动跳过向量化逻辑
|
||
// - V2 替换为真实 Embedder 时无需改业务代码
|
||
type NoopEmbedder struct{}
|
||
|
||
// NewNoopEmbedder 构造
|
||
func NewNoopEmbedder() *NoopEmbedder { return &NoopEmbedder{} }
|
||
|
||
// Name 返回 provider 名
|
||
func (n *NoopEmbedder) Name() string { return "noop" }
|
||
|
||
// Embed 不做向量化,直接返回 nil
|
||
func (n *NoopEmbedder) Embed(ctx context.Context, texts []string) ([][]float32, error) {
|
||
return nil, nil
|
||
}
|
||
|
||
// Dim 返回 0(noop 没有维度)
|
||
func (n *NoopEmbedder) Dim() int { return 0 }
|
||
|
||
// Available 返回 false(明确告知调用方不要依赖我)
|
||
func (n *NoopEmbedder) Available() bool { return false }
|
||
|
||
// ========================================================================
|
||
// Embedder 工厂(V1 只支持 noop;V2 扩展)
|
||
// ========================================================================
|
||
|
||
// NewEmbedder 根据 provider 名构造对应的 Embedder
|
||
//
|
||
// 参数:
|
||
// - provider:从 xk_system_config.ai_kb_embedding_provider 读到的值
|
||
// - apiKey:从 xk_system_config.ai_kb_embedding_api_key 读到的值(V1 noop 不用)
|
||
//
|
||
// 返回:
|
||
// V1 默认走 noop 分支
|
||
// V2 新增 bge_m3/aliyun/openai 时在这里加 case
|
||
// 未知 provider 也回落到 noop(不阻断业务)
|
||
func NewEmbedder(provider, apiKey string) Embedder {
|
||
switch provider {
|
||
case "noop", "":
|
||
return NewNoopEmbedder()
|
||
// V2 预留:
|
||
// case "bge_m3":
|
||
// return NewBGEM3Embedder(apiKey)
|
||
// case "aliyun":
|
||
// return NewAliyunEmbedder(apiKey)
|
||
// case "openai":
|
||
// return NewOpenAIEmbedder(apiKey)
|
||
default:
|
||
// 未知 provider 回落到 noop(保证可用性)
|
||
return NewNoopEmbedder()
|
||
}
|
||
}
|