docs: 添加 SPEC.md 规格文档
This commit is contained in:
143
SPEC.md
Normal file
143
SPEC.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# 上下文门控器 - SPEC.md
|
||||||
|
|
||||||
|
## 1. 项目概述
|
||||||
|
|
||||||
|
**项目名称:** context-gatekeeper
|
||||||
|
**功能:** 轻量级上下文选择器,在同一会话中自动从历史对话里选出最小且相关的片段,减少话题污染和控制上下文长度。
|
||||||
|
**约束:** 纯 Python,无额外模型依赖,2 核 2G 可运行。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 核心架构
|
||||||
|
|
||||||
|
### 四步流程
|
||||||
|
|
||||||
|
```
|
||||||
|
用户输入 → 锚点提取 → 话题门控 → 稀疏召回 → 最小覆盖选择 → 组装 Prompt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据结构
|
||||||
|
|
||||||
|
```python
|
||||||
|
Block:
|
||||||
|
- user_text: str # 用户消息
|
||||||
|
- assistant_text: str # 助手回复
|
||||||
|
- tokens_user: int # 用户消息 token 数(估算)
|
||||||
|
- tokens_assistant: int # 助手消息 token 数(估算)
|
||||||
|
- anchors: list[str] # 锚点列表(2-gram/3-gram)
|
||||||
|
- turn_id: int # 对话轮次
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 功能模块
|
||||||
|
|
||||||
|
### 3.1 锚点提取 (Anchor Extraction)
|
||||||
|
|
||||||
|
- 中文: 2-gram / 3-gram 连续字符串
|
||||||
|
- 英文: 单词
|
||||||
|
- 代码标识符、版本号、引号短语、反引号内代码
|
||||||
|
- 计算 IDF 权重,高频通用词自动降权
|
||||||
|
|
||||||
|
### 3.2 话题门控 (Topic Gate)
|
||||||
|
|
||||||
|
输入: 当前 query 锚点集合 + 活跃话题锚点集合
|
||||||
|
输出: `continue` | `switch`
|
||||||
|
|
||||||
|
规则:
|
||||||
|
- `overlap > 0.45`: 继续当前话题
|
||||||
|
- `overlap < 0.20` 且 `new_ratio > 0.70`: 切换新话题
|
||||||
|
- 有指代词(这个/那个/它/上面/刚才/继续/展开)→ 强暗示继续
|
||||||
|
|
||||||
|
### 3.3 稀疏召回 (Sparse Retrieval)
|
||||||
|
|
||||||
|
评分函数:
|
||||||
|
```
|
||||||
|
score(block, query) = 1.5 * lex(user_text, query)
|
||||||
|
+ 0.7 * lex(assistant_text, query)
|
||||||
|
+ 1.0 * exact_match(query, block)
|
||||||
|
+ 0.2 * recency
|
||||||
|
```
|
||||||
|
|
||||||
|
- `lex`: BM25/IDF overlap,简单实现
|
||||||
|
- `exact_match`: 英文术语、代码标识符、数字版本号、引号短语完整命中加分
|
||||||
|
- `recency`: 弱先验,仅做微调
|
||||||
|
|
||||||
|
候选集: top 20 blocks
|
||||||
|
|
||||||
|
### 3.4 最小覆盖选择 (Minimum Coverage Selection)
|
||||||
|
|
||||||
|
贪心算法:
|
||||||
|
```
|
||||||
|
每步选"单位长度收益最大"的 block:
|
||||||
|
gain = covered_anchor_idf / cost^alpha
|
||||||
|
|
||||||
|
停止条件:
|
||||||
|
- 覆盖率达到 85% (η=0.85)
|
||||||
|
- 或达到 token 预算上限
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.5 句级裁剪 (Intra-Block Trimming)
|
||||||
|
|
||||||
|
选中 block 后,进一步裁剪:
|
||||||
|
- 保留覆盖了 query anchors 的句子
|
||||||
|
- 保留 exact match 的句子
|
||||||
|
- 连带保留对应用户问题句
|
||||||
|
|
||||||
|
### 3.6 常量区 (Stable Constraints)
|
||||||
|
|
||||||
|
维护一个小的稳定约束区:
|
||||||
|
- 用户显式声明的长期偏好
|
||||||
|
- 输出语言、风格、禁用项
|
||||||
|
|
||||||
|
不是记忆系统,只是一个固定 KV。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 对外接口
|
||||||
|
|
||||||
|
```python
|
||||||
|
class ContextGatekeeper:
|
||||||
|
def __init__(self, token_budget: int = 4000)
|
||||||
|
|
||||||
|
def add_turn(self, user_text: str, assistant_text: str) -> None
|
||||||
|
"""添加一轮对话到历史"""
|
||||||
|
|
||||||
|
def select(self, query: str) -> list[dict]:
|
||||||
|
"""为当前 query 选择上下文 blocks"""
|
||||||
|
# 返回: [{"user": ..., "assistant": ..., "turn_id": ...}, ...]
|
||||||
|
|
||||||
|
def set_constraint(self, key: str, value: str) -> None
|
||||||
|
"""设置稳定约束"""
|
||||||
|
|
||||||
|
def get_constraints(self) -> dict:
|
||||||
|
"""获取当前所有约束"""
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 测试验证
|
||||||
|
|
||||||
|
使用 MiniMax API 做一个端到端对话测试:
|
||||||
|
- 模拟多轮对话(至少 3 轮,其中包含话题切换)
|
||||||
|
- 验证:
|
||||||
|
1. 同一话题内,后续问题能召回前面对话
|
||||||
|
2. 切换话题后,旧话题内容不会被召回
|
||||||
|
3. 有指代词时,强制继承最近 1-2 个 block
|
||||||
|
4. token 预算控制正常
|
||||||
|
|
||||||
|
.env 文件格式:
|
||||||
|
```
|
||||||
|
MINIMAX_API_KEY=你的key
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 验收标准
|
||||||
|
|
||||||
|
- [ ] 纯 Python,无第三方模型依赖
|
||||||
|
- [ ] 可在 2 核 2G 环境运行
|
||||||
|
- [ ] 单元测试覆盖核心模块
|
||||||
|
- [ ] 端到端对话测试通过
|
||||||
|
- [ ] .env 正确 gitignore
|
||||||
|
- [ ] 代码上传 Gitea 仓库
|
||||||
Reference in New Issue
Block a user