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 解决不兼容问题。
相关推荐
sugar__salt1 天前
Document 切割:RAG 知识库数据预处理实战
javascript·langchain·embedding·js·rag·cheerio
今天AI了吗1 天前
Hermes Agent 搭建全流程:从本机试跑到可持续运行的个人 AI Agent
java·人工智能·python·学习·embedding
段一凡-华北理工大学3 天前
向量数据库实战:选型、调优与落地~系列文章02:Embedding 嵌入模型选型指南:OpenAI、BGE、Jina、Cohere 横评
大数据·数据库·人工智能·embedding·高炉炼铁·jina·工业智能体
月落星还在4 天前
Spring MVC 与 Spring Boot:从“手动挡”到“自动驾驶”的进化论,兼谈前后端分离的哲学
spring boot·spring·云原生·mvc
衣乌安、5 天前
归一化与点积在 Embedding 中的作用
ai·embedding
张彦峰ZYF6 天前
大模型LLM ACA - ACP认证考试真题冲刺演练二答案参考
人工智能·lora·llm·embedding·rag·few-shot
uncle_ll6 天前
LangChain 实战指南:回调系统、自定义组件与有状态对话全落地
langchain·llm·embedding·agent·大模型开发·rag
程序员佳佳8 天前
模型网关灰度不是调百分比:把放量、观测和回滚做成账本
java·数据库·人工智能·redis·gpt·aigc·embedding
wang09078 天前
自己动手写一个spring之MVC_3
java·spring·mvc