# 规则过滤引擎设计 ## 1. 目标 当前过滤层的定位是: - 不让 LLM 直接做最终过滤决策 - 让 LLM 只产出结构化信号 - 由规则引擎输出最终 `keep / drop / review` 决策 当前链路是: `item -> content extraction -> llm summary -> filter rule engine` ## 2. 输入输出 ### 2.1 FilterInput 过滤层统一读取四类输入: - `item` - `article` - `summary` - `context` 其中: - `item` 表示上游标准化候选条目 - `article` 表示正文提取结果 - `summary` 表示结构化 LLM 摘要结果 - `context` 表示额外的用户偏好或运行时上下文 ### 2.2 FilterDecisionResult 过滤结果统一输出: - `decision` - `keep` - `drop` - `review` - `matched_rules` - `reasons` - `labels` - `priority` - `matches` 这样后续的知识库 sink、推送层或人工审核都可以稳定消费。 ## 3. 规则结构 当前规则文件位置: - `configs/filter_rules.json` 单条规则结构为: ```json { "rule_id": "keep-worth-keeping-method", "enabled": true, "stop_on_match": false, "conditions_all": [ { "field": "summary.worth_keeping", "op": "eq", "value": true }, { "field": "summary.category", "op": "in", "value": ["方法论", "工具实践"] } ], "action": { "decision": "keep", "reason": "Structured summary marked the content as worth keeping in a durable category.", "labels": ["summary", "durable"], "priority": 80 } } ``` ## 4. 当前支持的操作符 - `eq` - `ne` - `in` - `not_in` - `contains` - `overlap` - `gte` - `lte` - `exists` 当前条件组合方式: - `conditions_all` - `conditions_any` ## 5. 动态上下文字段 规则支持从其他字段动态取值,例如: ```json { "field": "summary.topics", "op": "overlap", "value": { "from_field": "context.interest_topics" } } ``` 这允许上层 Agent 在调用 `filter_summary_result` 时,把当前关注主题动态注入,而不必把偏好硬编码在规则文件里。 ## 6. 当前默认规则思路 当前默认规则分为三类: - 质量拦截 - 低质量正文直接 `drop` - 疑似截断或付费墙进入 `review` - 摘要价值判断 - `资讯 + worth_keeping=false` 直接 `drop` - `方法论/工具实践 + worth_keeping=true` 直接 `keep` - 个性化补充信号 - `summary.topics` 与 `context.interest_topics` 重叠时提升为 `keep` - 其他 `worth_keeping=true` 的结果默认进入 `review` ## 7. 规则执行流程 当前过滤流程按以下顺序执行: 1. 上层先产出结构化 `summary` 2. 可选附带 `item`、`article` 与 `context` 3. 规则引擎按 `priority` 从高到低遍历规则 4. 每条规则根据 `conditions_all` / `conditions_any` 判断是否命中 5. 命中的规则被收集为 `matches` 6. 最终根据命中结果收敛为 `keep / drop / review` 当前决策收敛规则是: - 只要命中任意 `drop`,最终结果就是 `drop` - 否则只要命中任意 `keep`,最终结果就是 `keep` - 否则如果命中任意 `review`,最终结果就是 `review` - 如果没有任何规则命中,默认回落到 `review` 这样设计的原因是: - `drop` 应该拥有最高约束力 - `keep` 只在没有硬性淘汰时生效 - 默认不自动放行未知内容 ## 8. 为什么让 LLM 调用规则 tool,而不是直接裁决 当前架构故意拆成两层: - LLM 负责生成结构化信号 - 规则引擎负责输出最终过滤决策 原因是: - LLM 适合做语义理解、归类、摘要和价值信号提取 - 规则引擎适合做稳定、可复现、可审计的最终判断 因此推荐的调用方式是: 1. LLM 先调用提取 tool 2. LLM 或本地脚本产出 `summary_result` 3. LLM 再调用 `filter_summary_result` 4. 后续根据过滤结果决定是否入库、推送或人工审核 这意味着: - LLM 是编排者 - rule engine 是裁决器 而不是让 LLM 在过滤阶段再次自由发挥。 ## 9. 调用示例 ### 9.1 本地脚本 ```bash python scripts/run_filter_rules.py ^ --summary outputs/reference/summary/result.loop.json ^ --extracted outputs/reference/extracted/read-flow-2026.extracted.json ^ --output outputs/reference/filter/filter-decision.json ``` ### 9.2 带上下文的调用 ```json { "interest_topics": ["个人信息管理", "阅读工作流"] } ``` 将这份上下文作为 `--context` 传入后,规则就可以根据当前关注主题做加权。 ### 9.3 MCP tool `filter_summary_result` 接收: - `summary_result` - 可选 `extracted_article` - 可选 `item` - 可选 `context` 返回: - `decision` - `matched_rules` - `reasons` - `labels` - `priority` - `matches` ## 10. 当前落地位置 - 过滤模型: - `src/summary_mcp/models/filtering.py` - 规则引擎: - `src/summary_mcp/filters/engine.py` - 默认规则: - `configs/filter_rules.json` - 本地运行脚本: - `scripts/run_filter_rules.py` - MCP tool: - `filter_summary_result` ## 11. 当前阶段的设计结论 第一版过滤层采用“规则主导,LLM 提供信号”的架构: - LLM 不直接做最终裁决 - 上层 Agent 可以决定是否调用过滤 tool - 真正的过滤决策由规则引擎输出 - 灰区内容后续再考虑是否引入第二层 LLM 辅助判断