RAG 系统 Embedding 流程分析与自托管 API 适配

1. 问题背景

在构建一个轻量化 RAG(检索增强生成)系统时,使用了 LangChain 框架结合自托管的 FreeLLMApi(兼容 OpenAI 格式)进行文本向量化。在开发过程中,遇到了运行时类型错误,并对 Embedding 模型如何处理文本以及自托管服务与 OpenAI 原生服务的差异产生了疑问。


2. 运行时问题分析

2.1 错误现象

错误日志:

TypeError: Document.__init__() got an unexpected keyword argument 'page_content'

2.2 根本原因

该错误发生在 load_chunks_from_file 函数中。代码尝试通过 Document(page_content=..., metadata=...) 实例化对象。

  • 原因 :实例化时传递的参数名 page_content 与当前环境中安装的 Document 类定义不匹配。这通常是因为导入了错误的 Document 类,或者使用了版本不兼容的 LangChain 核心库。
  • 解决方案 :确保从 langchain_core.documents 正确导入 Document 类,并核对该版本类构造函数的参数名。

3. 深度技术探讨:Embedding 工作机制

3.1 文本是如何转换为向量的?

调用 embeddings.embed_documents(batch_texts) 时,内部执行流程如下:

原始文本 →\rightarrow→ Tokenizer分词 →\rightarrow→ Token IDs (整数序列) →\rightarrow→ 神经网络 →\rightarrow→ Embedding向量

核心结论:神经网络无法直接处理字符串,必须经过 Token 化转换为数字 ID 才能进行矩阵运算。

3.2 _tokenize 方法的作用

在 LangChain 的 OpenAIEmbeddings 实现中,_tokenize 方法的作用不是预处理文本 ,而是计数(量长度)

  • 目的:计算当前文本包含多少个 token,以判断是否超过模型最大长度限制(例如 8191 tokens)。
  • 逻辑 :如果超限 →\rightarrow→ 执行截断或切分 →\rightarrow→ 再次检查;如果不超限 →\rightarrow→ 进入 _get_len_safe_embeddings 阶段。

3.3 _get_len_safe_embeddings 解析

该方法名为 "Length Safe",意味着它假设输入文本已经通过了长度校验,因此它会跳过所有校验步骤 ,直接将原始字符串(batch_texts)发送给 API 接口。


4. 自托管服务 vs OpenAI 原生 API

4.1 推理逻辑对比

特性 OpenAI 原生 API 自托管推理服务 (如 FreeLLMApi)
传输内容 原始字符串 (str) 可能是字符串,也可能是 Token IDs
Tokenize 位置 服务端完成 可能在客户端 完成 →\rightarrow→ 传输 IDs
API 接口 https://api.openai.com/v1 私有 IP (如 172.21.6.101) + 自定义端口
模型定义 明确的模型名 (如 text-embedding-3) 可能使用 auto 等路由别名

4.2 如何判断是否为自建推理服务?

  1. IP 地址 :使用私有 IP 段(10.x, 172.16-31.x, 192.168.x)。
  2. 端口号 :使用非标准 HTTPS 端口(如 3001)。
  3. 客户端类型self.client 的类型不是标准 openai.OpenAI 类。
  4. 输入参数 :API 调用时传递的是 batch_tokens (Listint) 而非 batch_texts (Liststr)。

5. 适配方案:让自托管服务走 OpenAI 逻辑

若自托管服务兼容 OpenAI 格式,但其 SDK 强制要求传递 batch_tokens,而你希望传输原始文本,可采取以下方案:

方案 A:重写 Embedding 类 (推荐)

继承 OpenAIEmbeddings 并重写 embed_documents,绕过内部的 Token 转换逻辑,强制发送原始字符串。

方案 B:直接使用 HTTP 请求

不使用 LangChain 的封装,直接通过 requestshttpx 调用 /v1/embeddings 端点,确保 input 字段传递的是字符串列表。

python 复制代码
# 伪代码示例
response = requests.post(
    url="http://172.21.6.101:3001/v1/embeddings",
    json={"model": "auto", "input": ["原始文本1", "原始文本2"]},
    headers={"Authorization": f"Bearer {API_KEY}"}
)

6. 总结

  • 运行时错误 →\rightarrow→ 检查类导入与参数匹配。
  • Embedding 流程 →\rightarrow→ 本地 Tokenize 仅用于长度校验 →\rightarrow→ 传输原始文本 →\rightarrow→ 服务端 Tokenize →\rightarrow→ 生成向量。
  • 自托管识别 →\rightarrow→ 通过私有 IP、非标端口及 self.client 类型判断。
  • 适配关键 →\rightarrow→ 确认 API 接收的是 string 还是 token_ids,通过重写方法或直接调用 REST API 解决不兼容问题。
相关推荐
风起洛阳@不良使2 天前
Spring MVC的前端控制器配置(servlet+DispatcherServlet)
前端·spring·mvc
0566462 天前
agent学习Day23——文件上传与 Embedding 语义检索
python·学习·embedding
萧瑟余晖2 天前
Java深入解析篇十六之Spring MVC与WebFlux
java·spring·mvc
IKUN家族3 天前
Spring MVC(四)
java·spring·mvc
ShallWeL4 天前
【机器学习】(32)—— Embedding 串讲
人工智能·机器学习·embedding
ShallWeL4 天前
【机器学习】(31)—— 如何得到 Embedding
人工智能·机器学习·embedding
denggun123455 天前
大大模型 如何通过 本地排雷 远端GPU
python·embedding·pip
ShallWeL8 天前
【机器学习】(30)—— 嵌入空间
人工智能·神经网络·机器学习·embedding
孙启超8 天前
【AI应用开发】 RAG篇(四):Prompt 工程与进阶技术
人工智能·llm·embedding·rag·向量化·chunking·文档切分
Cachel wood8 天前
hands-on-modern-rl:动手学强化学习 贝尔曼方程
开发语言·网络·python·学习·embedding