news 2026/9/14 19:18:57

Outlines 输出类型(Output Types)完全指南:用 Python 类型约束 LLM 结构化生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Outlines 输出类型(Output Types)完全指南:用 Python 类型约束 LLM 结构化生成

Outlines 输出类型(Output Types)完全指南:用 Python 类型约束 LLM 结构化生成

【免费下载链接】outlinesStructured Outputs项目地址: https://gitcode.com/GitHub_Trending/ou/outlines

Outlines 以“输出类型”作为结构化生成的统一入口:向模型传入一个普通 Python 类型(如intLiteral、Pydantic 模型)或 Outlines 专用类型(ChoiceJsonSchemaRegexCFG),即可让文本生成严格服从该类型的语法约束。本文将完整梳理各类输出类型的用法、参数细节与底层编译原理,帮助你把这套类型体系直接应用到分类、信息抽取、JSON 生成与格式校验等实战场景中。

Overview:一次调用,一个输出类型

Outlines 模型在被调用时接收两个核心参数:一个prompt(提示词)和一个output type(输出类型),除此之外的任何推理关键字参数(如max_new_tokenstemperature)都会被原样转发给底层模型:

model("How many minutes are there in one hour", int) # "60" model("Pizza or burger", Literal["pizza", "burger"]) # "pizza" model("Create a character", Character, max_new_tokens=100) # '{"name": "James", ...}'

输出类型可以来自整个 Python 类型生态:

  • 大多数 Python 原生类型,例如intstr
  • typing模块中的类型,例如LiteralListDictEnum等;
  • 流行的第三方库类型,例如 Pydantic、GenSON。

Outlines 还针对特定输出结构提供了四个专用类型(下文逐一详解):

  • 多选一:Choice
  • JSON Schema:JsonSchema
  • 正则表达式:Regex
  • 上下文无关文法:CFG

核心心智模型:输出类型 = 函数返回值的类型提示

使用 Outlines 时,你只需要把“你希望函数返回什么类型”作为输出类型传给模型即可。例如下面这些函数签名:

from datetime import date from typing import Dict, List, Literal, Union from pydantic import BaseModel class Character(BaseModel): name: str birth_date: date skills: Union[Dict, List[str]] def give_int() -> int: ... def pizza_or_burger() -> Literal["pizza", "burger"]: ... def create_character() -> Character: ...

用 Outlines 模型生成时,把同样的类型直接作为输出类型传入,就能得到符合该类型约束的文本:

model("How many minutes are there in one hour", int) # "60" model("Pizza or burger", Literal["pizza", "burger"]) # "pizza" model("Create a character", Character, max_new_tokens=100) # '{"name": "James", "birth_date": "1980-05-10", "skills": ["archery", "negotiation"]}'

与函数类型提示的关键区别:返回的一律是字符串

Outlines 生成器永远返回字符串,这是它与普通函数类型提示最重要的差异。你需要自己把响应 cast 成目标类型:

result = model("Create a character", Character, max_new_tokens=100) casted_result = Character.model_validate_json(result) print(result) # '{"name": "Aurora", "birth_date": "1990-06-15", "skills": ["Stealth", "Diplomacy"]}' print(casted_result) # name=Aurora birth_date=datetime.date(1990, 6, 15) skills=['Stealth', 'Diplomacy']

src/outlines/generator.pyBlackBoxGeneratorSteerableGenerator实现中,__call__返回的都是self.model.generate(...)的结果——约束只作用于生成过程,生成完成后不会替你解析字符串,因此类型转换(如上面的model_validate_json)必须由调用方完成。

输出类型分类

根据使用场景,输出类型可以划分为以下几类。其中大部分来自 Python 原生或知名第三方库,而JsonSchemaRegexCFG是 Outlines 特有的三个类型。

基本 Python 类型

最直接的结构化生成方式是让回答符合某个基本类型,例如int或 Python 列表。可以使用原生基本类型和typing库中的类型:

from typing import Dict output_type = float # 合法输出示例:"0.05" output_type = bool # 合法输出示例:"True" output_type = Dict[int, str] # 合法输出示例:"{1: 'hello', 2: 'there'}"

