[Bug]: QA parser for .csv silently corrupts Q&A pairs: csv.reader quote handling + row/line index mismatch splice answers from wrong lines

用户在 RAGFlow 的 "Q&A chunk method"(问答分块方法)中,使用制表符(TAB)分隔的 .csv 文件进行解析时触发。当文件中包含以双引号(")开头的续行时,会导致内容被错误地拼接到其他问答对中,一些问答对丢失,且系统报告处理成功。该问题在大型 CSV 文件(如案例中的 67

[Bug]: QA parser for .csv silently corrupts Q&A pairs: csv.reader quote handling + row/line index mismatch splice answers from wrong lines

[Bug]: QA parser for .csv silently corrupts Q&A pairs: csv.reader quote handling + row/line index mismatch splice answers from wrong lines

快速结论:该报错发生在使用 RAGFlow 的 “Q&A chunk method” 解析制表符分隔的 .csv 文件时。优先排查 rag/app/qa.py 中 csv 分支的 csv.reader 调用是否添加了 quoting=csv.QUOTE_NONE 参数,以及其行索引逻辑是否使用物理行号而非 reader 的 record 索引。

问题场景

用户在 RAGFlow 的 “Q&A chunk method”(问答分块方法)中,使用制表符(TAB)分隔的 .csv 文件进行解析时触发。当文件中包含以双引号(”)开头的续行时,会导致内容被错误地拼接到其他问答对中,一些问答对丢失,且系统报告处理成功。该问题在大型 CSV 文件(如案例中的 679k 行 / 13,435 对荷兰语工单数据)中影响显著。

报错原文

# 问题核心在于隐式的数据损坏,而非明确的报错原文。用户观察到以下错误现象:
# 1. 答案字段包含了来自其他问答对的原始行(包括 TAB 和其他配对的问题)。
# 2. 部分续行完全消失。
# 3. 部分问答对凭空消失,且 progress_msg 报告成功。

# 最小复现代码输出示例(来自 Issue 修改前的逻辑):
'Q1: how to reset password' -> 'A1 step one\n"see the attached error log'
'Q2: printer offline' -> 'A2 restart spooler\nQ2: printer offline\tA2 restart spooler'
'Q3: vpn drops' -> 'A3 update client\nQ3: vpn drops\tA3 update client'

原因分析

两个缺陷相互作用导致了此问题:

  1. 意外的引号处理:csv.reader 被调用时使用了默认的 quotechar='"'。任何以双引号(”)开头且未在当行闭合的续行,都会开启一个引用字段,从而将后续的物理行(包括完整的 Q<TAB>A 行)吞并为一个 reader record(记录)。
  2. 行索引与记录索引不匹配:在处理 len(row) != 2 的 fallback 逻辑(续行追加)时,代码使用了 reader 的记录索引 i 来访问物理的 lines 列表。经过任何多行引用记录后,i 会永久性地落后于物理行号,导致后续所有 fallback 操作都会从错误的行提取文本,这是其他工单行被拼接到无关答案中的直接原因。偏移量会随着每个非闭合引号行的出现而增长。

环境排查

  • RAGFlow 版本:问题在 v0.26.x 部署中被观测到,且受影响的代码在当前 main 分支(rag/app/qa.py csv 分支,约 347-375 行)上依然存在。
  • 相关文件:代码位于 rag/app/qa.py 文件的 csv 处理分支。
  • csv.reader 调用:需要确认调用时是否显式设置了 quoting=csv.QUOTE_NONE 参数。若无此参数,默认的引号处理行为会触发第一个缺陷。

解决步骤

可优先尝试:

  1. 修改 rag/app/qa.py 中 csv.reader 的调用,显式设置 quoting=csv.QUOTE_NONE
    • 修改前:reader = csv.reader(lines, delimiter=delimiter)
    • 修改后:reader = csv.reader(lines, delimiter=delimiter, quoting=csv.QUOTE_NONE)
  2. 可能还需要修改续行追加的索引逻辑,将 lines[i] 替换为追踪物理行号,而不是使用 reader 的 record 索引 i。具体的实现方式可参考 Issue #16433 修改了最后的追加行号(从 len(list(reader)) 改为 len(lines)),但它并未彻底修复 index 漂移问题。

备注:上述 Option 1(添加 quoting=csv.QUOTE_NONE)被视为预期的正确最小修复方案。它通过禁止引号特殊含义来确保每个物理行恰好是一个 record,从而使 ilines 保持同步,同时解决两个缺陷。但需注意,此方案假设 CSV 中的 TITLE 和 ANSWER 字段本身不含引号作为元字符。

验证方法

使用 Issue 中提供的最小复现代码进行测试:

  • 输入数据:创建一个包含被双引号(”)打开的续行、正常问答对及其续行的制表符分隔文本列表。
  • 预期输出:每个问答对(Q1-Q3)都能正确获取其所有续行(包括被双引号包裹的行),并且不同问答对的内容不发生混淆。
  • 实际验证:将修复后的代码用于产生问题的 679k 行荷兰语工单数据集,确保所有 13,435 个问答对都被正确且完整地创建,无问答对丢失或内容交叉。

参考来源

infiniflow/ragflow #16791

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

celebrityanime
celebrityanime
文章: 14105

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注