Skip to main content
Glama
2gg-bit

evidence-sieve

by 2gg-bit
README.md
# Evidence Sieve · 证据筛

面向 Agent 的本地证据筛选小工具:**保守归组 + 差异保护 MMR**,保留来源、按版本和对象过滤,通过 **CLI / MCP / Skill** 使用,不需要修改 Agent 源码。

适合这样的场景:多个文档反复描述同一个事实,直接 Top-K 被重复段落占满,真正的限制条件和互补信息进不了上下文。

**v0.2 默认使用 `sieve_mmr`、λ=.7。** 它针对标准 MMR 的两个问题:高相关副本仍可能反复占位,以及不同数值、否定、条件可能被当成冗余压低。[MMR 原理与实现来源](docs/MMR.md)。

本版同时公开开发集调参、未用于调参的 NFCorpus 测试、旧 SciFact 回归及消融,并保留“精确归组+MMR”强基线。没有运行下游生成模型,不宣称提高最终回答正确率或节省总 token。

<!-- V2_RESULTS_START -->
以下为文档 Recall;标准 MMR 的 λ 来自独立开发集,不在测试集挑参数。

| 测试条件 | 调参标准 MMR(λ=.7) | Sieve-MMR(λ=.7) | 精确归组+MMR(调参 λ=1) |
| --- | --- | --- | --- |
| NFCorpus 自然文档(新留出) | 7.30% | 8.18% | 8.18% |
| NFCorpus 重复压力(新留出) | 6.98% | 8.09% | 8.09% |
| SciFact 自然文档(旧集回归) | 51.87% | 55.50% | 55.50% |
| SciFact 重复压力(旧集回归) | 51.21% | 55.17% | 55.17% |

**结论有限:优于本次调参的标准 MMR,但没有超过精确去重+相关性排序强基线。** 消融未发现语义归组额外提高 Recall/事实覆盖;新规则增加筛选耗时,来源列表增加 token。旧 12 个同义场景中,同 λ=.7 的事实覆盖为 94.44% 对 86.11%;但 MMR λ=.3 也能达到 100%,该集合不属于独立盲测。

置信区间、消融、成本和逐题记录见 [v0.2 完整实验报告](docs/BENCHMARKS_V2.md)。[v0.1 历史结果](docs/BENCHMARKS.md) 保留可查。
<!-- V2_RESULTS_END -->

## 它做什么

```mermaid
flowchart LR
    A[Agent 的问题] --> B[本地文档检索]
    B --> C[严格过滤版本和对象]
    C --> D[广召回候选]
    D --> E[保守去重与来源归组]
    E --> F[差异保护 MMR 与预算选择]
    F --> G[返回证据包给 Agent]
```

同义段落只显示一个代表原文,同时保留组内来源 ID。遇到规则能识别的数字、否定、单位、条件、代码标识符、版本和对象差异,会拒绝语义合并。随后进行 MMR 选择;对不能安全合并的证据对,排序时不施加相似度惩罚。低于相关性下限的条目不会入选。**这仍是启发式规则,不能保证找出所有重要差异。**

提供六种策略:`topk`、`mmr`、`naive_dedup`(仅向量阈值)、`exact`(精确去重)、`sieve`(保守去重后按相关性补位)和 **`sieve_mmr`(默认)**。它们共用检索编码器、过滤和预算。若需要原来的选择策略,显式传 `--method sieve`;原样复现 v0.1 则使用历史提交。

## 安装和第一次查询

要求 Python 3.11+。项目目前从 GitHub 安装,**尚未发布到 PyPI**。

Windows PowerShell:

```powershell
git clone https://github.com/2gg-bit/evidence-sieve.git
cd evidence-sieve
python -m venv .venv
.venv/Scripts/python.exe -m pip install -e ".[semantic]"
.venv/Scripts/python.exe -m evidence_sieve.cli search --corpus examples/corpus.jsonl --encoder semantic --query "备份保留多久,维护期间执行吗?" --version 2 --scope atlas
```

macOS / Linux:

