第8章 约束与可控生成
约束 可控的、类型安全的 LLM 生成,并附带保证。
LMQL 允许你指定语言模型输出的约束。这在脚本化提示词中很有价值,可以确保模型输出在预期位置停止,也可以引导模型在解码过程中遵循特定规则。
本章概述了当前可用的约束集合及其语义简介。除此之外,LMQL 的约束支持是模块化的,也可以进行扩展,关于自定义约束的详细说明请参阅后续章节。
支持的约束
停止短语 对于许多提示词,尤其是脚本化提示词,确保模型在遇到特定单词或符号时停止解码非常重要。为此,LMQL 支持 STOPS_AT 和 STOPS_BEFORE 约束。它们接受两个参数,第一个是分配模型输出的变量名,第二个是停止短语。
在下面的示例中,我们使用停止条件来确保一旦模型预测到换行符 \n,当前 THING 变量的解码就会结束,查询程序继续执行。
"A list of things not to forget when going to the sea (not travelling): \n" for i in range(5): "-[THING]" where STOPS_AT(THING, "\n")
promptdown A list of things not to forget when going to the sea (not travelling): - THINGSunscreen - THINGBeach towels - THINGSwimsuit - THINGSunglasses - THINGHat
TIP 如果查询中有多个变量具有相同的名称,该约束将应用于所有变量。
数字类型约束 生成变量的数据类型也可以进行约束。例如,我们可以使用 INT 约束变量为字符串编码的整数:
"A number: [N]" where INT(N)
promptdown A number: N2
这强制模型不能生成不对应整数的 token。
INFO LMQL 目前仅支持整数约束。但在未来的版本中计划支持浮点数和其他类型。
集合选择 LMQL 允许指定变量应为可能值集合中的选项。这可以重新表述为变量在可能值集合内,例如在以下示例中,THING in set(["Volleyball", "Sunscreen", "Bathing Suite"]):
sample(temperature=0.8) "A list of things not to forget when going to the sea (not travelling): \n" "- Sunglasses \n" for i in range(4): "- [THING] \n" from 'openai/text-ada-001' where THING in set(["Volleyball", "Sunscreen", "Bathing Suite"])
promptdown A list of things not to forget when going to the sea (not travelling): - THINGSunglasses - THINGSunscreen - THINGVolleyball - THINGSunscreen - THINGSunscreen
字符长度 类似于 Python,len 函数可用于引用变量的长度,从而可用于在字符级别上添加长度约束。
argmax "Hello [NAME]" from 'openai/text-ada-001' where len(NAME) < 10
promptdown Hello NAME ⏎ ⏎ I am in
Token 长度 除了字符级长度约束外,你还可以指定变量 token 长度的约束。这在想要确保模型不为特定变量生成过多 token 时很有用。例如,在以下示例中,我们限制变量 THING 的长度最多为 3 个 token:
"Greet the user in four different ways: [GREETINGS]" \ where len(TOKENS(GREETINGS)) < 10
promptdown Greet the user in four different ways: GREETINGS⏎ ⏎ 1. Hello there It's nice
与字符长度约束类似,len 函数也可用于引用 TOKENS(GREETINGS) 的数量。不过,现在的长度是以 token 而非字符来衡量的。因此,输出的确切字符长度取决于模型的 tokenization。
Token 长度约束比字符长度约束更便宜,因为它们可以作为对 tokenized 输出的简单长度检查来实现,而不是更昂贵的字符级长度检查(后者需要 detokenization 和 masking)。
正则表达式约束(预览) LMQL 还支持 REGEX 约束。这允许你在变量生成期间强制应用正则表达式。
作为一个简单的例子,考虑以下代码片段:
lmql "It's the last day of June so today is [DATE]" \ where REGEX(DATE, r"[0-9]{2}/[0-9]{2}")
promptdown It's the last day of June so today is DATE30/06
PREVIEW FEATURE REGEX 约束目前处于预览阶段,这意味着在某些情况下可能无法按预期工作。如果遇到任何问题,请在 LMQL 的 GitHub issue 追踪器中报告。
组合约束 可以使用 and 和 or 关键字组合多个约束,恢复 Python 布尔表达式,作用于 LMQL 查询中使用的变量。
例如,以下程序同时使用 token 长度约束和停止条件来生成一个短篇故事:
lmql "A story about life:[STORY]" \ where STOPS_AT(STORY, ".") and len(TOKENS(STORY)) > 40
promptdown A story about life:
STORYLife is a journey filled with ups and down, twists and turns, and unexpected surprises. It is a story that is unique to each individual, with its own set of challenges and triumphs.
For some, life begins with a loving family and a comfortable home, while for others it may start with struggle and hardship.
这里,我们对 . 字符强制实施停止条件,但前提是生成的故事至少达到 40 个 token。因此,在停止条件上使用 and 的含义是,只有当其他约束满足时,才强制执行停止条件。
LMQL 约束是如何工作的?
Token Masking 和积极验证 LMQL 约束在生成每个 token 时会被积极评估,并被运行时用于在生成过程中生成 token mask。这意味着提供的约束要么通过适当引导模型生成来直接满足,要么在生成早期就失败验证,从而节省生成无效输出的成本。在贪心解码(sample 或 argmax)的情况下,这会终止生成过程;在分支解码的情况下,它会修剪当前分支,并仅继续生成在剩余的有效分支中。
高级文本约束 约束是高级别的,在文本(而非 token)级别操作。例如,用户可以指定约束如 VAR in ["Hello", "Hi", "Greetings"],而无需考虑各个短语的具体 tokenization。LMQL 会自动将这些约束转换为 token mask,可用于在生成期间引导模型,从而仅通过一次生成调用即可生成满足所提供约束的输出。
自定义约束和理论背景 要了解 LMQL 的内部工作原理以及如何实现你自己的 LMQL 约束,请参阅关于实现自定义 LMQL 约束的章节。