通过组合集合类型与UnionOptional,可以构建更复杂的响应格式。例如下面这个用于表示半结构化数据的输出类型:

from typing import Dict, List, Optional, Tuple, Union output_type = Dict[str, Union[int, str, List[Tuple[str, Optional[float]]]]]

该类型对应的值是:键为字符串的字典,值可以是整数、字符串,或由“字符串 + 浮点数/None”组成的二元组列表。合法的生成响应示例(包含在字符串内):

{ "name": "Alice", "age": 30, "metrics": [("engagement", 0.85), ("satisfaction", None)] }

源码层面的细节:在 src/outlines/types/dsl.py 中,python_types_to_terms负责把 Python 类型逐层转换成 DSL 的Term对象。例如int映射为types.integer(正则[+-]?(0|[1-9][0-9]*)),float映射为types.numberbool映射为Regex("(True|False)"),原生dict则直接映射为内置 JSON 文法CFG(grammars.json)。容器类型的处理尤为精细:

  • _handle_list要求List必须恰好一个类型参数(只支持同构列表),否则抛出TypeError
  • _handle_dict要求Dict恰好两个类型参数,并强制给键加 JSON 双引号——即使键类型是int(如Dict[int, str]),因为 JSON 对象的键必须是被引号包裹的字符串;
  • _handle_union会先剥离None成员,再把None映射为Regex("None"),因此Union[int, str, None]Optional[Union[int, str]]都能正确处理,而不是像旧实现那样对NoneType报 “not supported”;
  • 转换过程有 10 层递归深度上限,超出会抛RecursionError,用于防止递归类型定义导致无限递归。

多选一(Multiple Choices)

Outlines 通过LiteralEnum输出类型支持多选一分类。例如:

from enum import Enum from typing import Literal class PizzaOrBurger(Enum): pizza = "pizza" burger = "burger" # 两种等价的多选输出类型 output_type = Literal["pizza", "burger"] output_type = PizzaOrBurger

此外,还可以使用 Outlines 特有的Choice类型,它接收一个list作为参数,特别适合选项列表是动态生成的场景:

from outlines.types import Choice def get_multiple_choices() -> list: # 这里可以包含复杂逻辑 return ["pizza", "burger"] output_type = Choice(get_multiple_choices())

源码层面的细节Choice在 src/outlines/types/dsl.py 中定义为@dataclass,字段为items: List[Any]to_regex对每个 item 递归调用python_types_to_terms后以|连接,例如Choice(["a", "b", "c"])会编译为正则(a|b|c)(见 tests/types/test_dsl.py 与 tests/types/test_to_regex.py 中的验证)。Enum类型则被转换为Alternatives:成员逐一转换后并列,且_get_enum_members会额外识别“以裸函数作为成员值”的枚举成员(通过__qualname__前缀区分普通方法与被赋值的函数),因此“函数枚举”这类特殊用法也能被正确约束。

JSON Schema

很多常见的 Python 类型存储的信息本质上等价于一份 JSON Schema。Outlines 中可用于生成符合 JSON Schema 文本的类型包括:

  • Pydantic 类
  • Dataclass
  • TypedDict
  • GenSON 的SchemaBuilder
  • Callable(函数的参数被转换为 JSON 键,类型注解用于定义值的类型)

例如:

from dataclasses import dataclass @dataclass class Character: name: str age: int output_type = Character def character(name: str, age: int): return None output_type = character

其中“Callable”路径由 src/outlines/types/utils.py 的get_schema_from_signature实现:通过inspect.signature读取每个参数的类型注解,未注解的参数会抛出ValueError;无默认值的参数在 schema 中为必填,带默认值的参数变为可选;随后用create_model动态构造 Pydantic 模型并导出model_json_schema()

另外两种 JSON Schema 格式——schema 字符串schema 字典——必须使用 Outlines 特有的JsonSchema类包装。原因是它们只是普通的字符串/字典,直接传入会产生歧义,无法与strdict等基本类型区分:

from outlines.types import JsonSchema schema_string = '{"type": "object", "properties": {"answer": {"type": "number"}}}' output_type = JsonSchema(schema_string) schema_dict = { "type": "object", "properties": { "answer": {"type": "number"} } } output_type = JsonSchema(schema_dict)

