首页 / 文章 / 实用笔记:我通过构建问答系统学会了RAG

实用笔记:我通过构建问答系统学会了RAG

《实用笔记:我通过构建问答系统学习RAG》的操作指南:面向采用该模式的团队提供的合同、检查清单以及可直接插入的代码片段。

2018 词

可将此内容视为《通过构建问答系统学习RAG》一文中理念的面向操作员的优化版本:清晰的阶段划分、有序的代码模块,以及便于交接时参考的恢复说明。在“概览”阶段,若能将其视为可量化的基准,效果会更好——在扩大范围之前,先记录一份最佳示例、一个故障案例以及回滚说明。在功能结果旁还需记录执行时间以及令牌或查询成本,提前了解成本情况可避免在从演示环境过渡到共享环境时出现意外账单。

关于RAG的简短说明

关于流程的简要说明:在修改代码之前,需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一个位置,以便操作人员无需查看整个流程即可进行审核。 需引用实际作为答案依据的段落。如果没有引用,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

系统的结构

在修改代码之前,需先明确系统的阶段划分、各步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。 需同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。 必须引用那些真正作为答案依据的段落。如果没有引用,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

构建数据摄取管道

在构建数据摄取管道阶段时,应在修改代码之前明确输入内容、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏的状态。相比冗长的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能明确指向单一责任方,而非整个混乱的管道。必须引用实际作为答案依据的段落;没有引用的话,操作人员就无法区分是虚假信息还是索引缺失所致。

下载

在下载阶段,应在修改代码之前明确输入内容、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

{
  "doc_id": "bsp-mpr-2023-q3",
  "title": "Monetary Policy Report Q3 2023",
  "period_label": "Q3 2023",
  "publication_date": "2023-08-17",
  "url": "https://www.bsp.gov.ph/..."
}

解析

在解析阶段,应在修改代码之前明确输入内容、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。除了功能结果外,还需记录处理时间以及令牌或查询成本。提前显示成本信息,可避免在从演示环境切换到共享环境时出现意外费用。必须注明实际作为答案依据的段落;没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

分块、嵌入与插入更新

在分块嵌入与插入阶段,修改代码之前需明确输入内容、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储及功能标志应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审核。 需注明实际作为答案依据的段落。若没有引用,操作人员就无法区分是幻觉内容还是索引缺失所致。

def chunk_text_by_tokens(text, enc, chunk_size=512, overlap=64):
    tokens = enc.encode(text)
    stride = chunk_size - overlap
    chunks = []
    start = 0
    while start < len(tokens):
        window = tokens[start : start + chunk_size]
        chunks.append(enc.decode(window))
        if start + chunk_size >= len(tokens):
            break
        start += stride
    return chunks

检索层与最新性问题

在修改代码之前,对于检索层和阶段,需明确输入内容、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。 必须引用实际作为答案依据的段落。如果没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

RECENCY_WEIGHT = 0.85

def combined_score(hit):
    relevance = hit.score / max_score
    recency = (pub_date - min_date).days / date_span_days  # 0.0 to 1.0
    return (1 - RECENCY_WEIGHT) * relevance + RECENCY_WEIGHT * recency
# scope to Q1 2023 only
retrieve_chunks(query, period_label_key="q1_2023")

# scope to a date range
retrieve_chunks(query, publication_date_from="2023-01-01", publication_date_to="2023-12-31")

生成层

在生成层阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相较于冗长的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能指向单一责任模块,而非复杂的流程链。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。 在生成层阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 除了功能结果外,还需记录执行时间以及token或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

你实际遇到的问题

在处理“你实际遇到的问题”这一阶段时,首先需明确相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 应将配置信息与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中管理,这样操作人员无需查看整个系统结构即可进行审计。 在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词往往无法解决检索效果不佳的问题。

PDF模板文件导致的代码污染

在处理 PDF 模板中的冗余代码阶段时,首先需明确相关规范:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改不会偏离原有设计。

def _clean_parsed_text(text):
    text = text.replace("\x0c", "\n\n")
    text = re.sub(r"Classification:\s*GENERAL\s*\n", "", text)
    text = re.sub(r"Monetary Policy Report\s*[--][^\n]+\|\s*\d+\s*\n", "", text)
    text = re.sub(r"\n{3,}", "\n\n", text)
    return text.strip()

意外数据清除

在处理 Accidental 集合清除阶段时,首先需写下相关规范:所需输入、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 优先选择小型、可测试的单元,而非冗长的脚本。当某个步骤失败时,故障应指向单一责任点,而非复杂的流程链。 在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词很难解决检索效果不佳的问题。 在处理 Accidental 集合清除阶段时,首先需写下相关规范:所需输入、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

def _should_recreate_qdrant_collection():
    if _is_production_environment():
        return False
    explicit = os.environ.get("QDRANT_RECREATE_COLLECTION", "").lower()
    if explicit in ("1", "true", "yes"):
        return True
    app_env = (os.environ.get("ENVIRONMENT") or "").lower()
    return app_env in ("development", "dev", "local")

重新导入后的重复数据块

在将“重新导入后的重复数据块”这一环节视为可度量的指标时,其效果最佳。在扩大范围之前,应先记录一份理想的处理结果、一个故障案例以及回滚说明。 配置应置于应用程序代码之外。环境文件、密钥存储和功能开关应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。 数据分块策略与检索策略应分开。当质量指标发生变化时,修改其中一项不应强制要求重新编写另一项的策略。

client.delete(
    collection_name=collection,
    points_selector=models.Filter(must=[
        models.FieldCondition(
            key="source_file",
            match=models.MatchValue(value=source_file)
        )
    ]),
)

免费套餐下的冷启动延迟

将冷启动延迟视为可测量的指标时,其在不同阶段的表现最为理想。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 同时记录正常流程和恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续需要补充的功能。 应将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

选择分块大小

将“确定分块大小”这一阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一份理想的输出样本、一个故障案例以及回滚说明。 相较于庞大的脚本,应优先选择小型且易于测试的单元。当某个步骤出现故障时,故障原因应能明确指向某个具体的责任模块,而非复杂的流程链。 应将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。 将“确定分块大小”这一阶段视为可度量的对象来处理效果最佳。在扩大范围之前,先记录一份理想的输出样本、一个故障案例以及回滚说明。 除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

你会做出的不同调整

在“你会怎么做”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审核。 需引用实际作为答案依据的段落。如果没有引用,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

试试看

在“尝试阶段”,在修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的内容。 必须引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

运营检查清单

在处理运营检查清单阶段时,首先需写明相关约定:所需输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。

应将此阶段视为输入参数与验证后输出结果之间的契约。为相关成果命名,明确成功标准,并杜绝默许部分完成的情况。

在调整提示词之前,先使用固定的问题集测试召回率。频繁更换提示词很难改善较差的检索效果。

锁定依赖版本,并记录用于演示的图像摘要。可重复性比经验知识更重要。

在功能结果之外,还需记录处理时间以及token或查询成本。提前了解成本情况,可避免从演示环境过渡到共享环境时出现意外费用。

在调整提示词之前,先使用固定的问题集测试召回率。频繁更换提示词很难改善较差的检索效果。

在推广该技术栈之前,应先冻结版本,为关键流程保存标准记录,并明确回滚步骤。共享环境需要设置速率限制、租户验证机制,以及明确的密钥轮换负责人。与其追求华丽的临时演示,不如注重扎实的可靠性。

a30a0175d827的批处理说明:不要将提供者密钥放入代码仓库,为每个会话设置令牌上限,并将转录内容存储在评估测试用例的旁边,以便后续更换模型时仍能保持可比性。