跳过内容

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 指导生成的索引。这可能需要一些时间,但只需做一次。如果您想使用相同的模式多次生成,请确保只调用一次 generate.json

自定义类型

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

使用 JSON Schema

您可以传递一个表示JSON Schema规范的字符串给generate.json,而不是Pydantic模型:

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 架构生成器 - GenSON

Outlines 集成了 GenSON 构建器,以能够动态声明 JSON 模式。可以按如下方式使用:

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}

每当您通过构建器更新架构时,您需要重新定义大纲生成器以包含这些更改。根据之前的示例:

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在通过其构建器动态修改架构时的行为。下面是一个示例,说明您如何可能丢失required信息,并生成缺少字段的json:

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

print(builder.to_schema())
# {'$schema': 'http://json-schema.org/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'}}}