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

第12章 嵌套查询与提示模块化

第12章:嵌套查询

使用嵌套提示来模块化你的查询代码。嵌套查询允许你在另一个查询的上下文中执行一个查询函数。通过嵌套多个查询函数,你可以将较小的可复用组件组装成复杂的程序。为此,LMQL 将过程式编程的理念应用于提示工程。

为了更好地理解这一概念,让我们看一个简单的示例:

lmql@lmql.query def chain_of_thought(): '''lmql "A: Let's think step by step.\n [REASONING]" "Therefore the answer is[ANSWER]" where STOPS_AT(ANSWER, ".") return ANSWER.strip() '''

"Q: It is August 12th, 2020. What date was it " "100 days ago? [ANSWER: chain_of_thought]"

这里,占位符变量 `ANSWER` 被注释为引用查询函数 `chain_of_thought`。这意味着将使用 `chain_of_thought` 的嵌套实例化来生成 `ANSWER` 的值。

要理解其在运行时的行为,请考虑此程序的执行轨迹:

promptdown Model Output Q: It is August 12th, 2020. What date was it 100 days ago? wait800 beginincontext chain_of_thought A: Let's think step by step. wait800 REASONING 100 days ago would be May 4th, 2020. Therefore the answer is endincontext ANSWER May 4th, 2020 wait800 fadeincontext wait800

你可以点击“Replay”重新播放动画。

为了生成 `ANSWER`,`chain_of_thought` 定义的额外提示和约束被插入到主查询上下文中。然而,在 `ANSWER` 生成后,这些额外指令会从轨迹中移除,只保留嵌套查询调用的返回值。这种机制类似于过程式编程中函数的栈帧。

嵌套允许你使用仅局部相关的变量特定指令,而不会干扰程序的其他部分,从而将提示逻辑封装为可重用的组件。

参数化查询

你还可以向嵌套查询传递参数,以自定义其行为:

lmql@lmql.query def one_of(choices: list): '''lmql "Among {choices}, what do you consider \ most likely? [ANSWER]" where ANSWER in choices return ANSWER '''

"Q: What is the capital of France? \ [ANSWER: one_of(['Paris', 'London', 'Berlin'])]"

promptdown Model Output Q: What is the capital of France? wait800 beginincontext one_of Among ['Paris', 'London', 'Berlin'], what do you consider most likely? endincontext wait800 ANSWER Paris wait800 fadeincontext wait800

例如,在这里我们使用 `one_of` 来生成多项选择题的答案。选项作为参数传递给嵌套查询,这使得我们可以将相同的代码用于不同的问题。

多部分程序

你还可以依次使用多个嵌套查询,从而允许你反复向提示中注入指令,同时不干扰整体流程:

lmql@lmql.query def dateformat(): '''lmql "(respond in DD/MM/YYYY) [ANSWER]" return ANSWER.strip() '''

"Q: When was Obama born? [ANSWER: dateformat]\n" "Q: When was Bruno Mars born? [ANSWER: dateformat]\n" "Q: When was Dua Lipa born? [ANSWER: dateformat]\n"

"Out of these, who was born last?[LAST]"

promptdown Model Output Q: When was Obama born? wait200 beginincontext dateformat (respond in DD/MM/YYYY) endincontext wait200 ANSWER 04/08/1961 wait200 fadeincontext wait200 hideincontext wait200

Q: When was Bruno Mars born? wait200 beginincontext 1dateformat (respond in DD/MM/YYYY) endincontext 1wait200 ANSWER 08/10/1985 wait200 fadeincontext 1wait200 hideincontext 1wait200

Q: When was Dua Lipa born? wait200 beginincontext 2dateformat (respond in DD/MM/YYYY) endincontext 2wait200 ANSWER 22/08/1995 wait200 fadeincontext 2wait200 hideincontext 2wait200

Out of these, who was born last? LAST Dua Lipa

我们指示模型在回答初始问题时使用特定的日期格式。由于 `dateformat` 作为嵌套函数使用,这些指令只是临时包含在内,每生成一个答案包含一次,并在移动到下一个问题之前被移除。

一旦生成了所有中间答案,我们就查询 LLM 来比较各个日期并确定最新的一个,此时最后的查询不受 `dateformat` 指令的影响。

返回值

如果查询函数不返回值,则将其作为嵌套函数调用时,执行后不会移除插入的指令。因此,无返回值嵌套函数的效果相当于宏展开,如下所示:

这在需要在一个或多个位置使用固定模板时很有帮助,例如用于列表项。此外,如下图所示,嵌套函数也可以参数化以自定义其行为。

lmql@lmql.query def items_list(n: int): '''lmql for i in range(n): "-[ITEM]" where STOPS_AT(ITEM, "\n") '''

"A list of things not to forget to pack for your \ next trip:\n[ITEMS: items_list(4)]"

promptdown A list of things not to forget to pack for your next trip: ITEM- Passport ITEM- Toothbrush ITEM- Phone charger ITEM- Sunscreen

Python 中的嵌套查询

要在 Python 环境中使用嵌套查询,你可以从一个 `@lmql.query` 函数引用另一个。

python@lmql.query def dateformat(): '''lmql "(respond in DD/MM/YYYY)[ANSWER]" return ANSWER.strip() '''

@lmql.query def main_query(): '''lmql "Q: It is August 12th, 2020. What date was it \ 100 days ago? [ANSWER: dateformat]" '''

这里,`main_query` 将 `dateformat` 作为嵌套查询引用,两个函数都定义在同一文件的顶层。不过,你也可以从其他文件导入并复用查询代码,只要它们在你的主查询函数作用域内可访问即可。利用这一特性,你可以编写可复用查询函数的库,用于跨应用程序甚至供其他用户使用。

评论 (0)