Guidance:大模型结构化提示生成引擎指南
guidance-ai/guidance 是微软研究院发起的开源项目,实时 Star 数约 21.8k,是一套「引导大模型输出」的编程范式。本文基于官方 README 完整翻译整理,从项目定位、安装方式、Pythonic 接口、约束生成、自定义函数、JSON 生成到离线调试逐一说明。
Guidance 的核心思想是:用 Python 代码直接控制大模型输出的结构。它允许你约束生成(正则、CFG 上下文无关文法),也允许把控制流(条件、循环、工具调用)和文本生成无缝交织在一起。相比传统的提示工程或微调,Guidance 在保证输出质量的同时,还能降低延迟和成本——因为很多 token 在语法上是确定的,不需要模型再算一遍。
对做应用的开发者来说,Guidance 解决的是一个非常实际的痛点:你让大模型输出 JSON,它经常给你多一个逗号、少一个引号、或者夹带几句解释;你让它输出 A/B/C/D,它经常给你输出「我觉得答案是 C」。传统做法是写一大堆后处理正则去修,修不好就重试。Guidance 的做法是从生成源头就把这些非法路径堵死,模型只能在你规定的语法空间里选 token,既省了后处理的麻烦,也省了重试浪费的 token。
这个项目最初由微软研究院发起,后来成立了独立的 guidance-ai 组织维护。它和同样来自微软的 Semantic Kernel、以及学术界的 Outlines、Textual Inversion 等项目一起,构成了「结构化大模型输出」这条技术路线的主要工具链。对于想把大模型真正用在生产环境、而不是只在 Demo 里聊天的团队来说,这一类工具是必学的。
一、项目官方信息
| 项目 | 信息 |
|---|---|
| GitHub 仓库 | guidance-ai/guidance |
| 联系邮箱 | guidanceai@microsoft.com |
| 社区 | Discord |
| Star 数 | 约 21.8k |
| 开源协议 | MIT |
| 主要语言 | Jupyter Notebook / Python |
| 支持后端 | Transformers、llama.cpp、OpenAI 等多种 |
二、官方图片
封面取 GitHub 仓库官方图,本文不重复贴图。项目 Logo 见 docs/figures/guidance_logo_blue.svg。
三、环境要求
Guidance 通过 PyPI 分发,支持多种后端。只要你已经装好所需模型的后端(比如 Transformers 后端需要 PyTorch,llama.cpp 后端需要本地 GGUF 权重),就可以直接安装 Guidance 本体。Python 版本要求 3.9+,建议配合 Jupyter Notebook 使用,因为 Guidance 提供了交互式 widget,能在浏览器里实时高亮生成过程。
硬件方面,如果你用 Transformers 后端跑 7B 级模型,建议 16GB 显存;跑 70B 建议 80GB 或多卡。如果你用 llama.cpp 后端跑 GGUF 量化模型,8GB 显存甚至 CPU 也能跑小模型。如果你只用 OpenAI 后端,则本地不需要 GPU,只需要网络能访问 OpenAI API。
四、完整部署步骤
1. 安装 Guidance
pip install guidance
2. 安装后端(按需)
- Transformers 后端:
pip install transformers torch,然后用Transformers("microsoft/Phi-4-mini-instruct")加载。 - llama.cpp 后端:下载 GGUF 权重,用
LlamaCpp("path/to/model.gguf")加载。 - OpenAI 后端:设置
OPENAI_API_KEY环境变量,用OpenAI("gpt-4o")加载。
Guidance 的设计是「后端无关」的:同一份 Prompt 模板和约束文法,换一个后端就能跑不同模型,不需要改业务代码。这种设计在做模型对比实验时特别方便——你可以用同一套 Prompt 模板分别跑 Phi、Llama、Qwen,看哪个模型在你的任务上表现最好,而不用为每个模型重写一遍 Prompt。
安装完成后,建议先跑一个最简单的 Hello World 例子验证环境是否正常,比如用 Transformers("microsoft/Phi-4-mini-instruct") 加载一个小模型,跑一次 gen(max_tokens=20),能正常出结果就说明 Guidance 和后端都装好了。第一次运行会自动从 Hugging Face 下载 Phi-4-mini 权重,国内用户建议先配好 Hugging Face 镜像端点再跑,否则第一次下载权重可能会卡住,影响调试心情。
五、使用方法
1. Pythonic 接口:像写普通 Python 一样写 Prompt
Guidance 用 with system()、with user()、with assistant() 上下文管理器来组织对话角色,用 gen() 标记模型生成的位置:
from guidance import system, user, assistant, gen
from guidance.models import Transformers
# 也可以用 LlamaCpp 或其他后端
phi_lm = Transformers("microsoft/Phi-4-mini-instruct")
# Model 对象是不可变的,这是一个副本
lm = phi_lm
with system():
lm += "You are a helpful assistant"
with user():
lm += "Hello. What is your name?"
with assistant():
lm += gen(max_tokens=20)
print(lm)
命令行运行会输出拼接好的完整对话;在 Jupyter Notebook 里,Guidance 会渲染一个交互式 widget,高亮显示哪部分是模型生成的、哪部分是你写的。
这种写法和写普通 Python 几乎一样:用 with 上下文管理角色,用 += 拼接文本,用 gen() 标记模型生成的位置。你不需要学一套新的 DSL,也不需要写复杂的提示模板字符串,Python 的缩进、条件、循环都可以直接用。这也是 Guidance 相比其他提示工程工具最大的差异点——它把 Prompt 变成了代码,而不是把代码变成了 Prompt。
捕获生成的文本也很简单,给 gen() 起个名字就行:
lm = phi_lm
with system():
lm += "You are a helpful assistant"
with user():
lm += "Hello. What is your name?"
with assistant():
lm += gen(name="lm_response", max_tokens=20)
print(f"{lm['lm_response']=}")
2. 约束生成:保证输出语法
Guidance 最强大的能力是约束输出。比如你要求模型只输出一个数字:
lm = phi_lm
with system():
lm += "You are a teenager"
with user():
lm += "How old are you?"
with assistant():
lm += gen("lm_age", regex=r"\d+", temperature=0.8)
print(f"The language model is {lm['lm_age']} years old")
模型被强制只能输出数字,不管 temperature 多高都不会冒出别的字符。
如果输出必须是预设列表里的某一项,用 select():
from guidance import select
lm = phi_lm
with system():
lm += "You are a geography expert"
with user():
lm += """What is the capital of Sweden? Answer with the correct letter.
A) Helsinki
B) Reykjavík
C) Stockholm
D) Oslo
"""
with assistant():
lm += select(["A", "B", "C", "D"], name="model_selection")
print(f"The model selected {lm['model_selection']}")
这比让模型自由生成再自己做正则校验高效得多——模型从一开始就被限制在合法选项里,不会浪费 token。
Guidance 的约束系统底层是上下文无关文法(CFG),理论上可以约束任何能用 CFG 描述的输出格式。只要后端模型完整支持 Guidance 的约束机制(即在推理时能动态修改 logits mask),你就能写出任意复杂的文法约束,比如「只允许输出符合国家邮政编码格式的字符串」「只允许输出符合 SQL SELECT 语法的查询」「只允许输出符合特定 XML 标签嵌套规则的文档」。这种能力在做结构化输出、做工具调用、做代码生成时特别有用。
3. 离线调试文法(不调模型 API)
迭代约束规则时,可以用 Mock 模型在本地验证文法是否正确,不需要真的调用大模型:
from guidance import gen
from guidance.models import Mock
grammar = "expr=" + gen(regex=r"\d+([+*]\d+)*", name="expr")
# 1) 直接校验字符串是否符合文法
assert grammar.match("expr=12+7*3") is not None
assert grammar.match("expr=12+*3") is None
# 2) 用本地 Mock 模型跑一遍完整流程
lm = Mock(b"<s>expr=12+7*3")
lm += grammar
print(lm["expr"]) # 12+7*3
这个能力对做工程化特别重要:你可以在 CI 里跑文法测试,不需要花 GPU 钱。
传统做 Prompt 开发的流程是:写一个 Prompt → 调模型跑一遍 → 看输出对不对 → 改 Prompt → 再跑一遍。这个循环每一步都要花钱、花时间。Guidance 让你可以把「文法设计」和「模型调用」解耦:先用 Mock 模型在本地把文法调试到正确,确认文法本身没问题,再接真实模型跑端到端测试。这样能把调试成本降一个数量级。
4. 自定义 Guidance 函数
用 @guidance 装饰器可以把复杂 Prompt 封装成可复用函数,比如封装一个零样本选择题:
import guidance
from guidance.models import Model
ASCII_OFFSET = ord("a")
@guidance
def zero_shot_multiple_choice(
language_model: Model,
question: str,
choices: list[str],
):
with user():
language_model += question + "\n"
for i, choice in enumerate(choices):
language_model += f"{chr(i+ASCII_OFFSET)} : {choice}\n"
with assistant():
language_model += select(
[chr(i + ASCII_OFFSET) for i in range(len(choices))], name="string_choice"
)
return language_model
然后像 gen() 一样调用它,language_model 参数会自动传入。这种封装方式的好处是:你可以把复杂的业务 Prompt 模板(比如「先给一段背景说明,再列选项,最后让模型选一个字母」)封装成一个函数,以后在任何地方复用,不用每次都复制粘贴一大段 Prompt 字符串。Guidance 函数可以层层嵌套、组合,最终拼成一棵完整的 Prompt 文法树。
lm = phi_lm
with system():
lm += "You are a student taking a multiple choice test."
for mcq in questions:
lm_temp = lm + zero_shot_multiple_choice(question=mcq["question"], choices=mcq["choices"])
converted_answer = ord(lm_temp["string_choice"]) - ASCII_OFFSET
print(lm_temp)
print(f"LM Answer: {converted_answer}, Correct Answer: {mcq['answer']}")
5. 组合文法:生成合法 HTML
多个 Guidance 函数可以组合成完整的上下文无关文法。官方示例演示了如何用这种方式让模型生成结构合法的 HTML 页面(标签闭合、嵌套正确):
- 先用
@guidance(stateless=True)定义无状态的原子函数,比如_gen_text生成不含标签的文本、_gen_text_in_tag生成被某个标签包裹的文本。 - 再逐层组合:
_gen_header生成 head、_gen_heading生成 h1/h2/h3、_gen_para生成 p 标签、_gen_body生成 body、_gen_html生成完整页面。 - 最后用
make_html包装,加capture捕获结果,加with_temperature控制采样温度。
这种方式生成的 HTML 标签一定闭合、结构一定合法,因为文法层面就禁止了非法输出。
这个例子虽然只是演示用的简化版 HTML 文法(不是完整的 HTML 规范实现),但它展示了 Guidance 组合文法的思路:从最小的原子函数开始,一层层往上拼,每层只负责自己那一小块语法规则,最后拼成一个完整的语言模型输出文法。这种思路和写编程语言的编译器是一样的——词法分析、语法分析、语义分析,每一层都有明确的边界。
6. 快速前递(Token Fast-Forwarding)
Guidance 的另一个核心优化是快速前递:当文法约束意味着下一个 token 是确定的(比如刚生成了 <h1>,后面必然跟 </h1>),Guidance 不需要模型再做一次前向计算,直接把这个 token 插进去。这在生成 JSON、HTML 这类结构化内容时能省掉大量 GPU 调用,显著降低延迟和成本。
举个具体例子:生成一个 100 个 token 的 JSON 字符串,其中可能有 30 个 token 是确定的(比如引号、冒号、逗号、括号),Guidance 可以直接跳过这 30 次模型前向计算,相当于把延迟降低了 30%。这也是为什么官方 README 说 Guidance 「相比传统提示工程或微调,能在降低延迟和成本的同时保持输出质量」——不是靠模型变小,而是靠少算不必要的 token。
7. 生成 JSON
JSON Schema 本质上就是一种上下文无关文法,Guidance 对它做了专门支持。基于 Pydantic 模型一行代码就能生成合法 JSON:
import json
from pydantic import BaseModel, Field
from guidance import json as gen_json
class BloodPressure(BaseModel):
systolic: int = Field(gt=300, le=400)
diastolic: int = Field(gt=0, le=20)
location: str = Field(max_length=50)
model_config = dict(extra="forbid")
lm = phi_lm
with system():
lm += "You are a doctor taking a patient's blood pressure taken from their arm"
with user():
lm += "Report the blood pressure"
with assistant():
lm += gen_json(name="bp", schema=BloodPressure)
print(f"{lm['bp']=}")
生成的 JSON 一定符合 Pydantic 模型的约束(字段类型、取值范围、禁止额外字段),可以直接用 BloodPressure.model_validate_json() 反序列化。
这比传统的「让模型输出 JSON,然后 try-except json.loads,失败就重试」方案优雅得多。传统方案里,模型可能输出半截 JSON、多余文字、或者不符合 schema 的字段,你需要写一大堆容错逻辑;Guidance 从生成时就把 schema 翻译成文法约束,模型只能输出合法 JSON,一次成功,不需要重试。
六、FAQ
- Guidance 和 Outlines、Function Calling 有什么区别? Guidance 是微软出的,主打「Python 代码即 Prompt 模板」,把控制流和生成交织在一起;Outlines 是另一个类似项目;Function Calling 是 OpenAI 提供的专有接口。Guidance 的优势是后端无关、本地可跑、文法表达力强。
- 为什么要用约束生成而不是后处理校验? 后处理校验意味着模型可能生成非法输出,你需要重试或兜底;约束生成从源头就不让模型输出非法内容,既省 token 又省重试成本。
- 支持哪些模型后端? Transformers、llama.cpp、OpenAI 是官方 README 明确提到的;理论上任何能提供 logits 或补全接口的后端都可以适配。
- Jupyter widget 怎么用? 在 Notebook 里正常运行代码即可,Guidance 会自动检测环境并渲染交互式 widget,高亮显示生成过程。
- 遇到问题去哪问? Discord 社区 或邮件 guidanceai@microsoft.com。
- 和传统提示工程比,学习成本高吗? 不高。如果你已经会写 Python、会用上下文管理器,Guidance 的 API 基本一看就懂;新学的概念主要是「约束文法」和「快速前递」这两个,但理解了之后收益很大。
- 生产环境能用吗? 可以,但要注意后端是否完整支持 Guidance 的约束机制(即能否在推理时动态修改 logits)。OpenAI 等闭源 API 因为不开放 logits 修改,约束能力会受限;本地开源模型通过 Transformers 或 llama.cpp 后端则可以完整支持。
- 适合做什么场景? 结构化输出(JSON、XML、SQL)、工具调用、代码生成、选择题/分类任务、任何需要严格控制输出格式的场景。
- 和微调比有什么优势? 微调是改模型权重,成本高、周期长;Guidance 是在推理时加约束,不改权重、即插即用。对于「只要输出格式正确」的需求,Guidance 比微调轻得多。
- 能不能和 Unsloth、Transformers 一起用? 可以。Guidance 是推理时的约束层,不影响微调;Unsloth 微调完的模型,可以直接用 Guidance 加载做推理约束。
七、跳转链接
- GitHub 仓库:guidance-ai/guidance
- Logo 素材:docs/figures
- Discord 社区:discord.gg/cjPfAK43dz
- 联系邮箱:guidanceai@microsoft.com
本文所有命令与版本号均与官方 README 一致,不做任何臆造。Guidance 是微软系大模型工程化工具链的重要一环,适合做结构化输出、工具调用和代码生成场景,建议在本地小模型上先跑通示例再迁移到生产环境,能省掉大量后处理和重试成本,是生产级 LLM 应用的必备工具之一,值得每个大模型应用开发者认真学习和使用。