guidance README

The project's README from github.com/guidance-ai/guidance. As one markdown file: README.md.

Discord Email Hours
guidance

Guidance is an efficient programming paradigm for steering language models. With Guidance, you can control how output is structured and get high-quality output for your use case—while reducing latency and cost vs. conventional prompting or fine-tuning. It allows users to constrain generation (e.g. with regex and CFGs) as well as to interleave control (conditionals, loops, tool use) and generation seamlessly.

Install

Guidance is available through PyPI and supports a variety of backends (Transformers, llama.cpp, OpenAI, etc.). If you already have the backend required for your model, you can simply run

pip install guidance

Features

A Pythonic interface for language models

When using Guidance, you can work with large language models using common Python idioms:

from guidance import system, user, assistant, gen
from guidance.models import Transformers

# Could also do LlamaCpp or many other models
phi_lm = Transformers("microsoft/Phi-4-mini-instruct")

# Model objects are immutable, so this is a copy
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)

If run at the command line, this will produce output like:

<|system|>You are a helpful assistant<|end|><|user|>Hello. What is your name?<|end|><|assistant|>I am Phi, an AI developed by Microsoft. How can I help you today?

However, if running in a Jupyter notebook, then Guidance provides a widget for a richer user experience:

Guidance widget showing HTML generation

With Guidance, it's really easy to capture generated text:

# Get a new copy of the Model
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']=}")
lm['lm_response']='I am Phi, an AI developed by Microsoft. How can I help you today?'

Guarantee output syntax with constrained generation

Guidance provides an easy to use, yet immensely powerful syntax for constraining the output of a language model. For example, a gen() call can be constrained to match a regular expression:

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")
The language model is 13 years old

Often, we know that the output has to be an item from a list we know in advance. Guidance provides a select() function for this scenario:

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']}")
The model selected C

The constraint system offered by Guidance is extremely powerful. It can ensure that the output conforms to any context free grammar (so long as the backend LLM has full support for Guidance). More on this below.

Debug grammars offline (no model API calls)

When iterating on constraints, you can validate candidate strings locally and test a full run with the Mock model.

from guidance import gen
from guidance.models import Mock

grammar = "expr=" + gen(regex=r"\d+([+*]\d+)*", name="expr")

# 1) Validate strings directly against the grammar
assert grammar.match("expr=12+7*3") is not None
assert grammar.match("expr=12+*3") is None

# 2) Run the same grammar with a local mock model
lm = Mock(b"<s>expr=12+7*3")
lm += grammar
print(lm["expr"])  # 12+7*3

Create your own Guidance functions

With Guidance, you can create your own Guidance functions which can interact with language models. These are marked using the @guidance decorator. Suppose we wanted to answer lots of multiple choice questions. We could do something like the following:

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

Now, define some questions:

questions = [
    {
        "question" : "Which state has the northernmost capital?",
        "choices" : [
            "New South Wales",
            "Northern Territory",
            "Queensland",
            "South Australia",
            "Tasmania",
            "Victoria",
            "Western Australia",
        ],
        "answer" : 1,
    },
    {
        "question" : "Which of the following is venomous?",
        "choices" : [
            "Kangaroo",
            "Koala Bear",
            "Platypus",
        ],
        "answer" : 2,
    }
]

We can use our decorated function like gen() or select(). The language_model argument will be filled in for us automatically:

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']}")
<|system|>You are a student taking a multiple choice test.<|end|><|user|>Which state has the northernmost capital?
a : New South Wales
b : Northern Territory
c : Queensland
d : South Australia
e : Tasmania
f : Victoria
g : Western Australia
<|end|><|assistant|>b
LM Answer: 1,  Correct Answer: 1
<|system|>You are a student taking a multiple choice test.<|end|><|user|>Which of the following is venomous?
a : Kangaroo
b : Koala Bear
c : Platypus
<|end|><|assistant|>c
LM Answer: 2,  Correct Answer: 2

Guidance functions can be composed, in order to construct a full context free grammar. For example, we can create Guidance functions to build a simple HTML webpage (note that this is not a full implementation of HTML). We start with a simple function which will generate text which does not contain any HTML tags. The function is marked as stateless to indicate that we intend to use it for composing a grammar:

@guidance(stateless=True)
def _gen_text(lm: Model):
    return lm + gen(regex="[^<>]+") 

We can then use this function to generate text within an arbitrary HTML tag:

@guidance(stateless=True)
def _gen_text_in_tag(lm: Model, tag: str):
    lm += f"<{tag}>"
    lm += _gen_text()
    lm += f"</{tag}>"
    return lm

Now, let us create the page header. As part of this, we need to generate a page title:

@guidance(stateless=True)
def _gen_header(lm: Model):
    lm += "<head>\n"
    lm += _gen_text_in_tag("title") + "\n"
    lm += "</head>\n"
    return lm

The body of the HTML page is going to be filled with headings and paragraphs. We can define a function to do each:

from guidance.library import one_or_more

