Files
context-gatekeeper/DESIGN.md

8.5 KiB
Raw Permalink Blame History

上下文门控器 DESIGN.md

本文件描述 context-gatekeeper 的视觉语言、算法哲学与实现规范,供 AI 编码 Agent 在开发/重构时参考。 遵循 Google Stitch DESIGN.md 规范


1. Visual Theme and Atmosphere

工具型命令行项目,不是面向终端用户的可视化产品。

核心氛围是克制与精确——每个设计决策都有算法层面的理由,不做视觉上的"美化"。

代码即文档。输出即证据。接口即契约。


2. Design Language

审美取向

  • 风格工程师工具engineer-tool aesthetic。类比 htoptcpdump、Shellcheck
  • 信息密度优先:宁可多给一行调试输出,也不要静默失败
  • 零装饰原则:没有 emoji logo、没有渐变色、没有动画。结构即美学

排版规范

  • 代码:等宽字体('JetBrains Mono', 'Fira Code', monospace
  • 表格ASCII 风格 Pipe Table算法伪代码用 Markdown code block
  • 日志输出:单行简短,带时间戳或轮次编号

命名哲学

  • 变量名即类型注释:不用注释解释的东西,变量名就要说清楚
    • anchor_overlap_ratio, new_token_ratio
    • score, val, tmp
  • 函数名即行为描述extract_anchors() → 提取锚点,gate_topic() → 判断话题切换
  • 模块名即职责边界anchor.py 专司锚点提取,topic_gate.py 专司门控判断

3. Color PaletteCLI 输出配色)

CLI 输出不使用颜色时最安全。本节定义配色规范,供未来添加 --color 输出时参考。

Role Token Value Usage
正常输出 --text-normal #FFFFFF 日志、返回值
高亮/标题 --text-highlight #5EEAD4 锚点命中、话题切换提示
警告 --text-warn #FBBF24 Token 超限、召回率低
错误 --text-error #F87171 过滤异常、API 错误
Debug --text-debug #6B7280 内部状态、IDF 值
强调 --text-accent #A78BFA 核心指标(覆盖增益)

4. Algorithm Specification

本项目的核心是算法,不是 UI。实现必须严格遵循算法规范不得随意改参数。

4.1 锚点提取Anchor Extraction

职责:从文本中提取有检索区分度的词单元。

