Python 现代开发学习笔记

(六)先过滤可读材料,再检索与排序

1. 先用列表比较筛选顺序

先不调用向量库,只比较已经算好得分的三个候选:Bob 私人材料 0.99、公共 NumPy 材料 0.95、Alice 计划 0.80。当前用户是 Alice,需要从自己可读的材料中取得两项。

"""在已算好的候选得分上比较两种筛选顺序。"""
candidates = [
    {"source": "bob-private", "owner": "bob", "score": .99},
    {"source": "numpy-public", "owner": "public", "score": .95},
    {"source": "alice-plan", "owner": "alice", "score": .80},
]
# Alice 的候选集合先确定为公共材料和自己的材料。
allowed = [item for item in candidates if item["owner"] in ("public", "alice")]
correct = sorted(allowed, key=lambda item: item["score"], reverse=True)[:2]
# 若先在全库截断到两个结果,再删除无权读取的项,就少取了一份可用材料。
global_two = sorted(candidates, key=lambda item: item["score"], reverse=True)[:2]
late_filter = [item for item in global_two if item["owner"] in ("public", "alice")]
assert [item["source"] for item in correct] == ["numpy-public", "alice-plan"]
assert [item["source"] for item in late_filter] == ["numpy-public"]
print("先筛选:", correct, "先截断:", late_filter)

先去掉 Bob 的材料,剩下两项,排序后都能返回。如果先取全库前两项,得到 Bob 和公共材料,再去掉 Bob,结果只剩公共材料;Alice 的计划在第一次截断时已经被丢掉了。

同一组三个候选先筛选再取两项得到两份材料,先截断再筛选只剩一份

2. 将同样的顺序交给向量库

InMemoryVectorStore 把少量文档及其向量放在当前进程中。项目语料只有八块,重开进程时重新建库,便于观察。数据量增加后,可以将索引写入磁盘或独立服务,同时保存文档版本、分块规则和向量模型标识。

该类型从 langchain_core.vectorstores 导入。from_documents 是创建入口:接收 Document 列表和编码器,计算并保存文档向量,然后返回可查询的存储对象。问题到来时,使用同一个编码器计算查询向量,按相似度寻找材料。

# 对照完整文件阅读这一段;此处保留当前步骤使用的函数或类型。
class NoteIndex:
    """小型语料重开时重新建立索引,业务计划与图检查点另行持久保存。"""
    def __init__(self, embeddings: Embeddings | None = None):
        self.chunks = load_chunks()
        self.store = InMemoryVectorStore.from_documents(self.chunks, embedding=embeddings or CharacterEmbeddings())

    def search(self, query: str, owner: str, k: int = 2) -> list[dict]:
        # 先按归属过滤,再选择 k 个结果,模型不能指定另一个用户。
        pairs = self.store.similarity_search_with_score(
            query, k=k, filter=lambda document: document.metadata["owner"] in ("public", owner))
        return [{"chunk_id": doc.metadata["chunk_id"], "source": doc.metadata["source"],
                 "text": doc.page_content, "score": float(score)} for doc, score in pairs if score >= 0.12]

similarity_search_with_score 返回 (Document, score)。本例先用 filter 选出公共材料和当前用户的材料,再从中取两个结果。若先取全库前两名、最后才删除其他用户的内容,结果可能只剩很少几条,也让后续代码承担了不必要的数据处理。

文档建库与问题编码两条路径在权限过滤和相似度排序处汇合

对“NumPy 切片怎样避免改动原数组”,Alice 的检索结果先是 numpy.md:0,得分约 0.454545;随后是 alice-plan.md:0,约 0.218750。Bob 的私人学习计划不会进入本次候选材料。

本例还筛掉低于 0.12 的结果。这个值要随向量表示和问答集调整:挑出应命中的问题、同义问法和材料中没有答案的问题,分别查看得分与返回内容,再确定取舍。即便得分较高,也要读原文判断它是否真正支持问题中的论断。

增加笔记后,重新分块并更新对应向量;删除或修改笔记时,同步移除旧块,避免引用过期文字。这里的编号按“来源文件名:块序号”产生;持续编辑的知识库还应记录版本或内容摘要,才能回查某次回答使用的具体材料。

笔记正文与全部题解 · Ctrl / ⌘ K