资讯动态

Embench:构建Embedding模型与检索栈对比评估平台

发布时间:2026/8/30 17:59:26 来源:尧图企业网站定制
在检索系统选型时最容易被低估的工作不是搭建向量库也不是写召回接口而是回答一个问题在同一批业务数据上不同 embedding 模型和不同检索栈放到一起比较结果到底差多少Embench 这个名字代表了一种很实用的产品形态一个专门用来对比 embeddings 和 retrieval stacks 的 playground。你可以在里面快速注册模型、接入向量存储、跑同一份测试集、观察精度、延迟、召回率和成本曲线。这篇文章会围绕这种对比工作台应该怎么设计、怎么搭、怎么跑、怎么读结果展开适合正在做 RAG 初步选型、向量检索方案调研或者想建立一套可持续评估机制的开发者和算法工程师。读完之后你可以得到一套可复制的实验框架而不是零散跑代码的经验。1. 先理解嵌入模型和检索栈在对比中的角色差异真正的检索系统中embedding 模型和检索栈是两个职责完全不同的层。很多人混淆它们导致评估结果无法定位问题根源。这一节先拆开讲。1.1 embedding 模型决定的是语义表示质量嵌入模型解决的核心问题是如何把一段文本转换成一个固定维度的向量让语义相近的文本在向量空间中距离更近。通俗地说embedding 模型是系统的“翻译器”。它把人类语言变成机器能算距离的数值序列。不同模型的翻译质量差别很大有的擅长短文本匹配有的擅长长文档语义压缩有的对代码文本更友好有的对中文长尾词更稳定。同一个 query经过不同模型转换后在向量空间中分布的形态完全不同。在对比系统中embedding 层直接影响召回上限。如果模型本身没有把语义关系编码好后面无论换多快的索引召回出来的结果都是错的。反过来说模型选得再好如果检索栈的索引结构、距离算法、过滤逻辑有问题向量检索同样会返回一堆不相关结果。1.2 检索栈决定的是召回效率和过滤能力检索栈并不仅仅指 FAISS 或者 pgvector 这种向量搜索引擎。它是一条完整链路通常包含文本预处理与切分向量化调用向量索引构建相似度检索元数据过滤排序与 Top-K 截断可选的重排序模块检索栈解决的是给定目标向量如何快速从小到千万级甚至亿级的向量集合中找到最相关的候选同时还能支持业务过滤条件。需要注意检索栈里的索引类型影响了召回精度。使用 HNSW 索引比暴力扫描快但可能牺牲一点召回率。Flat 索引精度高但内存开销和延迟高。这类差异只有通过实际数据压测才能暴露出来。1.3 embeddings 与 retrieval stacks 的关系就像引擎与变速箱把嵌入模型和检索栈拆开是因为它们互相影响但影响方式不同。嵌入模型决定了理论上的语义上限检索栈决定了实际检索效率。两者组合之后才是端到端的检索效果。所以Embench 这类 playground 一定要能分别记录每一层的指标而不是只记录最终结果。否则当整体效果变差时你很难判断是模型向量表示不行还是索引参数配置问题。可以这样记忆它们的角色差异对比维度embedding 模型检索栈核心问题文本如何变成向量向量如何被存储和召回影响指标语义相关性上限召回率、延迟、吞吐量调优方式换模型、微调、改写指令换索引、调 efSearch、加过滤评估难点语言覆盖度、领域适配度索引构建时间、内存占用、过滤逻辑典型代表OpenAI text-embedding、BGE、CohereFAISS、pgvector、Milvus、Qdrant2. 设计评估目标前先把指标和数据组织方式定下来没有统一指标和数据集的对比实验没有意义。Embench 这类工作台能在团队内复用靠的就是把评估口径固定下来。2.1 核心指标要覆盖效果、延迟和资源三组维度先看效果指标。最常用的是 recallk、precisionk、MRR 和 NDCG。recallk 代表正确答案是否出现在前 k 条结果里precisionk 代表前 k 条结果里有多少是正确相关MRR 适合只有一条正确答案的场景它看的是第一条正确结果排多前NDCG 更复杂适合有分级相关性的评估集。再看延迟指标。一次检索端到端耗时包含 embedding 生成耗时和向量检索耗时。对线上系统来说p50 和 p95 延迟才有参考价值平均延迟容易被长尾请求拉偏。最后是资源指标。包括模型的显存占用、向量索引的内存大小、单次检索的 CPU/内存消耗。这些指标直接决定生产环境选型是否可落地。需要注意预训练模型在不同硬件上的性能差距很大因此所有耗时测试都要记录运行环境不能只在结果表里写一个数字。2.2 数据集要区分领域、场景和相关性分级通用数据集评估出的结果往往无法代表真实业务。Embench 的数据集中至少要包含三类查询业务真实查询。从用户日志中抽取能反映真实 query 的表达习惯。标准答案集。每个 query 需要对应一段或几段应该被召回的标准文档。负样本集。这类样本在业务中容易被误召回用于验证模型是否能分开易混淆文本。相关性不能只写“相关或不相关”建议使用三级标注相关性级别含义示例0不相关用户问退款政策召回的是商品详情页1部分相关用户问退款政策召回的是售后流程总述页2高度相关用户问退款政策召回的是退款政策细则页面分级相关性能支持 NDCG 这种指标对排序能力做更细粒度评估。如果数据集中只有“相关”和“不相关”很多排序问题会被平均掉。2.3 要在数据集里加入对抗性样本对抗性样本指的是语义相似但答案完全不同的文本。例如两条售后政策里的句子高度接近但一个适用于普通商品一个适用于生鲜商品。这类样本能测出模型是否真正理解业务约束而不是只靠字面重合度做匹配。实验设计阶段建议在数据集中加入 10% 到 20% 的对抗性样本。如果没有这些样本效果指标会虚高线上出问题时又很难复盘。3. 初始化环境、依赖和项目结构这一节会把 Embench 的工程底座搭出来。示例使用 Python 3.10向量存储部分选择 FAISS 和 pgvector 两种后端用于演示不同检索栈的差异。3.1 准备好基础依赖建议通过虚拟环境隔离依赖。在项目根目录创建requirements.txtnumpy1.26.4 pandas2.2.2 sentence-transformers3.0.1 openai1.35.0 faiss-cpu1.8.0 psycopg2-binary2.9.9 pyyaml6.0.1 tabulate0.9.0安装命令python -m venv .venv source .venv/bin/activate pip install -r requirements.txt这里把安装拆成两步是为了让你能清楚看到依赖是否解析成功。如果 network 环境受限可以把包源切换为内网镜像源但要注意版本一致性。实际项目中嵌入模型接口、向量库版本都会持续变化因此不建议使用最新版本直接上线最好锁定小版本。3.2 项目目录按实验维度拆分建议目录结构如下embench/ ├── config.yaml ├── embeddings/ │ ├── base.py │ ├── openai_embedding.py │ └── sentence_embedding.py ├── backends/ │ ├── base.py │ ├── faiss_backend.py │ └── pgvector_backend.py ├── datasets/ │ └── sample.jsonl ├── eval/ │ ├── metrics.py │ └── runner.py ├── outputs/ └── main.py这种结构的核心思路是model、backend、dataset 三者解耦。你要比较新的嵌入模型时不需要改检索后端你要比较新的检索栈时不需要改数据集数据集改动也不会影响模型调用和向量索引逻辑。3.3 配置中心用一个 YAML 文件管理实验参数实验参数分散在代码里最容易造成结果不可复现。建议使用 YAML 集中管理。experiment: name: sample_comparison top_ks: [3, 5, 10] datasets: path: datasets/sample.jsonl embedding_batch_size: 32 models: - name: bge_m3 type: sentence_transformer model_name: BAAI/bge-m3 dimension: 1024 normalize: true - name: openai_text_embedding_3_large type: openai model_name: text-embedding-3-large dimension: 3072 normalize: true backends: - name: faiss_hnsw type: faiss index_type: HNSW metric: cosine efConstruction: 200 efSearch: 64 - name: pgvector_ivfflat type: pgvector index_type: ivfflat lists: 100 probes: 10 redis_runner: repeat_times: 3 warmup_times: 1注意dimension要和模型实际输出维度一致。OpenAI 的text-embedding-3-large默认输出 3072 维但可以通过dimensions参数压缩如果配置值和实际值不一致索引构建直接报错。4. 实现最小可运行的对比流程这一节从零实现一个最小闭环读取测试数据 → 批量生成向量 → 存储到不同检索栈 → 执行查询 → 计算指标 → 输出报告。4.1 定义数据集结构数据集采用 JSONL 格式每一行代表一个文档或一个查询。文档集合和查询集合分开存放避免每次加载时重复过滤。示例{id: doc-001, text: 本品支持 7 天无理由退货但生鲜类商品除外。, category: policy} {id: doc-002, text: 退货申请提交后将在 24 小时内审核。, category: policy} {id: query-001, text: 生鲜商品可以无理由退货吗, answer_doc_ids: [doc-001], hard_negative_ids: [doc-002]}每条 query 都要维护answer_doc_ids。hard_negative_ids不是必须的但在对抗性测试中很有用。4.2 抽象 embedding 模型层需要一个统一的 base 接口让不同模型都能批量生成向量。# embeddings/base.py from abc import ABC, abstractmethod class BaseEmbedding(ABC): abstractmethod def embed_texts(self, texts: list[str]) - list[list[float]]: pass abstractmethod def embed_query(self, text: str) - list[float]: pass property abstractmethod def embedding_dim(self) - int: passSentenceTransformer 实现# embeddings/sentence_embedding.py from sentence_transformers import SentenceTransformer from embeddings.base import BaseEmbedding class SentenceEmbedding(BaseEmbedding): def __init__(self, model_name: str, normalize: bool True): self.model SentenceTransformer(model_name) self.normalize normalize self._dim self.model.get_sentence_embedding_dimension() def embed_texts(self, texts: list[str]) - list[list[float]]: vectors self.model.encode(texts, normalize_embeddingsself.normalize) return vectors.tolist() def embed_query(self, text: str) - list[float]: return self.embed_texts([text])[0] property def embedding_dim(self) - int: return self._dimOpenAI 实现# embeddings/openai_embedding.py from openai import OpenAI from embeddings.base import BaseEmbedding class OpenAIEmbedding(BaseEmbedding): def __init__(self, model_name: str, dimension: int, api_key: str, base_url: str None): self.client OpenAI(api_keyapi_key, base_urlbase_url) self.model_name model_name self.dimension dimension self._dim dimension def embed_texts(self, texts: list[str]) - list[list[float]]: response self.client.embeddings.create( modelself.model_name, inputtexts, dimensionsself.dimension, ) return [item.embedding for item in response.data] def embed_query(self, text: str) - list[float]: return self.embed_texts([text])[0] property def embedding_dim(self) - int: return self._dim需要注意OpenAI SDK 的base_url参数用于企业内部通过网关访问模型服务的情况。生产环境不要硬编码 API Key应通过环境变量注入。4.3 抽象检索后端后端接口只需要两个核心能力写入向量、检索 Top-K。不同后端实现的细节差异都被隐藏在这个接口后面。# backends/base.py from abc import ABC, abstractmethod class BaseBackend(ABC): abstractmethod def build_index(self, doc_ids: list[str], vectors: list[list[float]]) - None: pass abstractmethod def search(self, query_vector: list[float], top_k: int, filter_cond: dict None) - list[str]: pass abstractmethod def stats(self) - dict: passFAISS 后端示例# backends/faiss_backend.py import numpy as np import faiss from backends.base import BaseBackend class FaissBackend(BaseBackend): def __init__(self, dimension: int, index_type: str HNSW, ef_search: int 64): self.dimension dimension self.index_type index_type self.ef_search ef_search self.doc_ids [] self.id_to_pos {} if index_type HNSW: self.index faiss.IndexHNSWFlat(dimension, 32) self.index.hnsw.efSearch ef_search else: self.index faiss.IndexFlatIP(dimension) def build_index(self, doc_ids, vectors): self.doc_ids list(doc_ids) matrix np.array(vectors, dtypenp.float32) if self.index_type HNSW: self.index.add_with_ids(matrix, np.arange(len(self.doc_ids))) else: self.index.add(matrix) def search(self, query_vector, top_k, filter_condNone): vec np.array([query_vector], dtypenp.float32) scores, indices self.index.search(vec, top_k) results [] for score, idx in zip(scores[0], indices[0]): if idx -1: continue doc_id self.doc_ids[idx] results.append((doc_id, float(score))) return resultspgvector 后端需要使用 SQL 完成建表和检索。这个示例假定你已经有一个 PostgreSQL 实例并安装了 pgvector 扩展。CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE IF NOT EXISTS documents ( id TEXT PRIMARY KEY, content TEXT, embedding vector(1024) ); CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);# backends/pgvector_backend.py import psycopg2 from backends.base import BaseBackend class PgvectorBackend(BaseBackend): def __init__(self, dsn: str, dimension: int): self.conn psycopg2.connect(dsn) self.dimension dimension def build_index(self, doc_ids, vectors): cur self.conn.cursor() for doc_id, text, vec in zip(doc_ids, self._texts, vectors): cur.execute( INSERT INTO documents(id, content, embedding) VALUES (%s, %s, %s), (doc_id, text, vec) ) self.conn.commit() cur.close() def search(self, query_vector, top_k, filter_condNone): query_sql SELECT id, 1 - (embedding %s::vector) AS score FROM documents params [query_vector] if filter_cond: query_sql WHERE category %s params.append(filter_cond[category]) query_sql ORDER BY embedding %s::vector LIMIT %s params.extend([query_vector, top_k]) cur self.conn.cursor() cur.execute(query_sql, params) rows cur.fetchall() cur.close() return [(row[0], float(row[1])) for row in rows]build_index方法里的self._texts不能为空实际项目需要在调用前把文档文本一并传入这里只展示核心结构。4.4 评估 Runner 串联整个流程评估 runner 是 playground 的核心调度器。它要做的事包括加载数据集 → 准备模型 → 构建索引 → 逐条查询 → 计算指标 → 汇总。# eval/runner.py import time import statistics from eval.metrics import recall_at_k, mrr_at_k class EvalRunner: def __init__(self, model, backend, dataset, repeat_times3): self.model model self.backend backend self.dataset dataset self.repeat_times repeat_times def run(self, top_ks(3, 5, 10)): docs self.dataset[docs] queries self.dataset[queries] # 1. 构建文档向量 started_at time.perf_counter() doc_vectors self.model.embed_texts([d[text] for d in docs]) index_build_time time.perf_counter() - started_at # 2. 构建索引 started_at time.perf_counter() self.backend.build_index([d[id] for d in docs], doc_vectors) index_write_time time.perf_counter() - started_at # 3. 查询并计算指标 metrics {k: {recall: 0, mrr: 0} for k in top_ks} latencies [] for q in queries: q_vec self.model.embed_query(q[text]) for r in range(self.repeat_times): start time.perf_counter() result self.backend.search(q_vec, top_kmax(top_ks)) latencies.append(time.perf_counter() - start) result_ids [item[0] for item in result] for k in top_ks: metrics[k][recall] recall_at_k(result_ids, q[answer_doc_ids], k) metrics[k][mrr] mrr_at_k(result_ids, q[answer_doc_ids], k) num_queries len(queries) for k in top_ks: metrics[k][recall] / num_queries metrics[k][mrr] / num_queries return { index_build_time_sec: round(index_build_time, 3), index_write_time_sec: round(index_write_time, 3), latency_p50_ms: round(statistics.median(latencies) * 1000, 3), latency_p95_ms: round(sorted(latencies)[int(len(latencies) * 0.95)] * 1000, 3), metrics: metrics, }注意代码里计算 p95 的方式是简化的。它在重复测试次数足够时才更可靠如果真的要做统计建议使用分位数算法或保留全部延迟分布。4.5 指标计算函数# eval/metrics.py def recall_at_k(ranked_ids, answer_ids, k): ranked set(ranked_ids[:k]) answer set(answer_ids) if not answer: return 0.0 return len(ranked answer) / len(answer) def mrr_at_k(ranked_ids, answer_ids, k): for i, doc_id in enumerate(ranked_ids[:k]): if doc_id in answer_ids: return 1.0 / (i 1) return 0.0这两个函数都假设answer_ids至少有一个标准答案。实际生产数据中可能出现无答案的查询这种查询需要单独处理否则会拉低整体指标。5. 关键参数要理解后再调在 playground 里参数调整不应该是盲目试要理解每个参数对结果的影响。5.1 向量维度与内存开销向量维度直接决定索引体积。一个包含 100 万条文档的集合每个向量 1024 维占用内存大约为 1000000 * 1024 * 4 字节 4GB。如果使用 3072 维内存会变成约 12GB。在比较不同模型时必须把索引内存消耗纳入结果表。向量维度100 万条文档内存占用说明384 维约 1.5 GB轻量适合小模型768 维约 3 GB常见规模1024 维约 4 GBBGE 等模型默认3072 维约 12 GBOpenAI 大模型默认5.2 相似度度量的选择面相似度度量会影响向量语义效果。余弦相似度关注方向夹角适合大多数文本语义检索点积会受向量长度影响更适合带长度信息才有效的场景欧氏距离对数值范围敏感通常需要做归一化。如果要比较不同模型的结果统一使用余弦相似度除非你有明确理由换度量。这样可以避免指标偏差来自度量而不是模型。5.3 top_k 与阈值是并存概念不是替换关系检索场景中top_k 是硬截断阈值是软过滤。top_k 控制返回条数阈值控制相似度下限。两者应该组合使用。场景top_k 推荐值阈值推荐召回粗排50 到 2000.0 到 0.2RAG 上下文注入3 到 100.3 到 0.5语义搜索展示10 到 200.5 到 0.7不要把阈值设太高否则查不出任何结果。要在 playground 里做阈值扫描找到每个模型的 Precision-Recall 平衡点。5.4 批量大小影响吞吐但不影响精度embedding_batch_size只影响推理吞吐和显存占用不影响向量内容。批量太小会导致 GPU 利用率低、推理慢批量太大会导致显存溢出或 API 请求超时。batch_size影响8显存占用小但速度慢32常见默认值128吞吐高但要留意显存和 API 限流5.5 HNSW 的 efSearch 参数HNSW 在检索阶段可以通过efSearch控制探索范围。efSearch 越大准确性越高但延迟越高。下面是一个典型走势efSearchrecall10 变化延迟变化16较低低64中等中256接近暴力检索高1024逼近 Flat非常高在 playground 里建议每个后端至少测三组 efSearch 参数不要只测默认值。6. 运行对比实验并看懂结果使用命令行入口跑通主流程python main.py --config config.yamlmain.py读取 YAML加载模型列表和后端列表执行全量组合实验把结果写到outputs/sample_comparison.csv。输出的示例结果表modelbackendrecall3recall5mrr5p95 延迟(ms)索引内存(MB)bge_m3faiss_hnsw0.8120.8630.7543.2128bge_m3pgvector_ivfflat0.7820.8210.7216.896openai_largefaiss_hnsw0.7950.8410.73012.5512openai_largepgvector_ivfflat0.7760.8190.7189.1448读结果时不要只盯 recall 最高的组合。真正要关注的是三个点一是模型栈和后端的组合效应。比如 bge_m3 在 faiss 上 recall 高但换到 pgvector 却下降说明后端索引参数或切分方式影响到了模型表现。二是延迟与精度的拐点。openai_large 在 faiss 上召回接近 bge_m3但延迟明显更高。如果系统对 p95 有硬性要求bge_m3 可能更合适。三是内存成本。openai_large 需要的内存量是 bge_m3 的 4 倍。在线上部署时如果索引进不了内存性能会断崖式下跌。如果发现某个模型在多个后端上 recall 都低不要立刻调整索引参数先检查数据集切分策略。可能是文档太长模型把关键信息稀释了。7. 常见报错与排查路径Embench 这类对比平台跑起来之后出问题最多的集中在依赖版本不一致、向量维度不匹配、数据集格式错误与性能不稳定四类。7.1 维度不匹配导致索引构建失败现象FAISS 构建索引时报dimension mismatchpgvector 报different vector dimensions。可能原因YAML 配置里写的 dimension 与实际模型的输出维度不一致比如模型输出 1024配置里填了 1536。检查方式在模型实现里打印len(vector)与配置对比。解决方式以实际输出为准修改 YAML如果使用 OpenAI 模型要确认是否设置了dimensions参数。7.2 检索结果为空但索引构建没有报错现象search 返回空列表。可能原因pyvector 建表时向量类型维度错误或者 HNSW 索引在查询时没有正确搜索到邻接节点。检查方式查看数据库表定义确认embedding vector(1024)与配置维度一致手动执行一条 SQL 查询验证。解决方式重建表并重新插入数据对于 FAISS检查IndexHNSWFlat是否在build_index时调用了add_with_ids。7.3 不同模型之间的指标不可比现象A 模型 recall 低于 B 模型但 A 模型在人工抽样中表现更好。可能原因两个模型用了不同的相似度度量或者一个归一化一个没归一化。检查方式检查 YAML 中 normalize 字段对比向量的 L2 范数。解决方式统一设置normalize: true统一使用 cosine 距离。这是实验设计层面的问题不是模型问题。7.4 延迟波动大实验不可复现现象同一组命令跑三次p95 延迟相差几十毫秒。可能原因GPU 被其他任务占用API 存在限流或者数据集切分不固定导致索引状态不同。检查方式用nvidia-smi看显存占用查看 API 请求日志是否出现 429。解决方式跑实验前排空 GPU 任务API 调用要设置重试和退避策略每组实验至少跑三次取中位数或分位数。7.5 数据集里出现无答案的 query现象recallk 总是偏低且 mrr 出现大量 0。可能原因部分 query 没有标准答案但评估函数默认除以非零答案数。检查方式统计answer_doc_ids为空的 query 数。解决方式单独成组分析无答案场景的检索行为计算 recall 时排除这类 query或者用 special tag 标注。可以用一个简短检查表辅助排错排查顺序检查对象方法1输入数据检查 query 中 text 是否有空值文档 id 是否重复2向量维度打印实际向量长度核对配置3相似度度量确认所有模型使用同一度量4索引参数确认 HNSW 的 efSearch 不是 05系统资源检查内存、显存、文件描述符6日志查看模型调用和数据库连接日志8. 让对比工作台真正服务于生产选型Embench 这类 playground 的最终目的不是跑分而是帮助团队形成持续评估的习惯。要做到这一点需要建立清单并解决好边缘情况。8.1 发布前检查清单在选定 embedding 模型和检索栈并进入生产前建议按清单逐项确认[ ] 是否在同一份数据集上比较了至少两个模型、两个后端[ ] 数据集是否包含真实业务 query 和对抗性样本[ ] 是否记录了索引内存占用和 p95 延迟[ ] 是否明确相似度度量和归一化方式[ ] 是否处理了无答案 query 的评估口径[ ] 是否检查了不同 top_k 下的 Recall/MRR 曲线[ ] 是否预演了模型升级后的索引重建流程[ ] 是否评估了向量检索失败时的兜底逻辑[ ] 是否记录了模型 API 的限流和超时行为[ ] 是否确认新模型在长尾文本上的表现不低于旧模型这个清单的价值在于把隐性的技术决策变成显性的评审依据。8.2 学习环境与生产环境的差异要清楚在本地 notebook 里跑模型和生产环境部署检索服务两者差异很大。本地实验可以用 Python 脚本直接调用模型线上则需要考虑 API 网关、鉴权、日志采集、监控告警、灰度发布。本地实验使用 HNSW 索引得到很好延迟但生产环境需要评估索引是否需要常驻内存是否需要多副本是否需要动态更新向量。建议采用分层验证学习环境只验证链路是否跑通指标是否变化明显。测试环境模拟生产数据量验证索引构建时间和大流量下的稳定性。生产环境先灰度一个模型或一个检索栈用线上流量回放对比效果。8.3 扩展方向加入重排序和混合检索如果 Embedding 检索结果无法满足业务预期不要急着换更大模型。可以先引入重排序层和混合检索层。重排序使用交叉编码器或精排模型对召回结果做二次排序。它能改善 top-K 的顺序但无法改变召回集合本身因此必须放在召回之后。混合检索则把语义检索和关键词检索结合起来。常见做法是使用 BM25 和向量检索分别召回再用 RRFReciprocal Rank Fusion合并。这种方案对长尾 query 有稳定提升。在 Embench 中可以把重排序器和混合检索器也抽象成可插拔组件。这样评估就不只是模型与栈的对比还能支持“模型 检索栈 精排策略”的整体对比。8.4 对新的学习者的建议如果刚接触这套体系不要一上来就追求把所有模型、所有后端都测一遍。先从一组最小组合开始一个开源 embedding 模型、一个 FAISS 后端、100 条带标注的数据集。跑通流程后再逐步替换组件。先理解指标变化再理解参数变化。这样当你把组合扩展到生产环境时才能对每个变量的影响有实际判断。嵌入模型和检索栈的选型没有银弹。Embench 这种方式的价值在于它把模糊的“哪个方案更好”转化成“在哪些数据上、哪些指标上、以什么成本更好”。做到这一点选型的每一步就有了依据。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价