跳到内容

JSON 结构化生成

Outlines 可以使任何开源模型返回一个遵循用户指定结构的 JSON 对象。这在模型的输出需要由下游代码处理时非常有用:代码不理解自然语言,而是理解它被编程以理解的结构化语言。

人们希望从 LLM 获取 JSON 格式输出主要有两个原因

  1. 解析答案(例如使用 Pydantic),将其存储在某处,返回给用户等。
  2. 使用结果调用函数

在这两种情况下,Outlines 都能满足您的需求!实际上,要定义您希望模型遵循的 JSON 结构,您可以提供一个 Pydantic 模型或一个函数。无需重复代码!

使用 Pydantic

Outlines 可以从 Pydantic 模型推断输出结构。结果是包含 LLM 返回值的模型实例。

from pydantic import BaseModel

from outlines import models, generate


class User(BaseModel):
    name: str
    last_name: str
    id: int


model = models.transformers("microsoft/Phi-3-mini-4k-instruct")
generator = generate.json(model, User)
result = generator(
    "Create a user profile with the fields name, last_name and id"
)
print(result)
# User(name="John", last_name="Doe", id=11)

JSON 和空白字符

默认情况下,Outlines 会阻止模型生成带有语法换行符、制表符或多个空格的 json。默认的 whitespace_patternr"[ ]?"。如果 whitespace_pattern 允许无限间隔,小型模型倾向于进入无限重复循环。如果您希望允许模型生成多个制表符、换行符和空格,可以按如下方式设置空白模式

generator = generate.json(model, User, whitespace_pattern=r"[\n\t ]*")

性能

generation.json 计算一个索引,帮助 Outlines 指导生成。这可能需要一些时间,但只需执行一次。如果您想使用相同的 schema 进行多次生成,请确保只调用 generate.json 一次。

自定义类型

Outlines 提供了 自定义 Pydantic 类型,这样您就不必为常见的类型(例如电话号码或邮政编码)编写正则表达式了。

使用 JSON Schema

您可以使用表示 JSON Schema 规范的字符串代替 Pydantic 模型传递给 generate.json

from pydantic import BaseModel

from outlines import models
from outlines import generate

model = models.transformers("microsoft/Phi-3-mini-4k-instruct")

schema = """
{
  "title": "User",
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "last_name": {"type": "string"},
    "id": {"type": "integer"}
  },
  "required": ["name", "last_name", "id"]
}
"""

generator = generate.json(model, schema)
result = generator(
    "Create a user profile with the fields name, last_name and id"
)
print(result)
# User(name="John", last_name="Doe", id=11)

从函数签名

Outlines 可以从函数的签名推断输出结构。结果是一个字典,可以使用通常的字典展开语法 ** 直接传递给函数。

from outlines import models
from outlines import generate

def add(a: int, b: int):
    return a + b

model = models.transformers("microsoft/Phi-3-mini-4k-instruct")
generator = generate.json(model, add)
result = generator("Return two integers named a and b respectively. a is odd and b even.")

print(add(**result))
# 3

直接传递函数来指定结构的一个巨大优势是 LLM 的结构会随着函数的定义而改变。无需在多个地方修改代码!

从动态 JSON schema 构建器 - GenSON

Outlines 集成了 GenSON 构建器,能够动态声明 JSON schema。可以如下使用

from genson import SchemaBuilder

from outlines import models
from outlines import generate

builder = SchemaBuilder()
builder.add_schema({"type": "object", "properties": {}})
builder.add_object({"name": "Toto", "age": 5})

model = models.transformers(
    "HuggingFaceTB/SmolLM2-135M",
    device="auto",
)
generator = generate.json(model, builder)

res = generator("Return a json of a young boy")
print(res)
# {"name": "Ben", "age": 10}

无论何时您通过构建器更新 schema,都需要重新定义 outline 生成器以包含这些更改。以上述示例为例

from genson import SchemaBuilder

from outlines import models
from outlines import generate

builder = SchemaBuilder()
builder.add_schema({"type": "object", "properties": {}})
builder.add_object({"name": "Toto", "age": 5})

model = models.transformers(
    "HuggingFaceTB/SmolLM2-135M",
    device="auto",
)
generator = generate.json(model, builder)

res = generator("Return a json of a young boy")
print(res)
# {"name": "Ben", "age": 10}

builder.add_object({"hobby": "sports"})
generator = generate.json(model, builder)

res = generator("Return a json of a youg boy whose hobby is coding")
print(res)
# {"name": "Ben", "age": 10, "hobby": "coding"}

注意

请注意 GenSON 在通过其构建器动态修改 schema 方面的行为。以下是一个示例,说明您可能会丢失 required 信息并生成缺少字段的 json

builder = SchemaBuilder()
builder.add_schema({"type": "object", "properties": {}})
builder.add_object({"name": "Toto", "age": 5})

print(builder.to_schema())
# {'$schema': 'https://json-schema.fullstack.org.cn/schema#', 'type': 'object', 'properties': {'name': {'type': 'string'}, 'age': {'type': 'integer'}}, 'required': ['age', 'name']}

builder.add_object({"hobby": "sport"})
print(builder.to_schema())
# {'name': {'type': 'string'}, 'age': {'type': 'integer'}, 'hobby': {'type': 'string'}}}