首页 / 文章 / 实用笔记:Qdrant如何通过原生ColBERT将RAG令牌成本降低67%

实用笔记:Qdrant如何通过原生ColBERT将RAG令牌成本降低67%

《实用笔记》操作指南:Qdrant如何通过原生ColBERT将RAG令牌成本降低67%——为采用该模式的团队提供的合同模板、检查清单及可直接使用的代码片段。

1929 词

以下内容围绕“Qdrant如何通过原生ColBERT重排序将RAG令牌成本降低67%”这一主题,梳理出一条实用的实施路径。重点在于各种契约、校验条件以及可直接使用的代码占位符,而非激励性描述。 在完成概览阶段时,首先明确契约要求:所需的输入参数、成功标志,以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 需同时记录正常流程与异常恢复路径。重试机制、人工审核环节以及错误消息处理都是产品功能的一部分,而非后续需要补充的内容。

我们不需要的页面

将“We Don stage”视为可度量的操作界面使用效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某一步骤失败时,故障应指向单一责任模块,而非复杂的流程链。 为每轮操作和每次会话设定预算额度。智能工具会大量消耗上下文资源,设置上限可避免演示过程变成意外的费用账单。

为何选择 Qdrant 而非其他方案?

将“为何选择 Qdrant 而非其他方案”这一阶段视为可度量的评估维度最为有效。在扩大范围之前,先记录一份最佳案例、一个失败实例以及回滚说明。 把这一阶段视为输入与经过验证的输出之间的契约。为相关文档命名,明确成功标准,绝不允许出现悄无声息的半完成状态。 需为每轮及每次会话设定令牌预算。智能工具往往会过度扩展上下文,设置上限可避免演示过程变成意外的费用账单。

我们实际正在构建什么

“我们实际是什么”阶段若被视为可度量的对象,效果会更好。在扩大范围之前,先记录一份最佳处理方案、一个故障案例以及回滚说明。 在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,就能避免从演示环境过渡到共享环境时出现意外账单。 为每轮操作和每次会话设定令牌预算。智能工具往往会大量消耗上下文资源,设置上限可防止演示过程变成意外收费的源头。 “我们实际是什么”阶段若被视为可度量的对象,效果会更好。在扩大范围之前,先记录一份最佳处理方案、一个故障案例以及回滚说明。 需同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及死信处理都是产品功能的一部分,而非后续需要补充的内容。

成本与性能基准总结

在成本性能基准总结阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。相比冗长的脚本,更应采用小型、可测试的单元。当某个步骤失败时,故障原因应能明确指向单一责任方,而非复杂的流程链。如果后续步骤是代码调用或工具调用,相比自由形式的文字描述,结构化且经过模式验证的输出更为合适。

设置集合模式

在“设置集合”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与已验证输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 当下一步操作为代码编写或工具调用时,优先采用具有架构验证的结构化输出,而非自由形式的文本描述。

from qdrant_client import QdrantClient, models
# ADDED: Load FastEmbed models locally on CPU
from fastembed import TextEmbedding, LateInteractionTextEmbedding
COLLECTION_NAME = "legal_discovery"
DENSE_DIM = 384  # BAAI/bge-small-en-v1.5
COLBERT_DIM = 128  # colbert-ir/colbertv2.0
# ADDED: Instantiate the vector models
dense_model = TextEmbedding("BAAI/bge-small-en-v1.5")
colbert_model = LateInteractionTextEmbedding("colbert-ir/colbertv2.0")
client = QdrantClient("<http://localhost:6333>")
client.create_collection(
    collection_name=COLLECTION_NAME,
    vectors_config={
        "dense": models.VectorParams(
            size=DENSE_DIM,
            distance=models.Distance.COSINE,
            quantization_config=models.BinaryQuantization(
                binary=models.BinaryQuantizationConfig(always_ram=True),
            ),
        ),
        "colbert": models.VectorParams(
            size=COLBERT_DIM,
            distance=models.Distance.COSINE,
            multivector_config=models.MultiVectorConfig(
                comparator=models.MultiVectorComparator.MAX_SIM
            ),
            on_disk=True,
            hnsw_config=models.HnswConfigDiff(m=0),
        ),
    },
)

统一查询流程

在“统一查询管道”阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测其中的隐藏状态。应在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境切换到共享环境时出现意外费用。当下一步操作为代码编写或工具调用时,宜使用具有架构验证的结构化输出,而非自由形式的文本。在“统一查询管道”阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测其中的隐藏状态。需同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及错误处理措施都是产品本身的一部分,而非后续需要补充的内容。

# ADDED: Generate query embeddings (ColBERT uses query_embed to add prefix padding)
dense_query = next(dense_model.query_embed(query)).tolist()
colbert_query = next(colbert_model.query_embed(query)).tolist()
# Run the two-stage query in one network round-trip
results = client.query_points(
    collection_name=COLLECTION_NAME,
    prefetch=models.Prefetch(
        query=dense_query,
        using="dense",
        limit=prefetch_limit,
        params=models.SearchParams(
            quantization=models.QuantizationSearchParams(rescore=False),
        ),
    ),
    query=colbert_query,
    using="colbert",
    limit=top_k,
    with_payload=True,
)

