Guide · 使用教程
LLMLingua Python 教程:压缩预算、关键信息保留与效果校验
固定模型快照与 CPU 环境,检查中英文四组压缩输出、完整输入预算和关键短语,保留全部拦截结果及十一项本地验证。
本教程会保留失败,而不追求全部通过
用 LLMLingua-2 压缩两个合成中英文文本,各测试 rate=0.5 和 0.8,再核对精确短语与完整输入预算。四组实际输出均被保守门槛拦下,其中包含关键禁令变化、编号格式变化和中文 token 增加。程序保存报告,不调用下游回答模型。
固定环境为 Python 3.11、LLMLingua 0.2.2、PyTorch 2.2.2、Transformers 4.44.2、Accelerate 0.34.2、NumPy 1.26.4 和 tiktoken 0.14.0,在本次 Intel macOS CPU 环境运行。这是复现条件,不代表所有平台都应使用相同版本,部署前应重新核对兼容性与依赖。
第一步:安装独立环境
python3.11 -m venv .venv
source .venv/bin/activate
python -m pip install 'llmlingua==0.2.2' 'torch==2.2.2' 'numpy==1.26.4' 'transformers==4.44.2' 'accelerate==0.34.2' 'tiktoken==0.14.0'
python -m pip check
LLMLingua 是项目系列,下面明确启用 use_llmlingua2。不要把旧版默认 LLaMA 路径、LongLLMLingua 或其他方法的参数混进本示例,也不要把公开论文的最大压缩倍数当作本次结果。
第二步:准备固定模型与编码资源
保存为 prepare_model.py,在联网环境运行。本次使用官方多语言 BERT MeetingBank 权重,模型卡标注 Apache-2.0,库自身为 MIT。两者分别核对,不能将库许可套用到所有权重。
from huggingface_hub import snapshot_download
import tiktoken
snapshot_download(
'microsoft/llmlingua-2-bert-base-multilingual-cased-meetingbank',
revision='5f0c82792b7ea14c6484e015b6a072009496b7f2',
local_dir='./llmlingua-2-bert-base-multilingual-cased-meetingbank',
allow_patterns=['*.json', '*.txt', '*.safetensors'],
)
tiktoken.get_encoding('cl100k_base')
export HF_HOME="$PWD/.hf-cache"
export TIKTOKEN_CACHE_DIR="$PWD/.tiktoken-cache"
python prepare_model.py
这里有一个实际踩到的版本细节:0.2.2 的 LLMLingua-2 辅助逻辑根据 model_name 中的特定名称判断分词方式。本地目录路径需要保留 bert-base-multilingual-cased 这一段;改成泛化目录名会触发 NotImplementedError。本示例保留完整名称,没有修改安装包源码。
本路径不调用 NLTK 句子切分功能,因此不需要为了示例额外下载 punkt。严格离线前先准备实际所需模型、包与 tiktoken 缓存。离线环境变量限制库访问方式,不是操作系统网络沙箱;本次未关闭证书验证。
第三步:保存完整压缩与校验程序
将下面代码保存为 compress_validate.py。示例每种语言只有一段短文本,使用 CPU 与两线程,避免把 GPU 默认值直接用于当前机器。required 是保守的精确短语,不把编号中的 7 错当成“七天期限”已经保留。
import argparse,hashlib,json,time
from pathlib import Path
from importlib.metadata import version
import torch,tiktoken
from llmlingua import PromptCompressor
CASES=[
{'id':'en','text':'Policy REFUND-731. Customers may request a refund within 7 days. The fee is USD 19. Do not share passwords. We discussed the policy during a long friendly meeting and repeated the same background several times. The seven-day limit is mandatory, not thirty days.','required':['REFUND-731','within 7 days','USD 19','Do not share passwords']},
{'id':'zh','text':'政策编号 REFUND-731。购买后七天内可以申请退款,手续费为人民币19元。不得分享密码。会议中大家反复介绍背景和问候语,但这些寒暄不是规则。退款期限是七天,不是三十天。','required':['REFUND-731','七天','人民币19元','不得分享密码']},
]
QUESTION='What are the refund deadline, fee, and password restriction?'
def validate_input(text,rate):
if not text.strip():raise ValueError('empty text')
if not 0<rate<=1:raise ValueError('rate must be within (0,1]')
def assess(text,required,budget,encoding):
missing=[x for x in required if x not in text]
count=len(encoding.encode(text))
return dict(payload_tokens=count,budget=budget,missing=missing,
status='ready_for_review' if not missing and count<=budget else 'blocked')
def main():
p=argparse.ArgumentParser();p.add_argument('model_directory',type=Path);args=p.parse_args()
torch.set_num_threads(2)
enc=tiktoken.get_encoding('cl100k_base')
compressor=PromptCompressor(model_name=str(args.model_directory),device_map='cpu',
model_config={'torch_dtype':torch.float32},use_llmlingua2=True)
rows=[]
for case in CASES:
for rate in [0.5,0.8]:
validate_input(case['text'],rate);started=time.perf_counter()
result=compressor.compress_prompt_llmlingua2([case['text']],rate=rate)
compressed=result['compressed_prompt']
# Count the assembled downstream plain-text payload, not just compressed context.
payload='Answer only from evidence.\nEvidence:\n'+compressed+'\nQuestion:\n'+QUESTION
rows.append(dict(id=case['id'],rate=rate,compressed=compressed,
reported_origin_tokens=result['origin_tokens'],reported_compressed_tokens=result['compressed_tokens'],
elapsed_ms=round((time.perf_counter()-started)*1000,2),
assessment=assess(payload,case['required'],140,enc)))
output=dict(versions={n:version(n) for n in ['llmlingua','torch','transformers','tiktoken']},
model_sha256=hashlib.sha256((args.model_directory/'model.safetensors').read_bytes()).hexdigest(),
cases=CASES,rows=rows,note='No downstream model invoked. Substring presence is only a necessary check, not semantic faithfulness.')
Path('compression-output.json').write_text(json.dumps(output,ensure_ascii=False,indent=2));print('Saved compression-output.json')
if __name__=='__main__':main()
第四步:运行并保留完整报告
HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 python compress_validate.py ./llmlingua-2-bert-base-multilingual-cased-meetingbank
输出 compression-output.json 保存输入、required、模型权重 SHA-256、包版本、压缩文本、内部计数、组装后计数和拒绝原因。摘要便于检查模型变化,不是来源可信或授权的充分证明。真实资料和压缩日志都应受控保存。
这里的完整输入是示例将指令、证据和问题拼成的纯文本,不包含任何具体聊天 API 封装、工具 schema、多模态输入或输出预留。cl100k_base 只是本次明确选择的计数口径,不代表所有模型的真实账单。
第五步:核对四组实际结果
| 样本 | rate | 原文 tokens | 压缩文本 tokens | 组装输入 tokens | 检查 |
|---|---|---|---|---|---|
| 英文 | 0.5 | 56 | 29 | 51 | blocked |
| 英文 | 0.8 | 56 | 46 | 68 | blocked |
| 中文 | 0.5 | 87 | 82 | 104 | blocked |
| 中文 | 0.8 | 87 | 124 | 146 | blocked,超过预算 140 |
英文 0.5 的原始禁令 Do not share passwords 没有完整保留,输出出现 share passwords。需要阻止将这种压缩结果直接交给回答模型。编号还变成 REFUND - 731,精确字段规则因此拒绝;这一项可能是格式变化,不能单凭不匹配断言编号含义已经丢失。
中文输出在字之间插入空格,七天等精确短语无法直接匹配。0.8 配置的压缩文本 token 甚至超过原文,完整输入又多于压缩文本。说明 rate 不是实际 token 保留率,也不是成本或长度的硬上限;压缩模型分词、输出形式和最终计数口径会影响观察。
四条 blocked 不等于四条都已证实语义错误。门槛将预算与严格格式问题挡下,后续仍需检查事实和关系。不要为了让报告好看而静默去空格、去符号或删除 required;如果需要规范化,应先定义规则,再用独立案例验证不会误改事实。
第六步:验证门槛会拒绝坏结果
保存下列代码为 check_report.py,与主程序放在同一目录。它重新计算报告,并用合成控制检查超预算、缺失短语与可进入复核的情况。
import json
from pathlib import Path
import tiktoken
from compress_validate import CASES, QUESTION, assess, validate_input
out = json.loads(Path('compression-output.json').read_text())
encoding = tiktoken.get_encoding('cl100k_base')
assert len(out['rows']) == 4 and out['model_sha256']
for row in out['rows']:
case = next(c for c in CASES if c['id'] == row['id'])
payload = 'Answer only from evidence.\nEvidence:\n' + row['compressed'] + '\nQuestion:\n' + QUESTION
assert row['assessment'] == assess(payload, case['required'], 140, encoding)
assert row['assessment']['status'] == 'blocked'
for text, rate in [('', 0.5), ('x', 0), ('x', 1.1)]:
try:
validate_input(text, rate)
except ValueError:
continue
raise AssertionError('invalid input accepted')
assert assess('keep KEY', ['KEY'], 0, encoding)['status'] == 'blocked'
assert assess('text', ['MISSING'], 140, encoding)['status'] == 'blocked'
assert assess('keep KEY', ['KEY'], 140, encoding)['status'] == 'ready_for_review'
print('Report and refusal controls passed; semantic review is still required.')
python check_report.py
本次共十一项本地检查:四条真实输出回读、四条结果与模型摘要的结构检查、三个非法输入拒绝,以及超预算、缺失标记、正向复核三个控制。ready_for_review 只是可进入复核,不是可以自动发送或答案正确的认证。
怎样扩展成自己的验收
先为目标任务准备原文、问题、答案和必要证据,覆盖数字、否定、日期、来源与领域实体。精确字段检查只是必要条件,出现了某个数字也可能指向错误对象。还需要核对事实关系,并在获准条件下比较原始与压缩输入的下游答案。
本次没有调用下游模型,因此不能宣称压缩后问答质量不变。公开实验中的收益不能代替自己的业务评估,两个短文本也不足以估计中文整体表现。保护参数、其他模型或压缩策略需要另测,不承诺增加 force_tokens 就解决全部问题。
预算不满足时可以回退原文并重新选择证据,或调整其他冗余后再校验;缺失关键事实时保留错误并转人工复核。不要在已经丢失证据的压缩文本上无限重试。分块、去重、重排与压缩各有独立错误,按来源追踪到对应环节。
首次下载、模型加载与稳态压缩耗时分开计量,测试脚本的单次计时不代表生产并发或总延迟。本地开源软件没有按调用收费,但仍有计算、内存、存储、维护和下游模型成本。只有在相同质量与完整计费条件下,才能判断方案是否节省费用。
继续阅读:工具介绍、上下文优化推荐、Chonkie 对比、FlashRank 对比。官方资料:源码与许可、研究说明、固定模型。
常见问题
- 压缩率能当成实际 token 比例吗?
- 不能。实际输出和目标 tokenizer 会影响计数,本次中文 0.8 的计数甚至增加。
- 短语检查通过就无损吗?
- 不能。短语出现只是必要条件,还需核对数字、否定与关系及下游答案。
- 四组被拦下都表示语义错误吗?
- 不是。部分只是格式不匹配;报告区分格式、事实和预算问题。
- 分块、重排与压缩能互相替代吗?
- 不能直接替代。它们改变不同环节,组合后的效果仍需验证。
- 本次调用付费回答模型了吗?
- 没有。只验证本地压缩与门槛,不宣称下游问答质量或费用收益。
- 软件免费是否包含所有费用?
- 不包含模型准备、计算、内存、存储、维护和下游调用等成本。