diff --git a/KNOWLEDGE_RAG_STAGE12.md b/KNOWLEDGE_RAG_STAGE12.md index c3ea313..5b2c515 100644 --- a/KNOWLEDGE_RAG_STAGE12.md +++ b/KNOWLEDGE_RAG_STAGE12.md @@ -64,16 +64,20 @@ KnowledgeDocument -> chunk -> embed -> candidate retrieve -> rerank -> citation ## 实际结果 - 修复前基线:阶段 11 定向命令 `python -m pytest tests/test_knowledge_pipeline.py tests/test_knowledge_rag.py -q --disable-warnings` 为 `13 passed`;其中 10 条作业知识点只读取前 8 条,唯一匹配的第 10 条证据被漏掉。 -- 修复后定向命令:`python -m pytest tests/test_knowledge_pipeline.py tests/test_knowledge_vector_store.py tests/test_knowledge_eval.py tests/test_knowledge_rag.py -q --disable-warnings` 为 `19 passed`,退出码 0,37.94s。 -- 评审要求的评估命令:`python -m pytest tests/test_knowledge_eval.py -q --disable-warnings` 为 `4 passed`,退出码 0,0.24s;`python -m pytest tests/test_knowledge_eval.py tests/test_knowledge_rag.py -q --disable-warnings` 为 `15 passed`,退出码 0,37.89s。 -- 全量回归:`python -m pytest -q --disable-warnings` 为 `681 passed`,退出码 0,13:26;warnings 为既有项目噪音和测试环境输出,本次没有失败用例。 -- 固定评估命令:`python -m services.knowledge_eval`;5 个固定问题,4 个有标注问题,按“每题前 k 个去重文档命中数 / 该题相关文档数”计算 Recall@1=0.875、Recall@k=0.875;模式为 3 次 vector、1 次 keyword fallback、1 次 no-result,期望模式不匹配数为 0。 -- 固定评估的普通问题耗时:切分/索引构建 mean 0.381ms、P95 1.701ms;查询 mean 0.051ms、P95 0.103ms;合计 mean 0.433ms、P95 1.762ms。 -- 固定 64 文档性能样本、64 个切片、100 次查询、top-k=8:构建 0.901ms;查询 mean 0.418ms、P95 0.493ms;总耗时 42.745ms,按运行摊销 0.427ms/次。 +- 本次审查指定定向命令:`python -m pytest tests/test_knowledge_eval.py tests/test_knowledge_vector_store.py tests/test_knowledge_rag.py -q --disable-warnings` 为 `19 passed`,退出码 0,47.06s。 +- 全量回归(前一提交 `d370b5f`):`python -m pytest -q --disable-warnings` 为 `681 passed`,退出码 0,13:26;该结果未在本次标签修订后重跑,warnings 为既有项目噪音和测试环境输出。 +- 固定评估命令:`python -m services.knowledge_eval`;5 个固定问题,4 个有标注问题,最后一题的两个相关文档现在均为真实的数组下标文档(`array-boundary`、`array-indexing`),不再把排序稳定性文档误标为相关文档。按“每题前 k 个去重文档命中数 / 该题相关文档数”计算 Recall@1=0.875、Recall@k=0.875;模式为 3 次 vector、1 次 keyword fallback、1 次 no-result,期望模式不匹配数为 0;共索引 16 个切片。 +- 固定评估的普通问题耗时:切分/索引构建 mean 0.507ms、P95 2.239ms;查询 mean 0.061ms、P95 0.119ms;合计 mean 0.570ms、P95 2.326ms。 +- 固定 64 文档性能样本、64 个切片、100 次查询、top-k=8:构建 1.096ms;查询 mean 0.463ms、P95 0.473ms;总耗时 47.388ms,按运行摊销 0.474ms/次。 - 行为回归:查询第 10 条唯一知识点时从旧的“前 8 条漏检”变为返回 `assignment-kp:10`;最终结果仍最多 8 条。超过 64 条时第 65 条及以后保持明确的资源边界。 - 故障实验:向量 embedding 抛出异常时返回 `KNOWLEDGE_RETRIEVAL_UNAVAILABLE`,学生端继续 answer-only,不暴露内部异常或伪造引用。 - `git diff --check`:通过。 +### 维护者抽查答复 + +- ID10 能命中、ID70 不能进入结果,是因为 `MAX_INDEX_DOCUMENTS=64` 控制从数据库读取并建立索引的候选文档数:ID10 在前 64 条范围内,ID70 超出范围会被有意截断。`MAX_EVIDENCE=8` 是另一层边界,只控制最终返回给调用方的证据最多 8 条;它不决定是否建立索引。 +- 查询“数据库迁移”时,空索引走 `no_result`,返回 `NO_KNOWLEDGE_EVIDENCE`,保持原有 answer-only 回退;索引含数组/指针文档但没有匹配时走 `priority_fallback`,按稳定优先级返回已有证据,避免有作业知识记录时把学生端结果突然变成无证据。只有索引为空时才进入 no-result 分支。 + ## 事实、推断与未解决问题 - 已确认事实:索引只存在于单次调用创建的 `HybridKnowledgeIndex`;未写入数据库、磁盘、Redis 或跨请求全局缓存;候选读取上限为 64,最终证据上限为 8;固定集和回归测试均在无生产凭据的隔离环境运行。 diff --git a/tests/fixtures/knowledge_rag_eval.json b/tests/fixtures/knowledge_rag_eval.json index 23a3d5c..dce0378 100644 --- a/tests/fixtures/knowledge_rag_eval.json +++ b/tests/fixtures/knowledge_rag_eval.json @@ -20,6 +20,13 @@ "content": "稳定排序会保持相等元素的原有相对顺序。", "source_type": "offline-eval", "priority": 1 + }, + { + "document_id": "array-indexing", + "title": "数组下标", + "content": "数组下标从零开始,访问元素前确认索引合法。", + "source_type": "offline-eval", + "priority": 1 } ], "queries": [ @@ -50,7 +57,7 @@ }, { "query": "下标", - "relevant_document_ids": ["array-boundary", "sorting-stability"], + "relevant_document_ids": ["array-boundary", "array-indexing"], "expected_mode": "vector", "top_k": 1 } diff --git a/tests/test_knowledge_eval.py b/tests/test_knowledge_eval.py index f0e6231..5bc79e5 100644 --- a/tests/test_knowledge_eval.py +++ b/tests/test_knowledge_eval.py @@ -22,6 +22,10 @@ def test_fixed_knowledge_eval_reports_vector_fallback_and_no_result_modes(): assert metrics["mean_total_latency_ms"] >= metrics["mean_query_latency_ms"] assert metrics["performance_sample"]["document_count"] == 64 assert metrics["performance_sample"]["chunk_count"] == 64 + assert metrics["case_results"][-1]["retrieved_document_ids"] == [ + "array-boundary" + ] + assert metrics["case_results"][-1]["recall_at_k"] == 0.5 def _write_fixture(tmp_path, documents, queries):