Eino Embedding 组件核心解析:文本向量化与语义检索
介绍 Eino 框架中 Embedding 组件的核心功能与应用。Embedding 将文本转换为向量,用于语义相似度计算、搜索及 RAG 知识库问答。文章涵盖基础调用(NewEmbedder, EmbedStrings)、Option 配置、Chain/Graph 编排集成以及 Callback 监控,并详细展示了从知识库切块到向量检索的完整业务流程。

介绍 Eino 框架中 Embedding 组件的核心功能与应用。Embedding 将文本转换为向量,用于语义相似度计算、搜索及 RAG 知识库问答。文章涵盖基础调用(NewEmbedder, EmbedStrings)、Option 配置、Chain/Graph 编排集成以及 Callback 监控,并详细展示了从知识库切块到向量检索的完整业务流程。

说到 embedding 组件,本质上就是把文本变成一串数字向量,让程序能'按语义理解文本',而不只是按字符串匹配。
你可以把它理解成:
"今天天气不错"[0.12, -0.87, 0.44, ...]这串向量人是看不懂的,因为他是拿个程序看的。机器可以拿它来算'两个文本像不像'。
平时大家会用到的地方最常见就是这几类:
1. 文本相似度计算 比如:
虽然字不一样,但意思接近。Embedding 后,这两句话的向量距离会比较近,所以系统知道它们语义相似。
2. 语义搜索 这也是最常见的用途。比如你有很多文档、知识库、FAQ,用户问:
系统不是只搜关键词'修改''地址',而是把这个问题也做成向量,然后去找语义最接近的文档片段。这样即使文档里写的是'变更配送地址',也能搜出来。
3. RAG / 知识库问答 这类项目里 embedding 基本是核心组件之一了。流程通常是:
也就是说,它是'先找资料'这一步的关键。
4. 文本聚类 / 分类 / 去重 这个是生活中其他方面的应用,非 AI。比如你有很多评论、工单、反馈,可以用 embedding 做:
它不是直接拿来生成回答的。它更像一个'文本编码器'或者'语义检索工具'。
也就是:
这个组件的核心用途就一句话:
把文字转换成可计算的语义特征,方便程序判断哪些文本意思接近。
接下来,我先说下基础语法。
EmbedStrings(ctx, texts []string, opts ...Option)([][]float64,error)
意思就是:
例如:
texts := []string{"hello", "how are you"}
vectors, err := embedder.EmbedStrings(ctx, texts)
返回的 vectors 就是两段文本的向量表示。后面你可以拿这些向量去做:
它的使用可以分成两层来看:
我直接教你'你上手怎么写'。
EmbedStrings本质核心就这几步:
import (
"github.com/cloudwego/eino-ext/components/embedding/openai"
)
// 这个导入的包,是兼容 openai 的。如果你要用豆包,可以专门调用 embedding/ark 这个包。
embedder, err := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{
APIKey: accessKey,
Model: "text-embedding-3-large",
Dimensions: &defaultDim,
Timeout: 0,
})
if err != nil {
panic(err)
}
这里的作用是初始化一个'文本转向量'的对象。
几个关键参数:
APIKey:调用模型服务的密钥Model:选哪个 embedding 模型Dimensions:向量维度Timeout:超时时间EmbedStringstexts := []string{"hello", "how are you"}
vectors, err := embedder.EmbedStrings(ctx, texts)
if err != nil {
panic(err)
}
这一步做完后:
texts[0] 对应 vectors[0]texts[1] 对应 vectors[1]也就是说,输入几段文本,输出几组向量。
生成出来的 vectors 一般不会直接打印给用户看,而是继续做下面这些事:
你可以把它理解成一个普通组件,哪里需要文本转向量,哪里调用。
例如:
package main
import (
"context"
"fmt"
"log"
"github.com/cloudwego/eino-ext/components/embedding/openai"
)
func main() {
ctx := context.Background()
defaultDim := 3072
accessKey := "your-api-key"
embedder, err := openai.NewEmbedder(ctx, &openai.EmbeddingConfig{
APIKey: accessKey,
Model: "text-embedding-3-large",
Dimensions: &defaultDim,
Timeout: 0,
})
if err != nil {
log.Fatal(err)
}
texts := []string{"退款怎么申请", "如何进行退钱操作", "今天天气不错"}
vectors, err := embedder.EmbedStrings(ctx, texts)
if err != nil {
log.Fatal(err)
}
fmt.Println("文本数量:", len(vectors))
fmt.Println("第一条文本向量维度:", len(vectors[0]))
}
这就是最标准的'用'了!
公共 option 其实也挺有用的,比如 WithModel。
这表示你在调用时,可以临时覆盖模型参数。
vectors, err := embedder.EmbedStrings(ctx, texts, embedding.WithModel("text-embedding-3-small"))
大致意思就是:
embedder 初始化时有一个默认模型这个适合:
但是要注意,虽然向量在不同模型之前还是有一定的兼容,但是尽量不切换,就不要切换,影响效果。
如果你不是手动一行一行写,而是用 Eino 的 Chain 或 Graph,就可以把 embedding 当成节点塞进去。
初次接触 chain 的话,你可以将其当成一条流水线。
chain := compose.NewChain[[]string, [][]float64]()
chain.AppendEmbedding(embedder)
意思是:
[]string[][]float64也就是整条链专门做'文本 -> 向量'。
graph := compose.NewGraph[[]string, [][]float64]()
graph.AddEmbeddingNode("embedding_node", embedder)
意思是把 embedding 作为图里的一个节点,后面可以接别的节点一起跑。
这个一般用于:
Callback 有点像给整个链路外挂了一层'生命周期中间件 / 钩子机制'。
通常是:定义 handler,然后通过 compose.WithCallbacks 传进去。
例如:
handler := &callbacksHelper.EmbeddingCallbackHandler{
OnStart: func(ctx context.Context, runInfo *callbacks.RunInfo, input *embedding.CallbackInput) context.Context {
log.Printf("开始 embedding,文本数:%d, 内容:%v\n", len(input.Texts), input.Texts)
return ctx
},
OnEnd: func(ctx context.Context, runInfo *callbacks.RunInfo, output *embedding.CallbackOutput) context.Context {
log.Printf("embedding 完成,生成向量数:%d\n", len(output.Embeddings))
return ctx
},
}
然后运行时:
callbackHandler := callbacksHelper.NewHandlerHelper().Embedding(handler).Handler()
runnable, _ := chain.Compile(ctx)
vectors, err := runnable.Invoke(ctx, []string{"hello", "how are you"}, compose.WithCallbacks(callbackHandler))
这样你就能看到:
真正业务里,embedding 很少是'调一下就结束',我拿知识库问答给大家描绘一下整体流程。
比如一篇文档切成很多段:
chunks := []string{"退款申请需要在订单完成后 7 天内提交", "修改收货地址请在发货前联系人工客服", "发票可在订单详情页申请"}
chunkVectors, err := embedder.EmbedStrings(ctx, chunks)
通常会存到向量数据库里,同时保存原文:
query := []string{"订单下完以后地址还能改吗"}
queryVector, err := embedder.EmbedStrings(ctx, query)
找出最相似的几段知识。
这才变成完整的 RAG。
EmbedStrings[][]float64Option 临时覆盖参数Callback 打日志和监控Chain / Graph 编排
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online