11.4 Prompt、In-Context Learning 与结构化输出:字符串控制不了权限边界
模型工坊暂时没有重训阿花的基础模型,只在输入里写任务、示例和输出格式。几分钟后就能试新流程,这是 prompt 的优势;同一句指令换个模板、例子顺序或 generation config,结果也可能变化,这是它的代价。
Prompt 是版本化的程序输入,不是自然语言魔法。Chat role 最终会被 tokenizer template 编成 token sequence,模型仍在做条件生成。
本课目标
- 区分 zero-shot、few-shot 与 weight-updating fine-tuning;
- 正确使用 checkpoint 对应的 chat template;
- 设计任务、上下文、示例、约束和输出 schema;
- 理解 temperature/top-p/greedy 对生成与评估的影响;
- 用权限隔离而非“更强提示词”处理 prompt injection。
1. In-Context Learning 不更新权重
Zero-shot:只给任务/输入。Few-shot:在同一 context 放 demonstrations。模型参数保持不变,输出条件变为:
$$ p(y\mid instruction,examples,input). $$
它可以快速适配格式与局部 pattern,但 context 结束后不会把示例永久写入权重。不能把 few-shot 叫“临时微调”后忽略二者在状态、成本和安全上的差异。
2. Chat Messages 最终是 Token Sequence
不同 instruct checkpoints 即使共享 base,也可能使用不同 control tokens:
<system>...</system><user>...</user><assistant>或完全不同格式。手工拼接错误 role token 会显著改变行为。使用 tokenizer 附带的 chat template,并固定版本。
from transformers import AutoModelForCausalLM, AutoTokenizer
model_id = "your-pinned-instruct-checkpoint"
tokenizer = AutoTokenizer.from_pretrained(model_id, revision="pinned-revision")
model = AutoModelForCausalLM.from_pretrained(model_id, revision="pinned-revision")
messages = [
{"role": "system", "content": "Return one JSON object matching the schema."},
{"role": "user", "content": "Extract severity from: disk latency is critical"},
]
encoded = tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_tensors="pt",
return_dict=True,
)
generated = model.generate(
**encoded,
max_new_tokens=64,
do_sample=False,
)
new_tokens = generated[:, encoded["input_ids"].shape[1]:]
answer = tokenizer.batch_decode(new_tokens, skip_special_tokens=True)[0]若先 apply_chat_template(tokenize=False) 再单独 tokenize,通常要避免重复 special tokens。训练时一般不加 generation prompt;具体遵循模板 contract。
3. 一个可维护 Prompt 的组成
task / objective
input contract and delimiters
authoritative context
decision policy / abstention
few-shot examples
output schema
edge cases and constraints
untrusted content把稳定 policy 与动态 user/data 分开。用明确 delimiters 标记数据,但 delimiters 只是帮助模型识别结构,不是安全沙箱。
Prompt 中的“你必须永远……”无法阻止后续 untrusted text 影响模型,也无法限制应用实际 tool/network 权限。
4. Few-shot Example 选择
Examples 会同时教任务、格式、标签先验和风格。选择时覆盖:
- 正常与边界 case;
- 各 label/拒绝路径;
- 长度、语言和格式差异;
- 容易混淆的 negative examples;
- 正确 schema 与错误不可接受输出。
顺序、相似度和 label frequency 会影响结果。动态 retrieval examples 还可能引入数据泄漏/注入;检索索引与 examples 必须版本化。
不是“3 个精挑例子永远优于 10 个”。用 token budget 与 held-out eval 比较数量、覆盖和顺序。
5. “一步一步思考”不是可靠验证器
要求 rationale 可能改善某些任务,也可能生成流畅但错误的解释、泄露敏感中间信息或增加 token/latency。可优先要求可核查 artifacts:
- 最终结构化字段;
- 引用/证据 ID;
- 可执行代码与 tests;
- 计算结果和独立 checker;
- 对不确定性的明确拒绝。
不要把模型输出的 chain-of-thought 当作真实内部因果过程。高风险系统用外部规则、tool verification、多模型/人工审查,而不是只要求“再仔细想想”。
6. Generation Config 是行为的一部分
- greedy:每步选最高概率 token;
- sampling:按调整后分布采样;
- temperature:缩放 logits,越低通常越尖锐;
- top-k/top-p:截断候选分布;
- beam search:保留多个高分序列,未必适合开放对话;
- max_new_tokens/stop:控制终止。
设置 temperature 却 do_sample=False 可能没有预期效果,取决于 API。固定 prompt 却改变 decoding,也不是同一系统版本。
Greedy 在固定 stack 下通常更稳定,但浮点/kernel/版本差异仍可能改变 tie/后续序列。评估 stochastic generation 要多 samples/seeds,并报告 variance/pass@k 的定义。
7. Structured Output 不能只靠“请返回 JSON”
可靠流程:
- 定义 JSON Schema/grammar;
- 使用 provider/model 支持的 constrained decoding(若有);
- parse;
- schema validate;
- semantic validate(范围、交叉字段、权限);
- 明确 retry/repair/abstain;
- 原始输出与错误分类可审计。
JSON 可解析不代表事实正确。永远不要把模型生成的 SQL、shell、URL 或 tool arguments 未校验直接执行。
Prefill 输出开头可能提高格式一致性,但要与 chat template 的 assistant role/EOS 规则匹配。
8. Prompt Injection 是 Trust Boundary 问题
RAG 文档、网页、邮件和 tool output 都可能包含“忽略之前指令”之类内容。模型没有可靠的自然语言权限隔离,攻击文本与可信指令最终都在 context 中。
防护重点:
- 最小 tool 权限与 per-action authorization;
- allowlist/schema/type/range validation;
- 把 untrusted data 标记并限制其用途;
- sensitive data 不默认进 context;
- side-effect action 需用户确认/二次策略检查;
- egress/network/domain 控制;
- injection/adversarial eval 与日志;
- 模型输出永远作为 untrusted proposal。
System prompt 有优先语义是应用/model 训练约定,不是防火墙。隐藏 system prompt 也不能当 secret storage。
9. Context Budget 与 Information Placement
Token budget 包括 system、history、examples、retrieved docs、tool schemas、user input 和预留 output。超过窗口会截断/报错;未超出也不保证模型同等利用所有位置。
策略:
- 统计每部分 token quantiles;
- 优先保留当前任务与授权信息;
- 对 history 做可追溯摘要,保留关键原文;
- retrieval 去重/排序/引用;
- 为 output/工具回合预留预算;
- 测试关键信息在头/中/尾的位置敏感性。
“支持 1M context”不等于把一百万 tokens 全塞进去最优。成本、latency、noise 与利用率需要评估。
10. Prompt Evaluation 不是看三个 Demo
建立 versioned eval set:
- normal、edge、adversarial、multilingual;
- schema/parse rate;
- task correctness/groundedness;
- refusal over/under-rate;
- injection/tool misuse;
- latency/tokens/cost;
- stability across paraphrase/order/seed;
- human rubric 与 inter-rater agreement。
Prompt、model revision、tokenizer/template、retrieval corpus、tools 和 decoding config 一起组成 system version。只记录 prompt 文本无法复现。
反复根据同一 eval 改 prompt 会过拟合;保留 blind/private/future set。
11. Prompt、RAG 还是 Fine-tune
| 需求 | 常见起点 | 原因 |
|---|---|---|
| 改任务说明/输出格式 | Prompt/schema | 快、可回滚 |
| 提供频繁更新或可引用知识 | RAG/tool | 不必把事实写进权重 |
| 稳定改变大量行为/风格 | SFT/PEFT | 减少每次 context 示例 |
| 精确业务计算/权限 | Code/tool/rules | 可验证、可授权 |
| 多项组合 | Prompt + RAG + fine-tune + tools | 分层解决不同问题 |
Fine-tune 不是知识库更新的唯一手段,RAG 也不能自动保证答案使用了文档。第 12 章会把它们放进完整 LLM system。
常见误区
- Few-shot 会临时更新模型权重:它只改变当前条件输入。
- System prompt 是安全边界:权限必须由应用与工具层执行。
- 要求 chain-of-thought 就能验证正确性:解释也可能错误。
- 返回合法 JSON 就可执行:还需语义与权限校验。
- Prompt 固定即可复现:model/template/retrieval/decoding 都是版本。
练习
- 用正确/错误 chat template 比较 token IDs 与输出。
- 为 extraction task 设计 zero-shot/few-shot,并做 order ablation。
- 给 JSON 输出添加 schema、semantic validation 和失败策略。
- 构造带注入的检索文档,设计 tool allowlist 与确认边界。
- 建一份 prompt regression set,记录 model/template/decoding 版本。
小结
Prompt 与 in-context examples 通过输入 tokens 改变条件生成,不更新权重。可靠应用要匹配 chat template、冻结 generation config、验证结构化输出并持续评估。安全不能靠更强措辞,要在模型外建立权限、校验和副作用边界。
下一章把模型放进真实系统:SFT/偏好对齐改变策略,RAG 提供外部证据,tools/agents 执行动作,evaluation 贯穿整个链路。