进阶 lmql.ai 2026-10-11 18:10:44 · 34 阅读

第29章 LMQL与Python集成指南

Python 集成

用 Python 调用 LMQL。在现有应用中集成 LMQL 的首选方式就是使用 lmql Python 包。该包提供了一系列函数,让你可以直接在 Python 中定义和运行 LMQL 程序。

查询函数

在 Python 中,一段 LMQL 代码表现为标准的 Python 函数。这意味着你可以直接定义、参数化并调用 LMQL 代码,无需跳出现有的 Python 应用。

为实现这一目标,LMQL 提供了三个入口:

@lmql.query 装饰器:使用标准的 def 语法定义 LMQL 查询函数,支持捕获和访问周围的 Python 作用域。 lmql.run 函数:直接运行一段 LMQL 代码字符串作为查询,无需先定义函数。 lmql.F(...):将 LMQL 代码片段作为纯 lambda 表达式求值,这与 Python 的 lambda 关键字非常相似。

这三种方法在内部都会为提供的代码构建一个 LMQL 查询函数。本章首先讨论如何使用 lmql.query、lmql.run 和 lmql.F 定义和运行查询函数,随后介绍查询结果格式以及如何配置查询(例如指定模型、解码器和其他参数)。

lmql.query

@lmql.query 装饰器允许你将一段 LMQL 代码直接暴露为 Python 函数,并从现有代码中调用它:

```python import lmql

@lmql.query def chain_of_thought(question): '''lmql # Q&A 提示模板 "Q: {question}\n" "A: Let's think step by step.\n" "[REASONING]" "Thus, the answer is:[ANSWER]."

# 仅将 ANSWER 返回给调用者 return ANSWER '''

print(chain_of_thought('Today is the 12th of June, what day was it 1 week ago?')) ``` 输出: `5th of June.`

注意,实际的 LMQL 程序定义在函数的 docstring 中,这使得 LMQL 代码能够无缝嵌入。函数参数可以自由传入 LMQL,函数的返回值会传回给调用者。查询程序还可以捕获周围上下变量,从而在 LMQL 内部发挥 Python 的全部威力。

Jupyter 笔记本

你在 Jupyter Notebook 中可能会遇到与 @lmql.query 装饰器相关的问题,因为笔记本环境默认是异步的。为了解决这个问题,你可以将查询函数声明为 async,并在调用时使用 await 关键字。

或者,你也可以安装 nest_asyncio 包并调用 nest_asyncio.apply(),以在笔记本中启用嵌套事件循环。

变量捕获

@lmql.query 装饰器还允许你访问周围上下文的变量,并使其对查询程序可用。这是在定义时通过自动捕获函数闭包来实现的:

```python import re

a = 12

@lmql.query def query(): '''lmql # 访问全局命名空间中的 'a' "Tell me a fun fact about {a}: [FACT]" # 使用导入的 're' 模块 return re.sub(r'\d+', '[NUMBER]', FACT) '''

print(query()) ``` 输出: `[NUMBER] is the smallest number with exactly six divisors ([NUMBER], [NUMBER], [NUMBER], [NUMBER], [NUMBER], [NUMBER]).`

如图所示,在 @lmql.query 函数内部,你可以完全访问周围上下文,包括外部作用域中定义的任何变量和导入项。这使得 LMQL 能够灵活地集成到你现有的代码库中。

lmql.run

若要直接运行一段 LMQL 代码字符串,而不必先定义函数,可以使用 lmql.run 函数:

```python query_string = """ "Q: {question}\\n" "A: Let's think step by step.\\n" "[ANSWER]" """

await lmql.run(query_string, question="What is 2x3?") ```

注意,在使用 lmql.run 时,查询字符串中的换行符 \n 必须转义为 \\n,以避免语法错误。若要同步运行(不使用 await),可以使用 lmql.run_sync 函数代替。

转义 LMQL 专用控制字符

在直接从字符串构建查询时,务必正确转义 LMQL 专用的控制字符,以避免生成的查询程序出现语法错误。

预编译查询

使用 lmql.run 时,每次调用都会将查询字符串编译为查询函数。为了避免这种情况,你可以使用 lmql.query 函数配合字符串,创建一个只编译一次但可多次调用的函数:

```python chain_of_thought = lmql.query(""" "Q: {question}\\n" "A: Let's think step by step.\\n" "[ANSWER]" """, is_async=False)

chain_of_thought("What is 2x3?") ```