从数据块到句子的转换

在处理“从数据块到……”这一阶段时,首先需明确相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改不会出错。 建议采用小型、可测试的单元,而非冗长的脚本。当某个步骤失败时,故障应指向单一的责任模块,而非复杂的流程链。 应对稳定的系统指令和工具结构进行缓存。重复发送相同的开头信息是造成资源浪费的常见原因。

import re
import numpy as np
# ADDED: Basic sentence splitter regex
SENTENCE_SPLIT = re.compile(r"(?<=[.;])\s+(?=[A-Z])")
def max_sim(query_vecs: np.ndarray, doc_vecs: np.ndarray) -> float:
    # Compute token-to-token similarity matrix
    sims = query_vecs @ doc_vecs.T  # (num_query_tokens, num_doc_tokens)
    # Sum the maximum similarity scores along the document axis
    return float(sims.max(axis=1).sum())
def isolate_sentences(chunk_text: str, query_vecs: np.ndarray, colbert_model, top_n: int = 1):
    # ADDED: Split chunk text into candidate sentences
    sentences = [s.strip() for s in SENTENCE_SPLIT.split(chunk_text) if len(s.strip()) > 15]
    if not sentences:
        return [(chunk_text, 0.0)]
    # Embed each sentence locally using ColBERT
    sentence_vecs = list(colbert_model.embed(sentences))
    scored = [(sentences[i], max_sim(query_vecs, sentence_vecs[i])) for i in range(len(sentences))]
    scored.sort(key=lambda pair: pair[1], reverse=True)
    return scored[:top_n]
def build_optimized_prompt(query: str, chunk_texts: list[str], colbert_model) -> str:
    query_vecs = next(colbert_model.query_embed(query))
    context_parts = []
for i, text in enumerate(chunk_texts):
        top_sentences = isolate_sentences(text, query_vecs, colbert_model, top_n=1)
        isolated_text = " ".join(s for s, _ in top_sentences)
        context_parts.append(f"[Source Chunk {i+1}]: {isolated_text}")
    context_str = "\n\n".join(context_parts)
    return f"Context:\n{context_str}\n\nQuestion: {query}\nAnswer:"

数据块大小的金科玉律:为何数据块边界对准确性至关重要

在处理“黄金法则”阶段时,首先写下契约:所需的输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,杜绝默许的部分完成。 缓存稳定的系统指令和工具架构。重复发送相同的开头信息是导致资源浪费的常见原因。

权衡显而易见

在处理“The Tradeoff Speaks for”阶段时,首先需写下契约内容:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前明确成本有助于避免从演示环境过渡到共享环境时出现意外费用。 缓存稳定的系统指令和工具架构。重复发送相同的开头信息是导致资源浪费的常见原因。 在处理“The Tradeoff Speaks for”阶段时,首先需写下契约内容:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及死信处理都是产品的一部分,而非后续需要补充的功能。

财务影响是什么

将“财务阶段”视为可度量的对象来处理时效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某个步骤失败时,故障应指向单一责任主体,而非复杂的流程链。 为每轮及每次会话设定预算额度。智能工具会大量消耗上下文资源;设置上限可避免演示过程变成意外的费用账单。

面向生产环境的设计要点

在开发阶段,将设计要点视为可衡量的指标最为有效。在扩大范围之前,先记录一份最佳实践案例、一个失败案例以及回滚说明。 把这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,绝不允许默许不完整的完成状态。 为每轮操作和每次会话设定预算额度。智能工具往往会过度扩展上下文,设置上限可避免演示过程变成意外的费用账单。

GitHub

将 GitHub 测试环境视为可度量的对象时,其效果最佳。在扩大测试范围之前,需记录一份理想的操作流程、一个故障案例以及回滚说明。 在功能测试结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外账单。 为每次操作和每轮会话设定令牌预算。智能工具往往会大量消耗上下文资源,设置上限能防止演示环境变成意外收费的源头。 将 GitHub 测试环境视为可度量的对象时,其效果最佳。在扩大测试范围之前,需记录一份理想的操作流程、一个故障案例以及回滚说明。 需同时记录正常流程与异常恢复流程。重试机制、人工审核环节以及错误处理措施都是产品不可或缺的部分,而非后续需要补充的内容。

参考资料

在“参考阶段”,在修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。相比冗长的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能指向单一责任模块,而非复杂的流程链。如果后续步骤是代码调用或工具调用,应优先使用具有结构化格式且经过模式验证的输出,而非自由形式的文本。

操作检查清单

在“操作检查清单阶段”,在修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。

将配置信息置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一处,这样操作人员无需查看全部内容即可进行审计。

当下一步需要编写代码或调用工具时,应优先选择具有结构化格式且经过模式验证的输出,而非自由形式的文本。

在调整提示词之前,需先使用固定问题集来衡量检索覆盖率。仅仅更换提示词往往无法解决检索效果不佳的问题。

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

应将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现无声的、不完整的处理结果。

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

关于 98b4b4d4d553 的批量处理说明:请将提供商密钥移出代码仓库,设定单会话令牌上限,并将日志存储在评估用示例文件旁,以便后续更换模型时仍能保持数据可比性。