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 的封装,直接通过 requests 或 httpx 调用 /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 解决不兼容问题。
相关推荐
步行cgn4 小时前
FormHttpMessageConverter 详解:Spring MVC 中表单数据的读写转换器
java·spring·mvc
步行cgn7 小时前
Spring MVC + Thymeleaf 模板解析失败:从 500 错误到页面正常显示
java·spring·mvc
一木 之林14 小时前
Dify学习笔记 04 · 检索底座:Embedding、Rerank与向量数据库(第46-54集)
笔记·学习·embedding·dify
java_nnnn2 天前
spring web mvc图书管理小案例
前端·spring·mvc
步行cgn2 天前
StringHttpMessageConverter 详解:Spring MVC 中字符串读写的默认转换器
java·spring·mvc
步行cgn2 天前
@ResponseBody 详解:Spring MVC 中把返回值直接写入响应体
java·spring·mvc
AI小百科4 天前
Embedding 是什么?为什么它是 AI 的“翻译官“
人工智能·embedding
郭小李4 天前
spring mvc 工作流程
java·spring·mvc
乌暮4 天前
综合案例:宠物电商系统
java·开发语言·spring·mvc·restful·1024程序员节
郭小李4 天前
sping mvc五个常用注解
mvc