这里,设置 is_async=False 确保查询可以同步执行。

lmql.F

最后,lmql.F 提供了一种轻量级的方式来将 LMQL 代码片段作为简单的 lambda 表达式求值,这与 Python 的 lambda 关键字非常相似。

这为在代码中集成小型基于 LLM 的工具提供了一个轻量级入口,无需编写完整的 LMQL 程序:

```python summarize = lmql.F("Summarize the following in a \ few words: {data}: [SUMMARY]")

main_subject = lmql.F("What is the main subject (noun) \ of the following text? {data}: [SUBJECT]", "len(TOKENS(SUBJECT)) < 20")

text = "LMQL generalizes natural language prompting, ..."

summarize(data=text) # LMQL improves natural language prompting with # Python and fixed answer templates for better control over LLMs.

main_subject(data=text) # LMQL ```

从语法上看,lmql.F 表达式对应于单个 LMQL 提示语句,但不带 " 引号。lmql.F 函数返回一个可调用对象,可以像普通查询函数一样使用。

返回值:如果 lmql.F 只包含一个占位符变量,其生成的值将用作函数的返回值。否则,将返回一个包含所有占位符值的字典。

约束条件:要在 lmql.F 表达式中指定约束条件,可以传入 constraints=... 字符串参数,它将被解析并像普通 LMQL 提示语句中的 where 子句一样执行。

查询结果

通常,查询函数的结果由 return 语句的使用以及执行查询所用的解码算法决定:

带有 return 语句的查询函数

带有 return 语句的查询函数将返回该 return 语句的值。如果使用了具有多个输出序列的解码算法(例如 sample(n=2)),返回值将是所有返回值的列表。

```python number = lmql.F("A random number: [JOKE: int]")

# 采样两个结果 number(decoder="sample", n=2) ``` 结果: `[1, 25]`

不带 return 语句的查询函数

如果未指定 return 语句,查询函数的返回值将是一个指定的 lmql.LMQLResult 对象,其中包含查询程序中定义的所有变量的最后一次赋值:

```python class LMQLResult: # 替换了所有变量的完整提示词 prompt: str # 所有已赋值的模板变量的字典 variables: Dict[str, str] ```

这允许你在查询执行后检查完整的查询提示词以及单个变量的值:

```python joke = lmql.query(""" "Q: Tell me a joke about plants\\n" "A: [JOKE]" """, is_async=False)

joke() ``` 结果: `LMQLResult(prompt='Q: Tell me a joke about plants\nA: Why did the tomato turn red?\n\nBecause it saw the salad dressing!', variables={'JOKE': 'Why did the tomato turn red?\n\nBecause it saw the salad dressing!'}, distribution_variable=None, distribution_values=None)`

如果使用了具有多个输出序列的解码算法(例如 sample(n=2)),返回值将是一个包含所有此类 LMQLResult 对象的列表。

查询配置

为了进一步控制查询执行(例如设置模型、解码器等),你可以为任何 lmql.query、lmql.run 或 lmql.F 调用提供额外的配置参数:

```python @lmql.query(model=lmql.model("chatgpt"), decoder="argmax") def chain_of_thought(question): ... ```

支持以下参数:

model= - 用于查询的模型。这会覆盖查询程序中指定的模型。你可以传入模型标识符或一个 lmql.model 对象。 decoder= - 用于查询的解码器。这会覆盖查询程序中指定的任何解码器。你可以传入任何支持的解码器标识符。 output_writer= - 用于在执行期间流式传输查询进度的输出写入器,详见“输出流式传输”。默认为 None。 verbose= - 是否在查询执行期间打印详细日志输出(API 请求、LLM 推理参数等)。默认为 False。 certificate= - 是否为查询执行生成推理证书。默认为 False。 kwargs - 任何额外的关键字参数都会传递给解码算法。有关可用参数的更多详细信息,请参阅其他解码参数。

覆盖默认值

要在调用时覆盖这些配置参数,你还可以将它们作为额外关键字参数传递给查询函数调用:

```python # 使用 'gpt2-xl' 模型执行 'chain_of_thought' chain_of_thought("What is the meaning of life?", model="gpt2-xl", temperature=0.5) ```

结果对象是一个数据类,具有以下字段:

```python class LMQLResult: # 替换了所有变量的完整提示词 prompt: str # 所有已赋值的模板变量的字典 variables: Dict[str, str] ```

评论 (0)