新所得库 - 长文向量化踩坑记:用“摘要”替代“全文”,终结 NoneType
结论先行:向量化时用了全文,大文档/长网页超出嵌入模型上限 → 返回
None
→ 触发'NoneType' object is not subscriptable
。修复为用 LLM 生成的压缩摘要(compress_content
)做向量化,并加“无摘要则截取前 1000 字”的兜底,两条上传链路(upload-file
/upload-url
)已统一修复并通过回归。
一、问题是如何被发现的(现象 → 复现)
-
日志表象:解析成功、向量化开始,但随后报错:
'NoneType' object is not subscriptable
-
触发条件:当文件或网页正文极长(数万字符)时更易复现
-
关键矛盾:上游已经产出
compress_content
(摘要),但向量化入参仍然使用原始全文
这是“看上去更全面,实际更脆弱”的典型陷阱:高信息量≠高可用性。
二、根因定位(从现象到契约)
-
嵌入模型输入上限
大文本直接丢给嵌入接口,超长即截断或直接失败;部分实现超限返回None
,向量下游取值即触发'NoneType'
。 -
链路不一致
-
内容处理链:
LLMService.process_file_content
生成compress_content
(摘要与标签)✅ -
向量化链:仍用
html_content / file_text_content
全文 ❌
两条链路没有对齐。
-
-
缺少兜底策略
当摘要缺失或生成失败时,向量化没有降级,导致失败不可控。
三、修复方案(统一策略 + 可回退)
3.1 统一“向量化输入”为摘要优先
-
原则:始终优先用
compress_content
做嵌入,它是“可控长度的高信息密度表示”。
3.2 兜底策略
-
若没有摘要或摘要过短:使用原文前 1000 字(可通过配置项调整),在保证代表性的同时控制长度。
3.3 关键代码对比
修复前(节选)
# upload-file / upload-url 均类似
vectorize_success = await vectorize_file_content(
nk_id=nk_id,
file_content=file_text_content_or_html, # ❌ 使用全文
file_name=filename,
company_code=company_code,
user_id=user_id
)
修复后(节选)
# 1) 预处理后拿到 compress_content(LLM 摘要)
compress_content = result.summary or ""
# 2) 选择向量化入参(摘要优先,缺省退 1000 字)
content_for_embedding = (
compress_content if len(compress_content) > 0
else (file_text_content_or_html[:1000] if file_text_content_or_html else "")
)
# 3) 日志打点:可观测输入长度,便于排查
logger.info(f"[Vectorize] {filename} content_len={len(content_for_embedding)}")
# 4) 向量化
vectorize_success = await vectorize_file_content(
nk_id=nk_id,
file_content=content_for_embedding, # ✅ 使用摘要/兜底
file_name=filename,
company_code=company_code,
user_id=user_id
)
统一后,
upload-file
与upload-url
的语义一致,排障成本大幅下降。
四、验证与结果(事实闭环)
-
长文/长网页:不再触发
NoneType
,向量化成功率显著提升 -
日志观测:
content_len
落在预期范围内(通常 < 1200) -
RAG 检索:可稳定召回摘要关联片段,不再因超限失败导致空检索
五、风险与防线(把坑填平)
-
摘要失败风险:仍可能出现 LLM 侧异常
-
处理:兜底切片(前 1000 字)+ 失败重试(指数退避)
-
-
摘要信息损失:过度压缩可能漏关键信息
-
处理:摘要长度可配置(如 600/800/1200 字),按业务类别动态阈值
-
-
可观测性:没有数据就没有改进
-
处理:统一日志字段:
source_type
、raw_len
、summary_len
、embed_len
、cost_ms
、model_name
、err_code
-
六、我们学到了什么(可复用的方法论)
-
链路一致性优先:内容处理与向量化的输入语义必须对齐;一个用摘要,一个用全文,是隐蔽的不一致源。
-
摘要是工程而非装饰:在 RAG 管道里,摘要不仅是“字段”,而是稳定性的第一道闸门。
-
为失败设计:兜底策略(截断/降级/重试)要写进“第一版”,否则问题只会在流量上来后集中爆炸。
-
可观测即可改进:标准化日志字段,让每次失败都有“坐标”。
七、建议的提交说明(可直接使用)
fix(rag): 统一向量化输入为摘要,长文不再触发 NoneType
- upload-file / upload-url 改为优先使用 compress_content 进行向量化
- 当摘要缺失/异常时,降级为原文前 1000 字
- 新增日志:raw_len/summary_len/embed_len,便于排查与观测
- 回归验证:大文档/长网页不再出现 'NoneType' object is not subscriptable
八、后续优化路线(前瞻)
-
动态切片 + 语义融合:对无摘要场景,按句/段落滑窗嵌入并聚合,提升代表性
-
领域化摘要提示词:让摘要对齐检索目标(问答/事实/流程),减少信息“压没了”的风险
-
批处理与限流:嵌入服务加批量接口和 QPS 控制,避免峰时抖动
一句话收尾:
信息密度 要服务 资源边界;在向量化这一步,“可控的高密度摘要”永远优于“失控的长篇大论”。