Files
reader/docs/design/filter-rule-engine-design.md
T

5.1 KiB
Raw Blame History

规则过滤引擎设计

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

单条规则结构为:

{
  "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. 动态上下文字段

规则支持从其他字段动态取值,例如:

{ "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 本地脚本

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 带上下文的调用

{
  "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 辅助判断