```bash
git clone https://github.com/2gg-bit/evidence-sieve.git
cd evidence-sieve
python3 -m venv .venv
.venv/bin/python -m pip install -e ".[semantic]"
.venv/bin/python -m evidence_sieve.cli search --corpus examples/corpus.jsonl --encoder semantic --query '备份保留多久,维护期间执行吗?' --version 2 --scope atlas
```

语义模式使用本机 CPU,无 API Key。首次运行从 Hugging Face 下载 [多语言 MiniLM 模型](https://huggingface.co/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2),也会下载 tiktoken 编码文件;后续可复用缓存。安装 `[semantic]` 会包含 PyTorch 等较大的依赖。

若只想先验证接入,可安装 `pip install -e .` 并省略 `--encoder semantic`,使用默认 `tfidf`。**TF-IDF 是词面检索,不等同于语义去重能力。** 本项目实验也保留了该模式的负结果。

下面的短命令假设已激活虚拟环境;未激活时请使用上面的 `python -m evidence_sieve.cli` 形式。

```bash
evidence-sieve search --corpus examples/corpus.jsonl --encoder semantic --query "备份策略" --budget 1500 --k 4
evidence-sieve search --corpus examples/corpus.jsonl --encoder semantic --query "备份策略" --method mmr
evidence-sieve expand --corpus examples/corpus.jsonl --id retention
evidence-sieve serve --corpus examples/corpus.jsonl --encoder semantic
```

`serve` 会等待 MCP 客户端通过标准输入输出通信,直接启动后看似没有输出是正常现象。

## 准备自己的文档

**方式一:Markdown 文件夹。** 传 `--corpus /absolute/path/docs`,按空行切段,来源保留相对路径和行号。它不解析标题层级、front matter、PDF 或 Word,不自动推断版本。

**方式二:JSONL。** 每行一个 UTF-8 JSON 对象,推荐在需要版本/对象过滤时使用:

```json
{"id":"backup-v2-a","text":"备份保留 7 天。","source":"kb-a/backup.md#L12","version":"2","scope":"atlas","claim_key":"backup.retention","claim_value":"7 days"}
{"id":"backup-v2-b","text":"维护期间暂停自动备份。","source":"kb-b/operations.md#L8","version":"2","scope":"atlas"}
```

| 字段 | 说明 |
| --- | --- |
| `id` | 必填、唯一,供追溯和展开原文 |
| `text` | 必填,完整证据段落,最多 50,000 字符 |
| `source` | 必填,文件位置或来源说明;不会自动打开 URL |
| `version` / `scope` | 可选字符串,建议分别标记版本、产品或适用对象 |
| `claim_key` / `claim_value` | 可选,必须成对提供;用于同版本、同对象内的显式冲突提示 |

可把不同知识库的段落合并到一个 JSONL,保持 ID 唯一、来源清楚。不要把 `scope` 仅作为文件夹名随意填写:它会阻止跨 scope 合并,也影响严格查询过滤。

`--version 2` 只匹配元数据恰好为 `"2"` 的条目,缺少版本的条目会被排除。`conflicts` 只比较**已选证据中、显式填写的 claim 元数据**,不是通用自然语言矛盾检测器。

## 输出与参数

输出 JSON 的 `evidence` 每项包含代表原文、相似度、版本/对象和 `sources`;合并来源的 ID 仍可用 `expand` 查看。不会生成摘要或修改原文。`stats.duplicates_collapsed` 是整个候选池中归组的数量,不表示这些组最终都被选中。

| 参数 | 默认 | 含义 |
| --- | --- | --- |
| `--k` | 6 | 最多输出多少条证据 |
| `--fetch-k` | 40 | 筛选前最多考虑多少个候选 |
| `--budget` | 2000 | 完整紧凑 JSON 的 cl100k_base token 上限 |
| `--min-score` | 0.15 | 余弦相关性下限;不是相关概率 |
| `--duplicate-threshold` | 0.90 | 语义合并的余弦阈值 |
| `--lambda-mult` | 0.70 | `mmr` 与 `sieve_mmr` 使用;1 表示关闭多样性排序 |
| `--grouping` | semantic | `sieve_mmr` 的归组方式,可选 semantic / exact / none |
| `--no-protect-differences` | 未启用 | 关闭 `sieve_mmr` 排序阶段的差异保护,用于消融 |
| `--revision` | 未固定 | 可指定 Hugging Face 模型 commit;复现实验须固定 |

预算包括原文、所有来源和冲突信息,**不含 MCP 外层包装、宿主提示和后续展开请求**。不同宿主模型 tokenizer 不同,因此不能把这个上限当作实际账单。过长段落整个跳过,不偷偷截断;若来源列表过长,也可能导致整组无法装入预算。

## 接入 Claude Code / Codex / OpenCode / Pi

查看 [完整接入说明](docs/INTEGRATIONS.md),含绝对路径配置、Skill、环境变量、提示词示例和故障排查。标准 MCP 提供 `search_evidence`、`expand_evidence` 两个工具。Pi 等可调用 CLI。

**只筛选经本工具查询的资料,不会自动替换 Agent 的全部文件读取或搜索。** 只有候选仍留在服务内部、筛选结果才进入会话时,才可能改善上下文利用。

## 已有检索系统怎么复用

Python API 可以对自己的候选及向量进行筛选,无需重新构建一个知识库:

```python
from evidence_sieve import Document, EvidenceSieve, SearchOptions

# candidate_vectors 与 query_vector 必须由同一编码模型生成;行顺序与 documents 一致。
documents = [Document(id="a", text="备份保留 7 天。", source="kb/a.md", version="2")]
engine = EvidenceSieve(documents, vectors=candidate_vectors)
packet = engine.search("备份保留多久?", SearchOptions(version="2"), query_vector=query_vector)
```

示例中的两个向量变量由调用方提供。当前没有内置 Qdrant、Milvus、云知识库连接器,也没有重排模型。

## 实验和开发

查看 [v0.2 实验报告](docs/BENCHMARKS_V2.md)、[协议及开发补充记录](benchmarks/PROTOCOL_V2.md) 和 [原始结果](results/v0.2/)。包含 24 个旧中英文机制场景、SciFact 回归、新 NFCorpus 留出测试、多组消融、配对置信区间。开发集包含 SciFact train 全部 809 题、NFCorpus dev 全部 324 题。实验脚本不把 gold/qrels 输入算法。

```bash
python -m pip install -e ".[semantic,benchmark,dev]"
python -m pytest -q
python -m ruff check .
python -m ruff format --check .
python -m build
python benchmarks/check_mmr_reference.py
python benchmarks/mmr_study.py dev
python benchmarks/mmr_study.py test
python benchmarks/mmr_study.py controlled
python benchmarks/controlled_grid.py
python benchmarks/report_v2.py
python benchmarks/provenance_v2.py
```

只复核已发布参数时可跳过 `dev`,使用仓库已有的 `selected.json`;若算法 hash 不一致,脚本会要求重新选择参数。公开数据从 BEIR 官方 Hugging Face 固定 revision 镜像下载,数据与 embedding 缓存在忽略的 `work/`,模型在 Hugging Face 缓存中。时间指标受硬件、缓存和负载影响,完整开发网格需要较长时间。

## 当前边界

- 原型通过本地 CLI 和真实 MCP 协议测试;未做各 Agent 宿主的完整模型任务评测。
- 守护规则覆盖有限,中文数字、实体关系、复杂条件等可能误判;高相似度不是含义相同的证明。
- 默认语义模型最多编码 128 个模型 token,长段落尾部可能无法参与相似度计算;返回的原文仍是完整段落。推荐自行准备短且语义完整的切块。
- 内存内全量索引,没有增量更新、持久化向量库或大规模性能保证。TF-IDF 使用稠密矩阵,大语料可能占用大量内存。
- 不自动判定来源权威性、不推断哪个版本最新、不消除自然语言冲突。
- 目前没有下游生成模型对比,不能宣称减少幻觉、提高答案正确率或降低总成本。来源元数据和补充事实可能使返回 token **增加**。

MIT 开源。外部模型和数据各自遵循原许可证;公开 SciFact、NFCorpus 文本不随本仓库分发。数据来源与许可见 [实验报告](docs/BENCHMARKS_V2.md)。