目录

Ollama 结构化输出教程:让模型返回固定格式的 JSON

将模型集成到程序时,我们通常需要的不是一段文字,而是程序可以直接读取的数据。例如,从一封投诉邮件中提取姓名、订单编号和问题类型。

Ollama 的 format 参数可以强制模型的输出符合你指定的 JSON 格式。

最简单的方式:只要求 JSON

curl http://localhost:11434/api/chat -d '{
  "model": "qwen3:8b",
  "messages": [{ "role": "user", "content": "列出中国台湾的三个城市和人口,用 JSON 回答" }],
  "format": "json",
  "stream": false
}'

输出保证是合法的 JSON,但字段名称和结构由模型自行决定,每次可能都不同。

指定 JSON Schema

将 JSON Schema 放进 format,就能精确规定字段:

curl http://localhost:11434/api/chat -d '{
  "model": "qwen3:8b",
  "messages": [{ "role": "user", "content": "介绍台北市" }],
  "format": {
    "type": "object",
    "properties": {
      "city": { "type": "string" },
      "population": { "type": "integer" },
      "landmarks": { "type": "array", "items": { "type": "string" } }
    },
    "required": ["city", "population", "landmarks"]
  },
  "stream": false
}'

message.content 会是一个符合格式的 JSON 字符串。

Python:使用 Pydantic 定义格式

from ollama import chat
from pydantic import BaseModel


class Complaint(BaseModel):
    customer_name: str
    order_id: str | None
    category: str          # 例如:物流、质量、退款、其他
    urgency: int           # 1~5
    summary: str


email = """
您好,我是王小美,订单编号 A20261012-88 的吹风机收到时外包装破损,
插电后完全没有反应。下周要出国,情况很紧急,请尽快处理退换货,谢谢。
"""

response = chat(
    model="qwen3:8b",
    messages=[
        {"role": "system", "content": "从投诉内容中提取信息。category 只能是:物流、质量、退款、其他。"},
        {"role": "user", "content": email},
    ],
    format=Complaint.model_json_schema(),
    options={"temperature": 0},
)

data = Complaint.model_validate_json(response.message.content)
print(data)
print(data.urgency)
customer_name='王小美' order_id='A20261012-88' category='质量' urgency=4 summary='吹风机外包装破损且无法通电,要求尽快退换货'
4

model_validate_json 会再次验证类型,确保程序拿到的数据正确。

限定选项:使用 Literal 或 Enum

from typing import Literal


class Complaint(BaseModel):
    category: Literal["物流", "质量", "退款", "其他"]

这样模型只能从这四个值中选择一个,比在提示词中说明更可靠。

JavaScript:使用 Zod 定义格式

npm install ollama zod
import ollama from "ollama";
import { z } from "zod";

const Product = z.object({
  name: z.string(),
  price: z.number(),
  tags: z.array(z.string()),
});

const response = await ollama.chat({
  model: "qwen3:8b",
  messages: [{ role: "user", content: "这款 iPhone 手机壳售价 590 元,防摔、轻薄、支持 MagSafe" }],
  format: z.toJSONSchema(Product),
});

const product = Product.parse(JSON.parse(response.message.content));
console.log(product);

(Zod 4 内置 z.toJSONSchema;旧版 Zod 3 可以使用 zod-to-json-schema 包。)

示例:批量分类

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


class Review(BaseModel):
    sentiment: Literal["正面", "负面", "中立"]
    topics: list[str]


reviews = [
    "饭菜很好吃,但是等了快一个小时。",
    "服务人员态度很好,下次还会再来!",
    "价格一般,味道也一般。",
]

for text in reviews:
    r = chat(
        model="qwen3:8b",
        messages=[{"role": "user", "content": f"分析这条评论:{text}"}],
        format=Review.model_json_schema(),
        options={"temperature": 0},
    )
    print(text, "→", Review.model_validate_json(r.message.content))

从图片中提取数据

搭配视觉模型,可以从发票、名片和截图中提取结构化数据:

class BusinessCard(BaseModel):
    name: str
    company: str | None
    title: str | None
    phone: str | None
    email: str | None


r = chat(
    model="gemma3",
    messages=[{"role": "user", "content": "提取名片上的信息", "images": ["./card.jpg"]}],
    format=BusinessCard.model_json_schema(),
    options={"temperature": 0},
)
print(BusinessCard.model_validate_json(r.message.content))

提高准确率的技巧

  • 将 temperature 设为 0,让输出更稳定。
  • 在提示词中也说明格式和字段含义,不要只依赖 schema。例如:“urgency 是 1~5 的整数,5 表示最紧急”。
  • 字段名称要清晰易懂(order_id 比 oid 好)。
  • 可能没有数据的字段设为可空(str | None),避免模型为了填充字段而编造内容。
  • 结构化输出保证的是格式,不保证内容正确。重要数据仍然需要验证。

更新于 2026年10月12日