@guidance(stateless=True)
def _gen_heading(lm: Model):
    lm += select(
        options=[_gen_text_in_tag("h1"), _gen_text_in_tag("h2"), _gen_text_in_tag("h3")]
    )
    lm += "\n"
    return lm

@guidance(stateless=True)
def _gen_para(lm: Model):
    lm += "<p>"
    lm += one_or_more(
        select(
            options=[
                _gen_text(),
                _gen_text_in_tag("em"),
                _gen_text_in_tag("strong"),
                "<br />",
            ],
        )
    )
    lm += "</p>\n"
    return lm

Now, the function to define the body of the HTML itself:

@guidance(stateless=True)
def _gen_body(lm: Model):
    lm += "<body>\n"
    lm += one_or_more(select(options=[_gen_heading(), one_or_more(_gen_para())]))
    lm += "</body>\n"
    return lm

Next, we come to the function which generates the complete HTML page. We add the HTML start tag, then generate the header, then body, and then append the ending HTML tag:

@guidance(stateless=True)
def _gen_html(lm: Model):
    lm += "<html>\n"
    lm += _gen_header()
    lm += _gen_body()
    lm += "</html>\n"
    return lm

Finally, we provide a user-friendly wrapper, which will allow us to:

from guidance.library import capture, with_temperature

@guidance(stateless=True)
def make_html(
    lm,
    name: str | None = None,
    *,
    temperature: float = 0.0,
):
    return lm + capture(
        with_temperature(_gen_html(), temperature=temperature),
        name=name,
    )

Now, use this to generate a simple webpage:

lm = phi_lm

with system():
    lm += "You are an expert in HTML"

with user():
    lm += "Create a simple and short web page about your life story."

with assistant():
    lm += make_html(name="html_text", temperature=0.7)

When running in a Jupyter Notebook so that the widget is active, we get the following output:

Guidance widget showing HTML generation with token fast-forwarding

Note the varying highlighting of the generation. This is showing another of Guidance's capabilities: fast-forwarding of tokens. The constraints imposed by a grammar often mean that some tokens are known in advance. Guidance doesn't need the model to generate these; instead it can insert them into the generation. This saves forward passes through the model, and hence reduces GPU usage. For example, in the above HTML generation, Guidance always knows the last opening tag. If the last opened tag was <h1> (for example), then as soon as the model generates </, Guidance can fill in h1> without needing the model to perform a forward pass.

Generating JSON

A JSON schema is actually a context free grammar, and hence it can be used to constrain an LLM using Guidance. This is a common enough case that Guidance provides special support for it. A quick sample, based on a Pydantic model:

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']=}")

# Use Python's JSON library
loaded_json = json.loads(lm["bp"])
print(json.dumps(loaded_json, indent=4))

# Use Pydantic
result = BloodPressure.model_validate_json(lm["bp"])
print(result.model_dump_json(indent=8))
lm['bp']='{"systolic": 301, "diastolic": 15, "location": "arm"}'
{
    "systolic": 301,
    "diastolic": 15,
    "location": "arm"
}
{
        "systolic": 301,
        "diastolic": 15,
        "location": "arm"
}

Note that the generated blood pressure is not one the model will have seen for a human. When generating JSON, a substantial number of tokens can often be fast-forwarded, due to the structural constraints imposed by the schema.

guidance (guidance-ai/guidance)

Searching Database…

21.8K ·GitHub
这是什么?Guidance 是一个 Python 库,提供一种高效的编程范式来引导大语言模型:你可以控制输出的结构,同时相比传统的提示词或微调,能降低延迟和成本。
它是加到我的 Python 项目里的库,还是一个独立的软件?它是一个安装到你项目里的 Python 库,不是独立软件:你写普通的 Python 代码,把控制流——条件判断、循环、调用工具——和模型生成穿插在一起。
我怎么把它跑起来?用 pip 从 PyPI 安装即可。它支持多种后端(Transformers、llama.cpp、OpenAI 等),所以如果你的模型后端已经有了,只需装好 Guidance 就行。
用的时候需要我自己的模型或者 API 密钥吗?需要——Guidance 驱动的是你自己提供的语言模型:可以是本地的,比如 Transformers 或 llama.cpp,也可以是 API,比如 OpenAI。它本身不带模型。
受限生成的保证在所有后端上都成立吗?不一定——输出符合任意上下文无关文法的保证,只有在后端大模型完整支持 Guidance 时才成立,所以覆盖程度取决于你选的后端。
select() 和 gen() 有什么区别?gen() 是在正则表达式这类约束下生成文本,而 select() 是让模型从你事先给定的列表中选出一项——当输出必须是固定选项之一时很有用。
我能在自己的代码里拿到模型生成的文本吗?可以——捕获生成的文本是一等特性:你可以把模型的输出直接取到你的程序里,README 里就展示了带装饰器的 Guidance 函数把捕获的内容作为 Model 对象返回。
这个项目还在活跃维护吗?这个仓库在 CPU 和 GPU 测试上运行持续集成,还有专门的文档和 Linux 工作流,说明开发在持续进行并且有测试保障。
Size
18.9 MB
Stars
21.8K
Age
3y
Commits · 90 Days