提取规则(优先级从高到低):

  1. 英文术语:完整单词,小写化(redisasynciopostgresql
  2. 代码标识符:变量名/函数名,提取连续字母数字串(v1.2.3v1
  3. 中文 n-gram2-gram + 3-gram分布式锁跨进程通信
  4. 引号短语:双引号/单引号内的完整短语

IDF 计算

  • 语料库:全局所有已处理的对话块
  • 平滑公式:log((N + 1) / (df + 1)) + 1
  • 低于 1.5 的 n-gram 不参与锚点计算(过高的 IDF 通常是噪声)

输出格式

# anchor.py
anchors: dict[str, float]  # {词单元: IDF值}

4.2 话题门控Topic Gate

职责:判断当前 query 是否属于新话题。

决策树

overlap = Σ IDF(t) for t ∈ A(q)∩A(T) / Σ IDF(t) for t ∈ A(q)
new_ratio = Σ IDF(t) for t ∈ A(q)\A(T) / Σ IDF(t) for t ∈ A(q)

overlap > 0.45  →  continue继续当前话题
overlap < 0.20 and new_ratio > 0.70  →  switch话题切换
has_deictic(q)  →  continue指代词强制继续
else             →  continue中间地带默认继续

指代词检测(必须硬编码,不得依赖规则引擎):

DEICTIC_PATTERNS = [
    r'^这个', r'^那个', r'^它', r'^这', r'^那',
    r'^继续', r'^然后呢', r'^还有呢',
]

4.3 话题切换时的内容词过滤

触发条件topic_gate 判定为 switch。

内容词识别

CONTENT_WORD_MIN_LEN = 4  # 中文词 >= 4字符
CONTENT_WORD_PATTERNS = [
    r'\b[a-z]{4,}\b',      # 英文术语 >= 4字母
    r'\bv?\d+\.\d+\.\d+\b', # 版本号
]

过滤逻辑

  • 候选块必须包含至少一个内容词,否则 score = 0.0(硬过滤)
  • 不走 IDF 阈值过滤IDF > 2.0 筛选出的是稀有字符 n-gram不是话题标识符

4.4 稀疏召回Sparse Recall

评分公式

score(b, q) = 1.5·lex(u_b,q) + 0.7·lex(a_b,q) + 1.0·exact(b,q) + 0.2·recency(b)
符号 定义 权重
lex(u_b,q) 用户轮次 BM25/IDF 与 Query 重叠度 1.5
lex(a_b,q) 助手轮次 BM25/IDF 与 Query 重叠度 0.7
exact(b,q) 完全匹配奖励(块含 Query 所有锚点) 1.0
recency(b) 新鲜度奖励:1 - (current_turn - b.turn_id) / window 0.2

召回数量上限top-20超出部分不参与后续选择。

4.5 最小覆盖选择Minimum Coverage Selection

贪心选择:每次选 gain = Σ IDF(t) for t ∈ cov(b)\covered(S) / cost(b)^α,直到:

  • 覆盖率达到 85%,或
  • Token 预算耗尽

参数α = 0.8(不得改动)

Token 估算len(text) * 1.5(与实际有 2-3 倍误差,保守估算)

4.6 句级裁剪Sentence-Level Pruning

输入:选中的 block可能含多句话

规则

  1. . \n 分割为句子
  2. 保留含 Query 锚点的句子,最多 3 句
  3. 助手侧即使不含锚点,也保留第一句(保证上下文衔接)
  4. 裁剪后的块 token 估算独立重新计算

5. API 接口规范

ContextGatekeeper(token_budget: int)

初始化。token_budget 为单次 prompt 的最大 token 数(不含系统 prompt

gate.add_turn(user_text: str, assistant_text: str) -> int

添加一轮对话。返回 turn_id(从 0 开始)。

gate.select(query: str) -> list[dict]

返回选中的上下文块列表。每项格式:

{
    'turn_id': int,
    'user': str,       # 裁剪后的用户轮次文本
    'assistant': str,  # 裁剪后的助手轮次文本
    'score': float,    # 该块的最终得分
    'covered_ids': list[int],  # 该块新增覆盖的锚点 turn_id
}

gate.build_prompt(query: str) -> str

返回可直接发给 LLM 的完整 prompt 字符串(不含系统 prompt


6. 模块边界Do's and Don'ts

允许

  • src/ 外层添加新模块(如 benchmark.py
  • 扩展 anchor.py 支持新的 n-gram 规则
  • 添加新的话题切换检测模式(需同时更新 DEICTIC_PATTERNS
  • 调整 token_budget 数值

禁止

  • anchor.py 里做话题门控判断(违反单一职责)
  • selector.py 里修改 α 参数(该参数经过实验验证)
  • BM25/IDF 替换为 embedding/reranker资源受限前提的核心约束
  • 在 CLI 输出中硬编码颜色Unix 管道场景下颜色会变成乱码)

7. Error Handling

场景 行为
add_turn() 时 token 估算 > budget 记录 warning不 abort
select() 时无候选块 返回空 list
锚点提取为空 fallback取 query 前 20 字符作为锚点
句级裁剪后块为空 跳过该块,继续选下一个

8. 测试规范

单元测试

  • 每个模块独立的 test_<module>.py
  • 锚点提取:输入固定文本,输出 IDF 值在已知范围内
  • 话题门控用预制的query, history, expected_decision三元组覆盖所有分支

集成测试

  • test_100rounds_v2.py4 话题 × 25 轮交替,验证零跨话题污染
  • 必须覆盖话题切换、内容词过滤、Token 预算耗尽、指代词强制继续

回归基准

  • 每次修改后必须通过 test_100rounds_v2.py
  • 对话轮次超过 100 时需重新评估 IDF 偏倚问题

9. Agent Prompt Guide给 AI 助手的参考)

快速参考

概念
锚点权重 用户侧 1.5 / 助手侧 0.7
覆盖停止阈值 85%
贪心参数 α 0.8
召回上限 top-20
Token 估算系数 ×1.5

常见任务提示词

"我想加一个 BM25 以外的召回策略" → → 检查 sparse.pylex() 函数,不要改动 score 公式的权重

"某个话题老被误过滤" → → 先查话题切换时的 content_word 过滤逻辑,确认过滤条件是否过严

"我想试试不同的 overlap 阈值" → → 修改 topic_gate.pyOVERLAP_CONTINUE_THRESHOLD,然后跑 test_100rounds_v2.py 验证零污染

"Token 预算想从 4000 改成 8000" → → 改 ContextGatekeeper(token_budget=8000),检查 test_100rounds_v2.py 中对应的 BUDGET 常量