跳到内容

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:

text
<system>...</system><user>...</user><assistant>

或完全不同格式。手工拼接错误 role token 会显著改变行为。使用 tokenizer 附带的 chat template,并固定版本。

python
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 的组成

text
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:控制终止。

设置 temperaturedo_sample=False 可能没有预期效果,取决于 API。固定 prompt 却改变 decoding,也不是同一系统版本。

Greedy 在固定 stack 下通常更稳定,但浮点/kernel/版本差异仍可能改变 tie/后续序列。评估 stochastic generation 要多 samples/seeds,并报告 variance/pass@k 的定义。

7. Structured Output 不能只靠“请返回 JSON”

可靠流程:

  1. 定义 JSON Schema/grammar;
  2. 使用 provider/model 支持的 constrained decoding(若有);
  3. parse;
  4. schema validate;
  5. semantic validate(范围、交叉字段、权限);
  6. 明确 retry/repair/abstain;
  7. 原始输出与错误分类可审计。

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 都是版本。

练习

  1. 用正确/错误 chat template 比较 token IDs 与输出。
  2. 为 extraction task 设计 zero-shot/few-shot,并做 order ablation。
  3. 给 JSON 输出添加 schema、semantic validation 和失败策略。
  4. 构造带注入的检索文档,设计 tool allowlist 与确认边界。
  5. 建一份 prompt regression set,记录 model/template/decoding 版本。

小结

Prompt 与 in-context examples 通过输入 tokens 改变条件生成,不更新权重。可靠应用要匹配 chat template、冻结 generation config、验证结构化输出并持续评估。安全不能靠更强措辞,要在模型外建立权限、校验和副作用边界。

下一章把模型放进真实系统:SFT/偏好对齐改变策略,RAG 提供外部证据,tools/agents 执行动作,evaluation 贯穿整个链路。

Built with VitePress | Software Systems Atlas