通过eino-ext如何正常indexer RAG?
通过eino-ext如何正常indexer RAG?
整体架构
文档文本 ──→ ARK Embedder(向量化)──→ DocumentConverter(格式转换)──→ Milvus Indexer(写入) ↑ ↑ doubao-embedding-vision BinaryVector + HAMMING安装配置
安装 向量数据库 - Milvus
services:etcd:container_name:milvus-etcdimage:quay.io/coreos/etcd:v3.5.18environment:-ETCD_AUTO_COMPACTION_MODE=revision-ETCD_AUTO_COMPACTION_RETENTION=1000-ETCD_QUOTA_BACKEND_BYTES=4294967296-ETCD_SNAPSHOT_COUNT=50000volumes:-${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcdcommand:etcd-advertise-client-urls=http://etcd:2379-listen-client-urls http://0.0.0.0:2379--data-dir /etcdhealthcheck:test:["CMD","etcdctl","endpoint","health"]interval:30stimeout:20sretries:3minio:container_name:milvus-minioimage:minio/minio:RELEASE.2023-03-20T20-16-18Zenvironment:MINIO_ACCESS_KEY:minioadminMINIO_SECRET_KEY:minioadminports:-"9001:9001"-"9000:9000"volumes:-${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_datacommand:minio server /minio_data--console-address ":9001"healthcheck:test:["CMD","curl","-f","http://localhost:9000/minio/health/live"]interval:30stimeout:20sretries:3standalone:container_name:milvus-standaloneimage:milvusdb/milvus:v2.5.10command:["milvus","run","standalone"]security_opt:-seccomp:unconfinedenvironment:ETCD_ENDPOINTS:etcd:2379MINIO_ADDRESS:minio:9000volumes:-${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvushealthcheck:test:["CMD","curl","-f","http://localhost:9091/healthz"]interval:30sstart_period:90stimeout:20sretries:3ports:-"19530:19530"-"9091:9091"depends_on:-"etcd"-"minio"# 这是新增的 Attu 服务哦!attu:container_name:milvus-attuimage:zilliz/attu:v2.5ports:-"8000:3000"# 把本地的 8000 端口映射到容器的 3000 端口 (Attu 默认端口)environment:# MILVUS_URL 指向 Docker 网络里的 Milvus standalone 服务MILVUS_URL:standalone:19530depends_on:-standalone# 确保 Milvus 启动后再启动 Attunetworks:default:name:milvusdockercompose up-d这会启动 4 个服务:
- etcd:Milvus 的元数据存储
- MinIO:对象存储,用于持久化向量数据
- Milvus standalone:向量数据库本身,端口19530
- Attu(可选):Milvus 可视化管理界面,端口8000
访问 localhost:8000 可进入Attu 这是Milvus是可视化操作页面
配置相关文件
方舟控制台:来购买向量模型 (主要目前只有多模态向量模型)
# 用于访问ARK服务的API密钥 ARK_API_KEY= # 指定使用的向量模型 EMBEDDER=doubao-embedding-vision-251215 # 坑爹模型代码介绍
Milvus 客户端初始化
varMilvusCli cli.Clientfuncinit(){ctx:=context.Background()client,err:=cli.NewClient(ctx,cli.Config{Address:"localhost:19530",// Milvus 默认端口})iferr!=nil{log.Fatalf("Failed to create client: %v",err)}MilvusCli=client}使用init()函数在包加载时自动建立连接。连接地址与 docker-compose 中映射的端口一致。
定义 Collection Schema
varfields=[]*entity.Field{{Name:"id",DataType:entity.FieldTypeVarChar,TypeParams:map[string]string{"max_length":"255"},PrimaryKey:true,},{Name:"vector",DataType:entity.FieldTypeBinaryVector,// 二进制向量(不是 FloatVector!)TypeParams:map[string]string{"dim":"16384",// ⚠️ 单位是比特(bit),不是字节},},{Name:"content",DataType:entity.FieldTypeVarChar,TypeParams:map[string]string{"max_length":"8192"},},{Name:"metadata",DataType:entity.FieldTypeJSON,},}踩坑记录
FloatVector vs BinaryVector:
ARK 的doubao-embedding-vision-*模型输出的是[]uint8(二进制向量),值域 0~255,而不是常见的[]float32浮点向量。
如果用entity.FieldTypeFloatVector,运行时会报类型不匹配错误:
invalid type, expected[]float32, got[]uint8dim 的单位是比特:
MilvusBinaryVector的dim参数单位是比特(bit),不是字节(byte)。
| 项目 | 值 |
|---|---|
| 模型输出维度 | 2048 个值(每个 0~255) |
| 字节数 | 2048 bytes |
| 比特数 | 2048 × 8 =16384 bits |
| Schema dim 应填 | 16384 |
如果错误地填了2048(以为是字节数),Milvus 会认为每条数据的向量只有 2048 bit = 256 byte,但实际传入 2048 byte,导致报错:
the num_rows(8)of field(vector)is not equal to passed num_rows(1)这里的8= 2048 ÷ 256,即 Milvus 把 1 条数据的 2048 byte 当成了 8 条 256-byte 的数据。
初始化嵌入器(Embedder)
timeout:=30*time.Second apiType:=ark.APITypeMultiModal// ⚠️ 多模态模型必须指定embedder,err:=ark.NewEmbedder(ctx,&ark.EmbeddingConfig{APIKey:os.Getenv("ARK_API_KEY"),Model:os.Getenv("EMBEDDER"),// doubao-embedding-vision-251215APIType:&apiType,Timeout:&timeout,})ARK 的嵌入 API 有两个不同的 HTTP 端点:
APITypeText APIType="text_api"#`/api/v3/embeddings`|纯文本嵌入模型 APITypeMultiModal APIType="multi_modal_api"#`/api/v3/embeddings/multimodal`|多模态嵌入模型doubao-embedding-vision-*模型只部署在 multimodal 端点上。
如果不设置APIType,SDK 会默认走 text 端点,导致:
doubao-embedding-vision-251215 does not support this api (status code: 400)判断方法:看模型名字是否包含vision,包含就必须用APITypeMultiModal。
自定义 DocumentConverter
查看 eino-ext 源码中默认 Converter 的实现(utils.go+types.go),发现它用的是带milvus:tag 的结构体指针:
typebinaryRowstruct{IDstring`json:"id" milvus:"name:id"`Contentstring`json:"content" milvus:"name:content"`Vector[]byte`json:"vector" milvus:"name:vector"`// BinaryVector 标量Metadata[]byte`json:"metadata" milvus:"name:metadata"`// JSON 序列化后的字节}funcbinaryDocumentConverter(_context.Context,docs[]*schema.Document,vectors[][]float64)([]interface{},error){rows:=make([]interface{},0,len(docs))fori,doc:=rangedocs{metadata,_:=json.Marshal(doc.MetaData)// 元数据需序列化为 []bytebyteVec:=make([]byte,len(vectors[i]))forj,v:=rangevectors[i]{byteVec[j]=byte(v)}rows=append(rows,&binaryRow{// ⚠️ 必须返回 struct 指针ID:doc.ID,Content:doc.Content,Vector:byteVec,Metadata:metadata,})}returnrows,nil}- struct 必须带
milvus:"name:xxx"tag:告诉 SDK 每个字段对应哪个列名 - Vector 字段类型是
[]byte:对应 BinaryVector - 返回值必须是 struct 指针(
&binaryRow{...}):不能是值或 map
组装 Indexer
indexer,err:=milvus.NewIndexer(ctx,&milvus.IndexerConfig{Client:MilvusCli,Collection:collection,// 集合名: "AwesomeEino"Fields:fields,// 上文定义的 SchemaEmbedding:embedder,// ARK 嵌入器MetricType:milvus.MetricType(entity.HAMMING),// 二进制向量用汉明距离DocumentConverter:binaryDocumentConverter,// 自定义转换器})写入时逐条存入(也可以批量)
for_,doc:=rangedocs{storeDoc:=[]*schema.Document{{ID:doc.ID,Content:doc.Content,MetaData:doc.MetaData}}ids,err:=indexer.Store(ctx,storeDoc)iferr!=nil{log.Fatalf("Failed to store documents: %v",err)}println("Stored document ID:",ids[0])}入口文件与文档定义
funcmain(){godotenv.Load(".env")// 加载环境变量docs:=[]*schema.Document{{ID:"doc1",Content:"Eino 是一个开源的 AI 应用开发框架...",MetaData:map[string]interface{}{"source":"eino-docs"}},{ID:"doc2",Content:"Milvus 是一个高性能向量数据库...",MetaData:map[string]interface{}{"source":"milvus-docs"}},}stage4.IndexerRAG(docs)}