JsonSchema接受两个可选参数:

  • whitespace_pattern(默认None):指定 JSON 语法空白符的匹配模式;不提供时使用默认的宽松 JSON 空白规则。该参数只由outlines_core后端应用(见 src/outlines/backends/outlines_core.py 中build_regex_from_schema(json_schema, whitespace_pattern)的调用);llguidancexgrammar后端不支持它,设置后会产生错误(llguidance后端的 docstring 明确标注 “Not supported by the llguidance backend; must beNone”,见 src/outlines/backends/llguidance.py)。
  • ensure_ascii(默认True):对应json.dumps方法的ensure_ascii参数。设为False时,schema 中的非 ASCII 字符会被转换为 Unicode 形式。

源码层面的细节JsonSchema的构造函数在 src/outlines/types/dsl.py 中会按输入类型分派:字典走json.dumps(schema, ensure_ascii=...),Pydantic 模型走model_json_schema(),TypedDict/Dataclass 走TypeAdapter(...).json_schema(),GenSON builder 走to_json(ensure_ascii=...);最后用jsonschema.Draft7Validator.check_schema校验 schema 合法性,非法输入会抛ValueError。它还提供了两个实用的类方法:JsonSchema.from_file(path)可直接从.json文件加载 schema;JsonSchema.convert_to(schema, target_types)可以在"str""dict""pydantic""typeddict""dataclass""genson"之间互相转换。

正则表达式(Regex Patterns)

Outlines 支持用正则表达式约束文本生成。由于正则本质上是字符串字面量,直接传入会有歧义,因此必须用outlines.types.Regex对象包装:

from outlines.types import Regex regex = r"[0-9]{3}" output_type = Regex(regex)

outlines.types模块内置了一批常用的正则模式(定义在 src/outlines/types/init.py),可以直接 import 作为输出类型使用,例如句子、邮箱、ISBN 等:

from outlines.types import sentence print(type(sentence)) # outlines.types.dsl.Regex print(sentence.pattern) # [A-Z].*\s*[.!?]

仓库源码中的内置模式远不止这三个,按__all__的导出清单可归纳为:

  • Python 风格类型stringintegerbooleannumberdatetimedatetime
  • 基本正则类型digitcharnewlinewhitespacehex_struuid4ipv4ipv6semvermac_addresshex_colorslugcredit_card
  • 文档类类型sentence[A-Z].*\s*[.!?])、paragraph(一个或多个句子后跟换行)、email(RFC 5322 兼容)、isbn

这些模式大多有严谨的注解,例如ipv6覆盖八组冒号十六进制、::零压缩、IPv4 映射等完整形式;credit_card按发卡机构前缀与长度匹配 Visa、Mastercard、Amex、Diners Club、Discover、JCB、Maestro、UnionPay 等卡号格式(不校验 Luhn 校验和)。

需要自己构建复杂正则时,可以使用 Outlines 的正则 DSL:它把正则表达式组织成Term对象树,支持String/Regex两种叶子节点,以及exactlyoptionalone_or_morezero_or_morebetweenat_leastat_most等量词方法,还可用+拼接、either()做或运算,并能把自定义类型直接嵌入 Pydantic 模型做字段校验。

上下文无关文法(Context-Free Grammars)

Outlines 允许生成符合上下文无关文法语法的文本。文法使用 Lark 语言定义。由于文法以字符串表达,因此必须用outlines.types.CFG对象包装:

from outlines.types import CFG grammar_string = """ start: expr expr: "{" expr "}" | "[" expr "]" | """ output_type = CFG(grammar_string)

源码层面的细节CFG@dataclass,字段为definition: str,并且重载了__eq__(按文法字符串相等比较)。它还提供了CFG.from_file(path)类方法,可以从文件直接读取文法定义。仓库在 src/outlines/grammars.py 中预置了几个常用 Lark 文法,通过read_grammar从 src/outlines/grammars/ 目录加载——包括arithmetic(算数文法)与json(JSON 文法,python_types_to_terms转换原生dict时使用的就是它),对应的.lark源文件位于 src/outlines/grammars/arithmetic.lark 和 src/outlines/grammars/json.lark,并在 tests/cfg_samples/ 下有对应的解析测试样本。

