Outlines 输出类型(Output Types)完全指南:用 Python 类型约束 LLM 结构化生成
Outlines 输出类型Output Types完全指南用 Python 类型约束 LLM 结构化生成【免费下载链接】outlinesStructured Outputs项目地址: https://gitcode.com/GitHub_Trending/ou/outlinesOutlines 以“输出类型”作为结构化生成的统一入口向模型传入一个普通 Python 类型如int、Literal、Pydantic 模型或 Outlines 专用类型Choice、JsonSchema、Regex、CFG即可让文本生成严格服从该类型的语法约束。本文将完整梳理各类输出类型的用法、参数细节与底层编译原理帮助你把这套类型体系直接应用到分类、信息抽取、JSON 生成与格式校验等实战场景中。Overview一次调用一个输出类型Outlines 模型在被调用时接收两个核心参数一个prompt提示词和一个output type输出类型除此之外的任何推理关键字参数如max_new_tokens、temperature都会被原样转发给底层模型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_tokens100) # {name: James, ...}输出类型可以来自整个 Python 类型生态大多数 Python 原生类型例如int、strtyping模块中的类型例如Literal、List、Dict、Enum等流行的第三方库类型例如 Pydantic、GenSON。Outlines 还针对特定输出结构提供了四个专用类型下文逐一详解多选一ChoiceJSON SchemaJsonSchema正则表达式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_tokens100) # {name: James, birth_date: 1980-05-10, skills: [archery, negotiation]}与函数类型提示的关键区别返回的一律是字符串Outlines 生成器永远返回字符串这是它与普通函数类型提示最重要的差异。你需要自己把响应 cast 成目标类型result model(Create a character, Character, max_new_tokens100) casted_result Character.model_validate_json(result) print(result) # {name: Aurora, birth_date: 1990-06-15, skills: [Stealth, Diplomacy]} print(casted_result) # nameAurora birth_datedatetime.date(1990, 6, 15) skills[Stealth, Diplomacy]在src/outlines/generator.py的BlackBoxGenerator与SteerableGenerator实现中__call__返回的都是self.model.generate(...)的结果——约束只作用于生成过程生成完成后不会替你解析字符串因此类型转换如上面的model_validate_json必须由调用方完成。输出类型分类根据使用场景输出类型可以划分为以下几类。其中大部分来自 Python 原生或知名第三方库而JsonSchema、Regex、CFG是 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}通过组合集合类型与Union、Optional可以构建更复杂的响应格式。例如下面这个用于表示半结构化数据的输出类型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 ChoicesOutlines 通过Literal或Enum输出类型支持多选一分类。例如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 类DataclassTypedDictGenSON 的SchemaBuilderCallable函数的参数被转换为 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类包装。原因是它们只是普通的字符串/字典直接传入会产生歧义无法与str、dict等基本类型区分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)的调用llguidance与xgrammar后端不支持它设置后会产生错误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文件加载 schemaJsonSchema.convert_to(schema, target_types)可以在str、dict、pydantic、typeddict、dataclass、genson之间互相转换。正则表达式Regex PatternsOutlines 支持用正则表达式约束文本生成。由于正则本质上是字符串字面量直接传入会有歧义因此必须用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 风格类型string、integer、boolean、number、date、time、datetime基本正则类型digit、char、newline、whitespace、hex_str、uuid4、ipv4、ipv6、semver、mac_address、hex_color、slug、credit_card文档类类型sentence[A-Z].*\s*[.!?]、paragraph一个或多个句子后跟换行、emailRFC 5322 兼容、isbn。这些模式大多有严谨的注解例如ipv6覆盖八组冒号十六进制、::零压缩、IPv4 映射等完整形式credit_card按发卡机构前缀与长度匹配 Visa、Mastercard、Amex、Diners Club、Discover、JCB、Maestro、UnionPay 等卡号格式不校验 Luhn 校验和。需要自己构建复杂正则时可以使用 Outlines 的正则 DSL它把正则表达式组织成Term对象树支持String/Regex两种叶子节点以及exactly、optional、one_or_more、zero_or_more、between、at_least、at_most等量词方法还可用拼接、either()做或运算并能把自定义类型直接嵌入 Pydantic 模型做字段校验。上下文无关文法Context-Free GrammarsOutlines 允许生成符合上下文无关文法语法的文本。文法使用 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算数文法与jsonJSON 文法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的类型分派到后端工厂函数——CFG走get_cfg_logits_processorJsonSchema走get_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_type与processorGenerator会抛出ValueError两者互斥processor参数仅对SteerableModel开放供高级用户直接注入已构建好的 logits processor。从源码结构看可推断出以下关键结论to_regex见 src/outlines/types/dsl.py 的to_regex函数把String、Regex、JsonSchema经build_regex_from_schema、Choice、KleeneStar/KleenePlus、Optional、Alternatives、Sequence、四种Quantify*统一递归编译为正则字符串因此绝大多数输出类型最终都落在“正则约束”这一能力上而JsonSchema与CFG则分别依赖后端的 JSON schema 与文法编译能力。输出类型的可用性上文介绍的各输出类型并非对所有模型都可用——部分模型只提供有限的结构化输出支持。具体支持范围取决于你使用的模型与后端如transformers、vllm、ollama、llamacpp、openai等请查阅对应模型文档参见 docs/features/models/ 目录与 结构化生成后端确认其支持哪些输出类型。实操时建议遵循两条原则本地可导向模型SteerableModel优先选择与后端匹配的输出类型注意whitespace_pattern仅outlines_core支持远程 API 模型则将输出类型直接透传给服务方能力支持的形式。【免费下载链接】outlinesStructured Outputs项目地址: https://gitcode.com/GitHub_Trending/ou/outlines创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →