如何让神经网络输出有效 JSON,无需变通方案和重试请求
每个尝试将语言模型连接到真实后端的人都遇到过这个问题。你编写了详细的提示词,要求模型返回严格符合结构的 JSON,在十个示例上测试——一切都很顺利。但在第一百次请求时,神经网络突然忘记关闭引号,在响应开头添加了"Here is your answer!"这样的短语,或者凭空编造一个不存在的字段。结果,Pydantic 抛出验证错误,服务崩溃。
通常,开发者通过重试 API 请求、使用复杂正则表达式,或在生成后尝试"修复"损坏的响应来解决这个问题。.txt 团队的开发者采取了不同的方法,创建了 Outlines——一个在单个 token 级别引导文本生成过程的库。

受控生成的工作原理
大多数框架将 LLM 视为黑盒:发送文本,等待返回完整字符串。Outlines 在采样过程中拦截控制权。在每个生成步骤中,库会检查模型词表中的哪些 token 符合指定模式,哪些违反了规则。
如果你请求一个数字,库会简单地将所有包含字母或标点的 token 的采样概率设为零。模型在物理上无法选择无效符号。结果不是对有效 JSON 的期望,而是从第一次尝试就能保证正确的结构。
这种方法节省 token 和时间。你不再需要在提示词中要求模型"不要添加额外文本",也不需要在解析失败时运行重新生成。
该库的功能
Outlines 的接口尝试模仿熟悉的 Python 类型语法。你只需将所需的数据类型与提示词一起传递。
固定响应选项
如果你需要从有限的值集合中进行选择,可以通过 Literal 或 Enum 来定义。模型不会写长篇推理——它会立即输出指定的值之一。
import outlines
from typing import Literal
from transformers import AutoTokenizer, AutoModelForCausalLM
model_name = "microsoft/Phi-3-mini-4k-instruct"
model = outlines.from_transformers(
AutoModelForCausalLM.from_pretrained(model_name, device_map="auto"),
AutoTokenizer.from_pretrained(model_name)
)
# Модель вернет строго одно из трех слов
sentiment = model(
"Оцени тональность ответа: 'Сервис работает отлично, спасибо!'",
Literal["Positive", "Negative", "Neutral"]
)
print(sentiment) # Positive
基于 Pydantic Schema 生成
对于复杂对象,可以使用标准的 Pydantic 模型。Outlines 根据 schema 构建语法,并确保响应结构完全符合它。
from pydantic import BaseModel
from enum import Enum
class TicketPriority(str, Enum):
low = "low"
medium = "medium"
high = "high"
urgent = "urgent"
class ServiceTicket(BaseModel):
priority: TicketPriority
category: str
requires_manager: bool
summary: str
prompt = """
Проанализируй обращение:
Срочно! Не могу войти в личный кабинет после оплаты. Через час презентация клиенту!
"""
# Модель сгенерирует JSON, который точно совпадает со структурой ServiceTicket
ticket_json = model(prompt, ServiceTicket, max_new_tokens=200)
ticket = ServiceTicket.model_validate_json(ticket_json)
print(ticket.priority) # TicketPriority.urgent
print(ticket.requires_manager) # True
正则表达式和语法
如果 Pydantic 模型对你的任务来说过于繁琐,可以定义严格的正则表达式。这对于提取电话号码、日期、邮政编码或创建内部 DSL 非常方便。
支持的模型
Outlines 适配你的技术栈。该库支持多种模式:
- 通过
transformers和llama.cpp进行本地推理 - 基于 vLLM 和 Ollama 的服务器解决方案
- OpenAI 和 Gemini 等外部 API
当使用自己的本地模型或本地推理服务器时,该库的优势最为明显。在这些场景中,直接的 token 掩码控制提供了 100% 的结构保证并加快了操作速度。
生产环境用例
在实践中,该库同时解决了几个常见的开发任务:
- 自动工单分类。从传入的客户邮件中提取类别、紧急程度和标签,而不会遇到损坏对象的风险。
- 实体提取与不完整数据处理。通过
Union类型,你可以允许模型返回填充的对象或带有缺失信息消息的显式字符串。 - 函数调用。你可以向模型传递一个常规 Python 函数,Outlines 自动从其参数中提取类型以形成正确的调用参数。
- 产品目录分类。将产品描述快速解析为类别、品牌和关键特征。
局限性和注意事项
尽管有这些优势,理解这项技术的具体细节很重要。为 token 计算掩码需要额外的资源。如果你的 Pydantic schema 由数十个嵌套对象和复杂正则表达式组成,生成前的语法准备可能需要一些时间。
此外,如果你仅通过 OpenAI API 工作,该库将使用提供商内部的机制(Structured Outputs / JSON Mode)。在这种情况下,Outlines 充当一个方便的统一接口,但采样过程本身由 OpenAI 服务器控制。
Outlines 解决了任何团队在将 LLM 投入生产时面临的真实工程问题。该项目消除了生成过程中的不稳定性,使得能够像使用常规类型化函数一样与神经网络交互。如果你正在基于开源模型构建后端服务或自主代理,这个工具绝对值得加入你的工具箱。
相关项目