输出类型的编译原理:从 Python 类型到 logits processor

理解输出类型如何在底层生效,有助于你判断哪些类型适合哪个后端。在 src/outlines/generator.py 中,Generator工厂函数按模型类型分派:

  • SteerableModel(本地可控模型)SteerableGenerator在初始化时会把输出类型编译成logits processor(该过程可能较昂贵,因此只构建一次并复用)。编译路径为:python_types_to_terms(output_type)先把任意 Python 类型转为 DSLTerm,然后按Term的类型分派到后端工厂函数——CFGget_cfg_logits_processorJsonSchemaget_json_schema_logits_processor(同时传入whitespace_pattern),其余全部走to_regex得到正则字符串后交给get_regex_logits_processor。每次调用__call__/batch/stream前都会先logits_processor.reset()再传给model.generate
  • BlackBoxModel/AsyncBlackBoxModel(远程 API 模型):输出类型不会编译为 logits processor,而是原样传给模型,由模型方自行处理约束(对应BlackBoxGenerator的 docstring 所述)。
  • 若同时传入output_typeprocessorGenerator会抛出ValueError(两者互斥);processor参数仅对SteerableModel开放,供高级用户直接注入已构建好的 logits processor。

从源码结构看,可推断出以下关键结论:to_regex(见 src/outlines/types/dsl.py 的to_regex函数)把StringRegexJsonSchema(经build_regex_from_schema)、ChoiceKleeneStar/KleenePlusOptionalAlternativesSequence、四种Quantify*统一递归编译为正则字符串,因此绝大多数输出类型最终都落在“正则约束”这一能力上;而JsonSchemaCFG则分别依赖后端的 JSON schema 与文法编译能力。

输出类型的可用性

上文介绍的各输出类型并非对所有模型都可用——部分模型只提供有限的结构化输出支持。具体支持范围取决于你使用的模型与后端(如transformersvllmollamallamacppopenai等),请查阅对应模型文档(参见 docs/features/models/ 目录与 结构化生成后端)确认其支持哪些输出类型。实操时建议遵循两条原则:本地可导向模型(SteerableModel)优先选择与后端匹配的输出类型(注意whitespace_patternoutlines_core支持);远程 API 模型则将输出类型直接透传给服务方能力支持的形式。

【免费下载链接】outlinesStructured Outputs项目地址: https://gitcode.com/GitHub_Trending/ou/outlines

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 19:18:57

C++ Qt天气预报大作业怎么拿高分:从数据模型到信号槽的完整架构

简介:基于C与QT开发的天气预报系统源码,是面向计算机相关专业学生课程设计和期末大作业的高分参考项目,覆盖从天气API数据获取、界面布局到交互逻辑的完整实现。资源包共74个文件,含6个头文件、5个C源文件、4个UI界面文件及1个项目…

作者头像 李华
网站建设 2026/9/14 19:17:06

CAN线故障诊断全指南:从原理到维修店选择,避免被“试错修车”坑

先讲个真实经历。去年有个朋友在文山市跑网约车,某天早上启动时仪表盘像圣诞树一样全亮:ABS灯、气囊灯、转向助力灯一起报警,挡位挂不上,车子直接趴窝。拖到附近一家维修店,师傅开口就说“行车电脑坏了,换一…

作者头像 李华
网站建设 2026/9/14 19:16:07

Mac mini部署大模型:Swift+Metal实战指南

1. 项目概述:一场被价格标签意外引爆的技术认知错位“当 Mac mini 的价格不再 mini”——这个标题乍看像一句调侃,实则精准戳中了2024年苹果生态开发者圈里最真实的一次集体怔忡。我第一次在朋友圈看到这条转发时,正用一台2018款Mac mini跑着…

作者头像 李华
网站建设 2026/9/14 19:16:02

Trae全栈开发:前后端分离架构实战指南

1. 项目概述:基于Trae的全栈分离系统开发前后端分离架构已成为现代Web开发的主流模式,而Trae作为新兴的全栈开发工具链,为这种架构提供了开箱即用的解决方案。我曾用Trae完成过三个企业级中台系统的开发,其中最复杂的项目包含87个…

作者头像 李华