Extract Pruning
面向对话记忆的本地消息规整与摘要组件。 接收 User / Assistant 消息,返回固定五字段 JSON,用于记忆写入预处理、内容归档和后续检索。
项目采用 BERT 门控 + 一份 Qwen3.5-2B 基座 + 两个 LoRA 适配器,提供 Python 接口、单样本 JSON 推理和支持断点续跑的批量 JSONL 推理。权重准备完成后,推理不依赖在线大模型 API。
1. 项目背景与功能
对话消息中经常混有用户请求、复制的文章、业务 JSON、客服对话和文件摘要。直接把全部内容存入记忆,会增加存储和检索负担;简单截断又可能丢失关键请求、数字、条件和人物关系。Assistant 的长回复也需要转换成适合后续使用的摘要。
本项目分别处理两侧消息:
- User 侧:判断是否需要规整;需要时生成融合后的短文本,并提取被处理对象的主题或实体名称。
- Assistant 侧:将非空回复概括为动作摘要,同时输出一个主要回应类型。
- 统一接口:把两个模型任务的结果合并为五个固定字段,供下游业务使用。
典型用途包括对话记忆写入预处理、摘要归档、按回应动作组织内容,以及生成向量检索所需的主题锚点。项目提供预处理结果,不包含数据库写入、向量索引、记忆去重或检索服务。
should_process_user_msg=false 表示无需进行本项目的 User 规整,不表示应删除该消息;调用方可以按业务规则保留原文。
2. 输入与输出
2.1 输入结构
输入是一个仅包含 msgs 的 JSON 对象:
{
"msgs": [
{"role": "User", "msg": "用户消息"},
{"role": "Assistant", "msg": "助手回复"}
]
}
| 字段 | 类型 | 约束 |
|---|---|---|
msgs |
array | 零至两条消息,每种角色最多一条 |
role |
string | 只接受 User、Assistant,大小写不敏感,顺序不限 |
msg |
string / null | null、空字符串和纯空白均视为空 |
缺失的一侧按空消息处理。重复角色、未知角色、额外字段或非字符串正文会被拒绝。多个历史轮次需由调用方先组织成当前请求,组件不会自动猜测配对关系。
路由只依据外层 role。 正文中的“用户:”“客服:”或嵌套对话标签是待处理内容,不会成为额外请求。
文件内容需由上游转换成文本摘要,再放入 <input-file-summary>...</input-file-summary> 等文本包装。本接口不直接读取图片、视频、PDF或文件路径。
2.2 五个输出字段
成功返回时始终包含五个字段,空值为真正的 JSON null,不是字符串 "null":
| 字段 | 类型 | 含义 |
|---|---|---|
assistant_memory_hint |
string / null | Assistant 动作摘要,目标是保留具体对象、问题、选项和条件 |
assistant_memory_type |
string / null | Assistant 的主要回应类型,取九类之一 |
should_process_user_msg |
boolean / null | 是否需要生成 User 规整结果;User 为空时为 null |
processed_user_msg |
string / null | User 规整文本,目标是保留真实表达、概括外部材料并区分来源 |
processed_user_topic_entity_hint |
string / null | 本次被处理对象的主题/实体名称,如“西红柿炒鸡蛋食谱” |
主题锚点是一个简短名称,不是实体列表或向量。后续 embedding 和数据库连接由调用方完成。
2.3 Assistant 九分类
| 类别 | 含义 |
|---|---|
comfort |
安慰、情绪支持 |
suggestion |
一般行动建议 |
recommendation |
推荐具体方案、产品或对象 |
warning |
风险、限制或后果提醒 |
instruction |
操作步骤、执行方法 |
question |
询问、追问或澄清 |
agreement |
认同或附和 |
repetition |
主要重复用户已有内容 |
other |
其他有效回应 |
每条回复只选择一个主要类型,摘要可以保留多个动作。类别由模型结合语义生成,不按问号或关键词直接分类。
2.4 完整示例
以下是项目端到端自测中的一个双侧请求。
输入:
{
"msgs": [
{
"role": "User",
"msg": "请记住这个食谱。<input-file-summary>西红柿炒鸡蛋:3个鸡蛋、2个西红柿,先炒蛋盛出,再炒西红柿,最后混合。</input-file-summary>"
},
{"role": "Assistant", "msg": "请问您需要修改文章,还是总结文章?"}
]
}
实际输出:
{
"assistant_memory_hint": "询问用户是否需要修改文章或总结文章。",
"assistant_memory_type": "question",
"should_process_user_msg": true,
"processed_user_msg": "我说了请记住这个食谱。我复制了一段关于西红柿炒鸡蛋的食谱,包含3个鸡蛋、2个西红柿,以及先炒蛋盛出、再炒西红柿、最后混合的步骤。",
"processed_user_topic_entity_hint": "西红柿炒鸡蛋食谱"
}
此例也展示了两侧独立性:Assistant 的文章处理问题不会写入 User 食谱摘要。示例是实际模型输出,不是语义正确性的保证;具体措辞可能随运行环境变化。
仅有User且门控不通过时,输出形态为:
{
"assistant_memory_hint": null,
"assistant_memory_type": null,
"should_process_user_msg": false,
"processed_user_msg": null,
"processed_user_topic_entity_hint": null
}
2.5 空值与失败约定
| 请求状态 | Assistant 两字段 | User 门控 | User 另外两字段 |
|---|---|---|---|
| 两侧均空 | null | null | null |
| 仅 Assistant 非空 | 模型生成 | null | null |
| 仅 User 非空、门控 false | null | false | null |
| 仅 User 非空、门控 true | null | true | 模型生成 |
| 两侧非空 | 模型生成 | true 或 false | 根据门控生成或置 null |
生成失败不是空值场景。 格式错误、非法类别、空生成字段或长度异常会报错,不会返回伪造的五字段成功结果。
3. 数据处理流程
flowchart TD
A[消息输入] --> B[校验与分流]
B --> C{User 非空?}
B --> D{Assistant 非空?}
C -->|否| E[User 三字段置 null]
C -->|是| F[规则 / BERT]
F --> G{"should_process_user_msg"}
G -->|false| H[User 两生成字段置 null]
G -->|true| I[User LoRA]
I --> UO["processed_user_msg<br/>processed_user_topic_entity_hint"]
D -->|否| J[Assistant 两字段置 null]
D -->|是| K[Assistant LoRA]
K --> AO["assistant_memory_hint<br/>assistant_memory_type"]
E --> L[合并五字段]
H --> L
UO --> L
J --> L
AO --> L
L --> M[输出 JSON]
图中的分支表示业务逻辑,不表示GPU并行执行。实现先计算User门控,再按需执行Assistant和User生成。同一请求最多调用两次Qwen;门控为false时省去User生成。
完整配对的 <input-file-summary>...</input-file-summary> 按规则直接通过门控;标签损坏等情况交给BERT判断。此规则只识别文本包装形式,不验证摘要内容的真实性。
4. 模型结构
4.1 组件与共享方式
flowchart LR
U[User 原文] --> G[BERT + 特征分类头]
G --> T["should_process_user_msg"]
T -->|通过| UP[User 提示词与输入处理]
A[Assistant 原文 + 可用 User 语境] --> AP[Assistant 提示词]
UP --> R[选择任务适配器]
AP --> R
Q[共用 Qwen3.5-2B 基座] --> R
R --> UL[激活 User LoRA<br/>rank 16]
R --> AL[激活 Assistant LoRA<br/>rank 8]
UL --> UO["processed_user_topic_entity_hint<br/>processed_user_msg"]
AL --> AO["assistant_memory_hint<br/>assistant_memory_type"]
| 组件 | 作用 | 权重目录 |
|---|---|---|
| BERT 门控 | 判断非空User是否需要规整 | model/bert/ |
| Qwen3.5-2B | 两侧共用的语义生成基座 | model/qwen_base/ |
| Assistant LoRA | 生成Assistant摘要和类型 | model/assistant_lora/ |
| User LoRA | 生成User规整文本和对象锚点 | model/user_lora/ |
同一个 ExtractPruning 实例只加载一份Qwen基座,同时挂载两个适配器,每次生成只激活其中一个。LoRA是模型内部线性层的参数增量,不是仅接在末端的分类头。本实现不把两个LoRA叠加合并。
4.2 BERT 门控网络
门控使用中文BERT编码器与文本结构特征联合二分类。随附配置为12层、隐藏维度768、12个注意力头、最大位置长度512。
flowchart LR
X[User 文本] --> TOK[分词与长度处理]
TOK --> BERT[12层 BERT 编码器]
BERT --> CLS[CLS 隐状态<br/>768维]
X --> FEAT[结构与词法特征<br/>9维]
CLS --> CAT[拼接为777维]
FEAT --> CAT
CAT --> L1[Linear 777 → 128]
L1 --> ACT[ReLU]
ACT --> DROP[Dropout 0.1<br/>推理时关闭]
DROP --> L2[Linear 128 → 1]
L2 --> SIG[Sigmoid]
SIG --> DEC[概率 ≥ threshold]
DEC --> OUT["should_process_user_msg"]
九维特征分别为:代码块数量、表格行数量、引用行数量、URL数量、处理意图词得分、内容边界词数量、第一人称占比、段落数量、文本长度。计数按实现中的上限归一化。
普通消息采用 sigmoid(logit) >= threshold 判为true,默认阈值0.5。输入超过编码容量时,保留头部和尾部,并对中间token均匀采样;结构特征仍从完整原文提取。这种编码不等于完整长文理解。
best.safetensors 包含微调后的编码器和分类头完整权重。tokenizer/ 提供分词器及BERT架构配置,无需再加载一份BERT基座权重。配置文件中保留的 BertForMaskedLM 元数据不代表实际使用掩码语言模型输出头:运行代码使用 AutoModel 编码器与自定义二分类头。
4.3 Qwen 基座与 LoRA 结构
随附Qwen3.5-2B的文本配置为24层、隐藏维度2048、MLP中间维度6144。其层类型由18个线性注意力层和6个全注意力层组成,每4层包含一个全注意力层;全注意力配置包含8个查询头和2个键值头。
项目通过 Qwen3_5ForConditionalGeneration 加载完整基座,但当前仅进行文本推理。基座包含视觉组件不意味着本接口已经提供图像或视频处理能力。
LoRA向线性变换加入低秩增量:
有效权重 = 冻结基座权重 W + (alpha / rank) × B × A
两个适配器作用于语言模型的注意力投影和MLP投影,共186个目标模块,不向视觉组件添加LoRA。
| 适配器 | rank | alpha | LoRA参数量 | 同次生成字段 |
|---|---|---|---|---|
| Assistant | 8 | 16 | 8,409,600 | 动作摘要、主要动作类型 |
| User | 16 | 32 | 16,819,200 | 主题实体锚点、规整文本 |
推理时所有参数冻结,使用非思考聊天模板和 do_sample=false。共享基座减少重复加载的权重内存,但仍需分别计算两侧任务。
4.4 任务输入与内部生成格式
Assistant路径:把两个消息槽序列化为JSON,与Assistant系统提示词一起输入模型。User用于必要的语境理解。模型内部生成hint/type两字段JSON,解析器检查非空摘要及合法类别。
User路径:默认只使用User正文,不附加Assistant。预处理调整字面转义和空白;超过512 token时按保头75%、保尾25%的预算策略截断,并加入中间截断标记。标记也占用预算,重新分词后可能存在少量长度差异。
User内部输出为:
主题实体锚点<|field_sep|>规整文本
解析器要求恰好一个分隔符且两侧非空,然后转换成外部JSON。业务方无需自行解析此分隔符。
5. 项目目录与权重准备
extract_pruning/
├── model/
│ ├── bert/
│ │ ├── best.safetensors
│ │ ├── config.json
│ │ └── tokenizer/
│ ├── qwen_base/ # 完整基座、分词器、模型配置
│ ├── assistant_lora/ # adapter_config.json + adapter_model.safetensors
│ └── user_lora/ # adapter_config.json + adapter_model.safetensors
├── prompts/
│ ├── assistant_prompt.txt
│ └── user_prompt.txt
├── config.json
├── pipeline.py # Python复用接口
├── predict.py # 单JSON入口
├── batch_predict.py # JSONL批量入口
├── gate_runtime.py # 门控网络、特征与编码
├── user_runtime.py # User输入处理和生成解析
├── sample_oss.py # 可选快照抽样工具
├── input_sample.json
├── output_sample.json
├── input_batch.jsonl
├── output_batch.jsonl
├── test_pipeline.py
├── test_batch.py
├── smoke_test.py
├── model_manifest.json
├── requirements.txt
└── README.md
运行前需取得匹配的基座、BERT、两个LoRA及提示词,并按上述目录放置。代码与权重包可以分开分发,克隆代码不代表已经取得权重。 本文档不提供尚未发布的下载地址。当前完整随附文件约5.08 GB(十进制),主要为基座权重。
模型和提示词路径相对 config.json 所在目录解析,也可配置部署环境中的绝对路径。适配器配置里的历史 base_model_name_or_path 是元数据,程序显式加载config指定的基座,不访问历史服务器。
模型加载使用 local_files_only=True,不会联网补齐缺失文件。正常推理不依赖OSS、训练数据、优化器状态或原训练目录。
6. 安装与使用
6.1 环境准备
使用Python 3.10或更新的兼容版本。已验证主要依赖组合:PyTorch 2.9.0、Transformers 5.15.0、PEFT 0.20.0、Accelerate 1.14.0。
在项目目录创建虚拟环境:
python -m venv .venv
Linux / macOS激活:
source .venv/bin/activate
Windows PowerShell激活:
.venv\Scripts\Activate.ps1
先按设备安装合适的PyTorch构建,再安装其余依赖:
# 需要CUDA时,应安装与GPU驱动兼容的PyTorch CUDA构建。
# 已有适用的PyTorch环境时无需重复安装。
python -m pip install torch==2.9.0
python -m pip install -r requirements.txt
通用pip命令是否提供所需CUDA构建取决于平台和软件源。运行GPU推理前确认CUDA可用:
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"
Qwen默认优先使用CUDA;不可用时使用CPU float32,速度更慢且主内存需求更大。auto 不会自动选择MPS或多卡分片。BERT默认使用CPU。显存需求随输入长度、输出长度和精度变化,不保证任意输入都能在固定显存容量内运行。
6.2 单样本推理
python predict.py --input input_sample.json --output result.json
需要独立诊断信息时:
python predict.py --input input_sample.json --output result.json --diagnostics diagnostics.json
- 输入文件支持UTF-8及UTF-8 BOM。
- 省略
--input时从stdin读取一个JSON对象。 - 省略
--output时业务JSON写入stdout,日志与错误写入stderr。 - 诊断文件记录门控概率/规则路径、token数量和截断信息,不混入业务JSON。
- 成功退出码0,失败1;失败不写新的成功结果。若目标文件此前存在,它仍是旧结果,应以退出码判断本次是否成功。
6.3 Python 应用集成
在项目目录运行,或将项目目录加入应用模块搜索路径:
from pipeline import ExtractPruning
engine = ExtractPruning("config.json")
result = engine.predict({
"msgs": [
{"role": "User", "msg": "用户消息"},
{"role": "Assistant", "msg": "助手回复"},
]
})
# 复用同一实例处理后续请求,可选择返回独立诊断信息。
result, diagnostics = engine.predict_with_diagnostics({
"msgs": [{"role": "User", "msg": "另一条用户消息"}]
})
应复用同一个engine。模型按需延迟加载,第一次调用包含加载开销。实例内部用请求锁保护适配器切换,因此同一实例串行处理请求。多个实例或进程各自加载模型,不能视为共享一份显存。
当前提供Python组件和命令行工具,未内置HTTP服务、GPU动态批处理或分布式调度。
6.4 批量 JSONL 推理
输入文件每行一个完整请求,不是外层JSON数组:
{"msgs":[{"role":"User","msg":"你好"},{"role":"Assistant","msg":""}]}
{"msgs":[{"role":"User","msg":""},{"role":"Assistant","msg":"请问您希望修改文章还是总结文章?"}]}
python batch_predict.py --input input_batch.jsonl --output batch_results.jsonl
输出第N行对应输入第N行,每行只含五个业务字段。批量入口复用一个模型实例、按条串行生成;文件批量处理不等于GPU张量批处理。
| 文件 | 内容 |
|---|---|
batch_results.jsonl |
逐行五字段结果 |
batch_results.run.json |
状态、完成行数、输入/代码/模型指纹 |
batch_results.diagnostics.jsonl |
每行耗时、门控及生成诊断 |
空白行、非法JSON或错误结构在推理前报错。每完成一条立即刷新输出并打印进度。模型异常时停在失败行,不跳过样本,不输出伪造的null成功结果。
断点续跑:
python batch_predict.py --input input_batch.jsonl --output batch_results.jsonl --resume
恢复时检查输入、配置、代码、提示词和模型是否变化。已有输出默认禁止覆盖;独立重跑请更换输出路径。status=complete 且输入输出行数一致,表示整批完成。
6.5 可选:从自有 OSS 快照抽样
核心入口接受任何符合接口规范的JSONL,不要求来自OSS。抽样工具仅适用于本项目快照结构:
python sample_oss.py --source /path/to/oss_data.jsonl --output sampled_inputs.jsonl --count 100 --seed 42
工具读取外层payload数组中的 source=llm、type=user_pruning/assistant_pruning 记录,只提取input。完整输入去重后随机抽样,再按源位置排序;不会复制监督标签或跨记录配对消息。来源行号保存在 .sources.json。
7. 配置说明
配置入口为 config.json。
| 参数 | 默认值 | 作用 |
|---|---|---|
base_model |
model/qwen_base |
共用基座路径 |
runtime.device |
auto |
Qwen设备,可明确设置cpu/cuda |
runtime.dtype |
bfloat16 |
CUDA精度;CPU实际使用float32 |
runtime.attention_implementation |
sdpa |
注意力实现 |
gate.weights |
model/bert/best.safetensors |
门控权重 |
gate.device |
cpu |
门控设备 |
gate.threshold |
0.5 |
放行阈值,范围0~1 |
gate.max_length |
512 |
BERT编码长度,范围8~512 |
assistant.adapter / user.adapter |
对应model子目录 | 两侧适配器路径 |
assistant.prompt / user.prompt |
对应prompts文件 | 两侧提示词 |
assistant.max_seq_length |
6144 |
输入加生成预留预算的上限 |
assistant.max_new_tokens |
512 |
Assistant最多生成token数 |
user.max_seq_length |
3072 |
输入加生成预留预算的上限 |
user.max_new_tokens |
384 |
User最多生成token数 |
user.input.budget |
512 |
默认User正文预算 |
user.input.head_ratio |
0.75 |
超长User可用保留预算中头部比例 |
user.input.use_assistant_note |
false |
是否附加Assistant尾注 |
user.input.assistant_note_budget |
128 |
开启尾注时的尾注预算 |
user.input.user_budget_with_note |
384 |
开启尾注时的User预算 |
降低门控阈值会增加或保持放行数量,但不保证提升整体质量。修改提示词、输入预算和尾注开关后,也应重新验证语义质量及资源需求。LoRA的rank/alpha是权重结构的一部分,不能通过修改推理配置把rank=8权重变成rank=16权重。
8. 测试与已验证行为
不加载模型的接口测试:
python test_pipeline.py
python test_batch.py
真实权重自测:
python smoke_test.py
自测重新生成 output_sample.json、smoke_test_results.json,覆盖空输入、短User、Assistant-only、文件摘要规则、双侧生成及适配器切换一致性。
随附100条批量功能测试已完成,来源映射、字段结构与路由空值规则均通过检查。样本覆盖User-only 79条、Assistant-only 12条、双侧9条;User门控通过11条、未通过77条、User为空12条,Assistant生成21条。
这些结果证明端到端接口可运行,不构成独立泛化准确率或跨硬件性能基准。快照样本可能与训练数据重合。结果核验见 batch_verification.json 和 smoke_test_results.json,权重复制校验见 model_manifest.json。
9. 已知边界
- 语义保真需要验收:格式合法、语句通顺不保证数字、条件、指代与来源归属全部正确;字面相似度也不是语义准确率。
- 长输入存在信息损失:User采用中间截断,BERT采用采样编码。Assistant超过输入加输出预算时直接报错。
- 清洗可能改变代码或转义文本:User预处理会调整空白、代码缩进和字面转义,代码类内容需单独验收。
- 语言与领域覆盖有限:门控和LoRA在新语言、新业务上的效果需要单独验证,文件摘要规则通过不等于内容理解正确。
- 错误明确暴露:解析失败、非法类别、空字段或达到生成上限会报错,当前没有自动重试或远程API回退。
- 共享基座不等于并行服务:单实例串行处理,未实现多GPU切分或多请求动态批处理。
接入业务时应保留原始消息,把五字段作为派生结果使用,并依据实际用途验收关键事实和主题对象的保留情况。