TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

Ollama 结构化输出:让本地模型返回 JSON

用 Ollama 的结构化输出约束本地模型:json 模式与 Schema 模式两级控制、Pydantic 和 Zod 的生成校验闭环、嵌套字段与视觉场景的结构化描述。

模型输出自由文本好写但难用——下游程序要解析就得分词、猜格式、处理异常。Ollama 的结构化输出把 JSON Schema 直接压在模型响应上,让每次回复都符合预定结构,数据抽取、图片描述、批量处理从此有确定性的接口 [1]。注意一点:Ollama 云端目前不支持结构化输出,这个能力只在本地服务上可用 [1]。

两级控制:json 模式与 Schema 模式

第一级是粗粒度的 json 模式:format 字段填字符串 json,模型保证输出合法 JSON,但结构自己定 [1]:

curl -X POST http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{
  "model": "gpt-oss",
  "messages": [{"role": "user", "content": "Tell me about Canada in one line"}],
  "stream": false,
  "format": "json"
}'
from ollama import chat

response = chat(
  model='gpt-oss',
  messages=[{'role': 'user', 'content': 'Tell me about Canada.'}],
  format='json'
)
print(response.message.content)
import ollama from 'ollama'

const response = await ollama.chat({
  model: 'gpt-oss',
  messages: [{ role: 'user', content: 'Tell me about Canada.' }],
  format: 'json'
})
console.log(response.message.content)

第二级是 Schema 模式:format 直接传 JSON Schema,字段名、类型、必填项全部约束到位 [1]:

curl -X POST http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{
  "model": "gpt-oss",
  "messages": [{"role": "user", "content": "Tell me about Canada."}],
  "stream": false,
  "format": {
    "type": "object",
    "properties": {
      "name": {"type": "string"},
      "capital": {"type": "string"},
      "languages": {
        "type": "array",
        "items": {"type": "string"}
      }
    },
    "required": ["name", "capital", "languages"]
  }
}'

文档还给了个实用建议:把 Schema 同时以字符串形式放进提示词,能让模型的响应更贴合预期——双重约束比单靠解码约束更稳 [1]。

这项能力的底层是受限解码:模型每生成一个 token 都要在 Schema 允许的范围内选择,字段顺序、引号闭合、类型合法性都由解码过程保证,而不是靠提示词恳求。这解释了它和「请在提示词里输出 JSON」的本质区别——后者模型随时可能加一句开场白破坏结构,前者从机制上杜绝了这种可能。代价是输出多样性受限,创作类任务不适合套 Schema,数据接口类任务才是它的主场。

类型安全的闭环

真正的工程红利在「Schema 定义一次、生成与校验两处复用」。Python 侧用 Pydantic:model_json_schema() 生成约束,model_validate_json() 校验响应,两端共享同一个类定义 [1]:

from ollama import chat
from pydantic import BaseModel

class Country(BaseModel):
  name: str
  capital: str
  languages: list[str]

response = chat(
  model='gpt-oss',
  messages=[{'role': 'user', 'content': 'Tell me about Canada.'}],
  format=Country.model_json_schema(),
)

country = Country.model_validate_json(response.message.content)
print(country)

JavaScript 侧用 Zod:z.toJSONSchema() 序列化 Schema,解析后再用同一个对象 parse 校验 [1]:

import ollama from 'ollama'
import * as z from 'zod'

const Country = z.object({
  name: z.string(),
  capital: z.string(),
  languages: z.array(z.string()),
})

const response = await ollama.chat({
  model: 'gpt-oss',
  messages: [{ role: 'user', content: 'Tell me about Canada.' }],
  format: z.toJSONSchema(Country),
})

const country = Country.parse(JSON.parse(response.message.content))
console.log(country)

这个闭环的价值在于把「模型输出不可信」变成「类型系统兜底」:即使模型偶发偏出 Schema,校验层会立刻报错而不是让坏数据流进业务代码。

表单填充、工单归类、简历解析、商品信息整理,这些任务的共同点是输入自由、输出规整,结构化输出正好架在中间。相比传统 NLP 的实体识别管线,用 Schema 定义抽取目标几分钟就能换一套字段重新上线,模型的理解能力顶替了标注数据。批量处理时配上 temperature 0 和固定 seed,同一份输入能稳定复现同一份输出,方便做回归测试。

数据抽取实战

结构化输出的典型场景是从自由文本里抽数据:定义好要返回的对象,让模型填字段。嵌套模型和可选字段都能表达 [1]:

from ollama import chat
from pydantic import BaseModel

class Pet(BaseModel):
  name: str
  animal: str
  age: int
  color: str | None
  favorite_toy: str | None

class PetList(BaseModel):
  pets: list[Pet]

response = chat(
  model='gpt-oss',
  messages=[{'role': 'user', 'content': 'I have two cats named Luna and Loki...'}],
  format=PetList.model_json_schema(),
)

pets = PetList.model_validate_json(response.message.content)
print(pets)

str | None 的可选字段是这里的关键设计:文本里没提到的信息让模型填 null,而不是硬编一个值。设计抽取 Schema 时,把「不确定就留空」显式写进类型定义,比在提示词里反复叮嘱有效得多。

视觉场景的结构化描述

视觉模型吃同一个 format 参数,图片描述从此可以做成确定性接口。分类字段用 Literal 枚举锁死取值范围,temperature 压到 0 换取最高确定性 [1]:

from ollama import chat
from pydantic import BaseModel
from typing import Literal, Optional

class Object(BaseModel):
  name: str
  confidence: float
  attributes: str

class ImageDescription(BaseModel):
  summary: str
  objects: list[Object]
  scene: str
  colors: list[str]
  time_of_day: Literal['Morning', 'Afternoon', 'Evening', 'Night']
  setting: Literal['Indoor', 'Outdoor', 'Unknown']
  text_content: Optional[str] = None

response = chat(
  model='gemma4',
  messages=[{
    'role': 'user',
    'content': 'Describe this photo and list the objects you detect.',
    'images': ['path/to/image.jpg'],
  }],
  format=ImageDescription.model_json_schema(),
  options={'temperature': 0},
)

image_description = ImageDescription.model_validate_json(response.message.content)
print(image_description)

这个模式能直接支撑图片审核、图片打标、相册检索这类管线:objects 数组带置信度,time_of_day 和 setting 是枚举值,下游按字段过滤就行,不再需要解析自然语言。

可靠性清单

把文档的实践建议收拢成三条 [1]:Schema 用 Pydantic 或 Zod 定义,一份定义同时用于约束生成和校验响应;温度调低(例如设为 0)让补全更确定;走 OpenAI 兼容接口时,结构化输出通过 response_format 字段启用。再补一条工程习惯:Schema 演进要当成接口变更管理,加字段可以、删字段和改类型要走版本号——下游解析代码都依赖这份结构 [1]。

本文改编自 Ollama 官方文档的 Structured Outputs 一节[1],原文以 MIT 许可证发布。

来源

  1. 1

引用本页: Ollama 结构化输出:让本地模型返回 JSON. TellWorld, https://www.tellworld.net/zh/practice/practice-ollama-structured-outputs. 更新于 2026-11-14

相关动态