模型能力
结构化输出
结构化输出允许 API 以特定格式返回响应,例如,返回一个符合您定义的模式(schema)的 JSON 对象,而不是自由格式的文本。此功能对于文档解析、实体提取和报告生成等任务特别有用。
TIP
使用支持的 schema 功能时,响应保证与您的 schema 匹配。
有两种方式可以从模型请求结构化输出。
主要且最灵活的方法是使用 response_format 参数。通过将 response_format.type 设置为 "json_schema" 并在 response_format.json_schema 下提供您的 schema,您可以精确定义模型应返回的结构化输出。当您不需要特定结构时,该参数也接受 "json_object" 用于任何格式良好的 JSON,或者接受默认的 "text" 用于自由格式的文本。
第二种方式是通过工具调用。当您定义工具时,xAI 模型将始终生成严格符合工具输入 JSON Schema(strict 标志隐式始终为 true)的工具调用参数。
NOTE
工具 schema 遵循本页描述的相同 JSON Schema 支持规则。完整详情请参阅 函数调用 文档。
您可以使用 Pydantic 或 Zod 等库定义 schema。
JSON Schema 支持
我们支持 JSON Schema 的一个实用子集。针对 Draft 2020-12 编写的 schema 效果最佳;也接受 Draft-07 schema。
支持的类型
stringnumberintegerbooleannullenumconstarrayobjectanyOfoneOf(行为与anyOf相同)allOf(仅单个子 schema;多个请参见尽力而为的关键字)$ref/$defs(仅非循环引用)
NOTE
additionalProperties 默认为 false,必须显式设置为 true。
要使字段可为空,请使用类型数组({"type": ["string", "null"]})或包含 null 的 anyOf 变体。未在 required 中列出的字段被视为可选字段。
字符串格式
以下值强制执行 format 关键字:
date · time · date-time · email · uuid · ipv4 · ipv6 · uri
其他 format 值被接受但不强制执行(请参见尽力而为的关键字)。
约束限制
以下约束由输出引擎强制执行,不超过以下阈值。超过这些限制的 schema 仍被接受,但一致性依赖于模型行为。
| 关键字 | 保证执行的限制 |
|---|---|
minimum / maximum / exclusiveMinimum / exclusiveMaximum | 无限制 |
minLength / maxLength | 2,048 |
minItems / maxItems | 256 |
minProperties / maxProperties | 64 |
尽力而为的关键字
这些关键字被接受但不进行结构化强制执行;模型会处理它们,在实践中也能可靠地做到,但不能保证输出满足这些约束。如果需要严格的一致性,我们建议进行验证。
notif/then/else- 包含多个子 schema 的
allOf - 未在字符串格式下列出的
format值 - 超过上述限制的约束
拒绝的 schema
以下情况将返回 400 错误:
- 零个变体的
enum或anyOf - schema 为
true或false的属性 maxContains/minContains- 作为数组的
items(元组验证请使用prefixItems)
正则表达式支持 (pattern)
在字符串字段上使用 pattern 关键字时,我们支持 ECMAScript 正则表达式 (ECMA-262) 的一个实用子集。
支持:
- 字面量和字符类(
[abc]、[a-z]、[^abc]) .(匹配任何 Unicode 代码点,包括换行符)- 替代
|、分组(...)和非捕获组(?:...) - 量词
*、+、?和重复范围{n}、{n,}、{n,m} - 简写类
\d、\w、\s(及其否定\D、\W、\S) - 常见转义:
\n、\t、\r、\f、\xHH、\uHHHH、\u{HHHHHH}
不支持:
- 后向引用(
\1、\k<name>等) - Unicode 属性转义(
\p{L}、\P{Letter}) - 单词边界(
\b、\B) - 正向和负向预查(
(?=...)、(?<=...)等) - 内联修饰符(
(?i)、(?m)等) - 条件表达式和其他高级构造
与标准 JavaScript RegExp 的语义差异:
.匹配换行符^和$是隐式的—模式总是匹配整个字符串(无需添加它们)- 捕获组
(...)没有语义效果(它们的行为类似于非捕获组) - 正则表达式在 Unicode 支持下进行求值
示例:发票解析
结构化输出的一个常见用例是解析原始文档。例如,发票包含供应商详细信息、金额和日期等结构化数据,但从原始文本中提取这些数据容易出错。结构化输出确保提取的数据符合预定义的 schema。
假设您想从发票中提取以下数据:
- 供应商名称和地址
- 发票编号和日期
- 行项目(描述、数量、价格)
- 总金额和货币
我们将使用结构化输出让 Grok 为此生成强类型的 JSON。
步骤 1:定义 Schema
您可以使用 Pydantic 或 Zod 来定义您的 schema。
from datetime import date
from enum import Enum
from pydantic import BaseModel, Field
class Currency(str, Enum):
USD = "USD"
EUR = "EUR"
GBP = "GBP"
class LineItem(BaseModel):
description: str = Field(description="Description of the item or service")
quantity: int = Field(description="Number of units", ge=1)
unit_price: float = Field(description="Price per unit", ge=0)
class Address(BaseModel):
street: str = Field(description="Street address")
city: str = Field(description="City")
postal_code: str = Field(description="Postal/ZIP code")
country: str = Field(description="Country")
class Invoice(BaseModel):
vendor_name: str = Field(description="Name of the vendor")
vendor_address: Address = Field(description="Vendor's address")
invoice_number: str = Field(description="Unique invoice identifier")
invoice_date: date = Field(description="Date the invoice was issued")
line_items: list[LineItem] = Field(description="List of purchased items/services")
total_amount: float = Field(description="Total amount due", ge=0)
currency: Currency = Field(description="Currency of the invoice")import { z } from "zod";
const CurrencyEnum = z.enum(["USD", "EUR", "GBP"]);
const LineItemSchema = z.object({
description: z.string().describe("Description of the item or service"),
quantity: z.number().int().min(1).describe("Number of units"),
unit_price: z.number().min(0).describe("Price per unit"),
});
const AddressSchema = z.object({
street: z.string().describe("Street address"),
city: z.string().describe("City"),
postal_code: z.string().describe("Postal/ZIP code"),
country: z.string().describe("Country"),
});
const InvoiceSchema = z.object({
vendor_name: z.string().describe("Name of the vendor"),
vendor_address: AddressSchema.describe("Vendor's address"),
invoice_number: z.string().describe("Unique invoice identifier"),
invoice_date: z.string().date().describe("Date the invoice was issued"),
line_items: z.array(LineItemSchema).describe("List of purchased items/services"),
total_amount: z.number().min(0).describe("Total amount due"),
currency: CurrencyEnum.describe("Currency of the invoice"),
});步骤 2:准备提示
系统提示
系统提示指示模型从文本中提取发票数据。由于 schema 是单独定义的,提示可以专注于任务,而无需在输出 JSON 中显式指定必需字段。
Given a raw invoice, carefully analyze the text and extract the relevant invoice data into JSON format.示例发票文本
Vendor: Acme Corp, 123 Main St, Springfield, IL 62704
Invoice Number: INV-2025-001
Date: 2025-02-10
Items:
- Widget A, 5 units, $10.00 each
- Widget B, 2 units, $15.00 each
Total: $80.00 USD步骤 3:最终代码
使用 SDK 的结构化输出功能来解析发票。
import os
from datetime import date
from enum import Enum
from pydantic import BaseModel, Field
from xai_sdk import Client
from xai_sdk.chat import system, user
# Pydantic Schemas
class Currency(str, Enum):
USD = "USD"
EUR = "EUR"
GBP = "GBP"
class LineItem(BaseModel):
description: str = Field(description="Description of the item or service")
quantity: int = Field(description="Number of units", ge=1)
unit_price: float = Field(description="Price per unit", ge=0)
class Address(BaseModel):
street: str = Field(description="Street address")
city: str = Field(description="City")
postal_code: str = Field(description="Postal/ZIP code")
country: str = Field(description="Country")
class Invoice(BaseModel):
vendor_name: str = Field(description="Name of the vendor")
vendor_address: Address = Field(description="Vendor's address")
invoice_number: str = Field(description="Unique invoice identifier")
invoice_date: date = Field(description="Date the invoice was issued")
line_items: list[LineItem] = Field(description="List of purchased items/services")
total_amount: float = Field(description="Total amount due", ge=0)
currency: Currency = Field(description="Currency of the invoice")
client = Client(api_key=os.getenv("XAI_API_KEY"))
chat = client.chat.create(model="grok-4.5")
chat.append(system("Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format."))
chat.append(
user("""
Vendor: Acme Corp, 123 Main St, Springfield, IL 62704
Invoice Number: INV-2025-001
Date: 2025-02-10
Items: - Widget A, 5 units, $10.00 each - Widget B, 2 units, $15.00 each
Total: $80.00 USD
""")
)
# The parse method returns a tuple of the full response object as well as the parsed pydantic object.
response, invoice = chat.parse(Invoice)
assert isinstance(invoice, Invoice)
# Can access fields of the parsed invoice object directly
print(invoice.vendor_name)
print(invoice.invoice_number)
print(invoice.invoice_date)
print(invoice.line_items)
print(invoice.total_amount)
print(invoice.currency)
# Can also access fields from the raw response object such as the content.
# In this case, the content is the JSON schema representation of the parsed invoice object
print(response.content)from openai import OpenAI
from pydantic import BaseModel, Field
from datetime import date
from enum import Enum
# Pydantic Schemas
class Currency(str, Enum):
USD = "USD"
EUR = "EUR"
GBP = "GBP"
class LineItem(BaseModel):
description: str = Field(description="Description of the item or service")
quantity: int = Field(description="Number of units", ge=1)
unit_price: float = Field(description="Price per unit", ge=0)
class Address(BaseModel):
street: str = Field(description="Street address")
city: str = Field(description="City")
postal_code: str = Field(description="Postal/ZIP code")
country: str = Field(description="Country")
class Invoice(BaseModel):
vendor_name: str = Field(description="Name of the vendor")
vendor_address: Address = Field(description="Vendor's address")
invoice_number: str = Field(description="Unique invoice identifier")
invoice_date: date = Field(description="Date the invoice was issued")
line_items: list[LineItem] = Field(description="List of purchased items/services")
total_amount: float = Field(description="Total amount due", ge=0)
currency: Currency = Field(description="Currency of the invoice")
client = OpenAI(
api_key="<YOUR_XAI_API_KEY_HERE>",
base_url="https://api.x.ai/v1",
)
completion = client.beta.chat.completions.parse(
model="grok-4.5",
messages=[
{"role": "system", "content": "Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format."},
{"role": "user", "content": """
Vendor: Acme Corp, 123 Main St, Springfield, IL 62704
Invoice Number: INV-2025-001
Date: 2025-02-10
Items:
- Widget A, 5 units, $10.00 each
- Widget B, 2 units, $15.00 each
Total: $80.00 USD
"""}
],
response_format=Invoice,
)
invoice = completion.choices[0].message.parsed
print(invoice)import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
import { z } from "zod";
const CurrencyEnum = z.enum(["USD", "EUR", "GBP"]);
const LineItemSchema = z.object({
description: z.string().describe("Description of the item or service"),
quantity: z.number().int().min(1).describe("Number of units"),
unit_price: z.number().min(0).describe("Price per unit"),
});
const AddressSchema = z.object({
street: z.string().describe("Street address"),
city: z.string().describe("City"),
postal_code: z.string().describe("Postal/ZIP code"),
country: z.string().describe("Country"),
});
const InvoiceSchema = z.object({
vendor_name: z.string().describe("Name of the vendor"),
vendor_address: AddressSchema.describe("Vendor's address"),
invoice_number: z.string().describe("Unique invoice identifier"),
invoice_date: z.string().date().describe("Date the invoice was issued"),
line_items: z.array(LineItemSchema).describe("List of purchased items/services"),
total_amount: z.number().min(0).describe("Total amount due"),
currency: CurrencyEnum.describe("Currency of the invoice"),
});
const client = new OpenAI({
apiKey: "<api key>",
baseURL: "https://api.x.ai/v1",
});
const completion = await client.chat.completions.parse({
model: "grok-4.5",
messages: [
{ role: "system", content: "Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format." },
{ role: "user", content: \`
Vendor: Acme Corp, 123 Main St, Springfield, IL 62704
Invoice Number: INV-2025-001
Date: 2025-02-10
Items:
- Widget A, 5 units, $10.00 each
- Widget B, 2 units, $15.00 each
Total: $80.00 USD
\` },
],
response_format: zodResponseFormat(InvoiceSchema, "invoice"),
});
const invoice = completion.choices[0].message.parsed;
console.log(invoice);import { xai } from '@ai-sdk/xai';
import { generateText, Output } from 'ai';
import { z } from 'zod';
const CurrencyEnum = z.enum(['USD', 'EUR', 'GBP']);
const LineItemSchema = z.object({
description: z.string().describe('Description of the item or service'),
quantity: z.number().int().min(1).describe('Number of units'),
unit_price: z.number().min(0).describe('Price per unit'),
});
const AddressSchema = z.object({
street: z.string().describe('Street address'),
city: z.string().describe('City'),
postal_code: z.string().describe('Postal/ZIP code'),
country: z.string().describe('Country'),
});
const InvoiceSchema = z.object({
vendor_name: z.string().describe('Name of the vendor'),
vendor_address: AddressSchema.describe("Vendor's address"),
invoice_number: z.string().describe('Unique invoice identifier'),
invoice_date: z.string().date().describe('Date the invoice was issued'),
line_items: z
.array(LineItemSchema)
.describe('List of purchased items/services'),
total_amount: z.number().min(0).describe('Total amount due'),
currency: CurrencyEnum.describe('Currency of the invoice'),
});
const result = await generateText({
model: xai.responses('grok-4.5'),
output: Output.object({ schema: InvoiceSchema }),
system:
'Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format.',
prompt: \`
Vendor: Acme Corp, 123 Main St, Springfield, IL 62704
Invoice Number: INV-2025-001
Date: 2025-02-10
Items:
- Widget A, 5 units, $10.00 each
- Widget B, 2 units, $15.00 each
Total: $80.00 USD
\`,
});
console.log(result._output);步骤 4:类型安全的输出
使用支持的 schema 功能时,输出将是类型安全的并遵循输入 schema。
{
"vendor_name": "Acme Corp",
"vendor_address": {
"street": "123 Main St",
"city": "Springfield",
"postal_code": "62704",
"country": "IL"
},
"invoice_number": "INV-2025-001",
"invoice_date": "2025-02-10",
"line_items": [
{ "description": "Widget A", "quantity": 5, "unit_price": 10.0 },
{ "description": "Widget B", "quantity": 2, "unit_price": 15.0 }
],
"total_amount": 80.0,
"currency": "USD"
}使用工具的结构化输出
NOTE
使用工具的结构化输出仅适用于支持的 Grok 4 系列模型。
您可以将结构化输出与工具调用结合使用,从工具增强的查询中获取类型安全的响应。这适用于两种情况:
这种组合使工作流程成为可能,模型可以使用工具收集信息并以可预测的强类型格式返回结果。
示例:带有结构化输出的智能体工具
此示例使用网络搜索查找某个主题的最新研究,并将结构化数据提取到 schema 中:
from pydantic import BaseModel, Field
class ProofInfo(BaseModel):
name: str = Field(description="Name of the proof or paper")
authors: str = Field(description="Authors of the proof")
year: str = Field(description="Year published")
summary: str = Field(description="Brief summary of the approach")import { z } from "zod";
const ProofInfoSchema = z.object({
name: z.string().describe("Name of the proof or paper"),
authors: z.string().describe("Authors of the proof"),
year: z.string().describe("Year published"),
summary: z.string().describe("Brief summary of the approach"),
});import os
from pydantic import BaseModel, Field
from xai_sdk import Client
from xai_sdk.chat import user
from xai_sdk.tools import web_search
# ProofInfo schema defined above
client = Client(api_key=os.getenv("XAI_API_KEY"))
chat = client.chat.create(
model="grok-4.5",
tools=[web_search()],
)
chat.append(user("Find the latest machine-checked proof of the four color theorem."))
response, proof = chat.parse(ProofInfo)
print(f"Name: {proof.name}")
print(f"Authors: {proof.authors}")
print(f"Year: {proof.year}")
print(f"Summary: {proof.summary}")import os
from openai import OpenAI
from pydantic import BaseModel, Field
# ProofInfo schema defined above
client = OpenAI(
api_key=os.getenv("XAI_API_KEY"),
base_url="https://api.x.ai/v1",
)
response = client.responses.parse(
model="grok-4.5",
input="Find the latest machine-checked proof of the four color theorem.",
tools=[
{"type": "web_search"}
],
text_format=ProofInfo,
)
proof = response.output_parsed
print(f"Name: {proof.name}")
print(f"Authors: {proof.authors}")
print(f"Year: {proof.year}")
print(f"Summary: {proof.summary}")import OpenAI from "openai";
import { zodResponseFormat } from "openai/helpers/zod";
import { z } from "zod";
// ProofInfoSchema defined above
const client = new OpenAI({
apiKey: process.env.XAI_API_KEY,
baseURL: "https://api.x.ai/v1",
});
// Convert Zod schema to JSON schema format
const format = zodResponseFormat(ProofInfoSchema, "proof_info");
const response = await client.responses.create({
model: "grok-4.5",
input: "Find the latest machine-checked proof of the four color theorem.",
tools: [
{ type: "web_search" }
],
text: {
format: {
type: "json_schema",
name: format.json_schema.name,
schema: format.json_schema.schema,
strict: true,
}
}
});
// Find the message in the output array
const message = response.output.find((item) => item.type === "message");
const textContent = message?.content?.find((c) => c.type === "output_text");
if (textContent) {
const proof = JSON.parse(textContent.text);
console.log(`Name: ${proof.name}`);
console.log(`Authors: ${proof.authors}`);
console.log(`Year: ${proof.year}`);
console.log(`Summary: ${proof.summary}`);
}示例:带有结构化输出的客户端工具
此示例使用客户端函数工具计算 Collatz 序列步骤,并以结构化格式返回结果:
from pydantic import BaseModel, Field
class CollatzResult(BaseModel):
starting_number: int = Field(description="The input number")
steps: int = Field(description="Number of steps to reach 1")const CollatzResultSchema = {
type: "object",
properties: {
starting_number: { type: "integer", description: "The input number" },
steps: { type: "integer", description: "Number of steps to reach 1" },
},
required: ["starting_number", "steps"],
additionalProperties: false,
};import os
import json
from pydantic import BaseModel, Field
from xai_sdk import Client
from xai_sdk.chat import tool, tool_result, user
# CollatzResult schema defined above
def collatz_steps(n: int) -> int:
"""Returns the number of steps for n to reach 1 in the Collatz sequence."""
steps = 0
while n != 1:
n = n // 2 if n % 2 == 0 else 3 * n + 1
steps += 1
return steps
collatz_tool = tool(
name="collatz_steps",
description="Compute the number of steps for a number to reach 1 in the Collatz sequence",
parameters={
"type": "object",
"properties": {
"n": {"type": "integer", "description": "The starting number"},
},
"required": ["n"],
},
)
client = Client(api_key=os.getenv("XAI_API_KEY"))
chat = client.chat.create(
model="grok-4.5",
tools=[collatz_tool],
)
chat.append(user("Use the collatz_steps tool to find how many steps it takes for 20250709 to reach 1."))
# Handle tool calls until we get a final response
while True:
response = chat.sample()
if not response.tool_calls:
break
chat.append(response)
for tc in response.tool_calls:
args = json.loads(tc.function.arguments)
result = collatz_steps(args["n"])
chat.append(tool_result(str(result)))
# Parse the final response into structured output
response, result = chat.parse(CollatzResult)
print(f"Starting number: {result.starting_number}")
print(f"Steps to reach 1: {result.steps}")import os
import json
from openai import OpenAI
from pydantic import BaseModel, Field
# CollatzResult schema defined above
def collatz_steps(n: int) -> int:
"""Returns the number of steps for n to reach 1 in the Collatz sequence."""
steps = 0
while n != 1:
n = n // 2 if n % 2 == 0 else 3 * n + 1
steps += 1
return steps
client = OpenAI(
api_key=os.getenv("XAI_API_KEY"),
base_url="https://api.x.ai/v1",
)
tools = [
{
"type": "function",
"function": {
"name": "collatz_steps",
"description": "Compute the number of steps for a number to reach 1 in the Collatz sequence",
"parameters": {
"type": "object",
"properties": {
"n": {"type": "integer", "description": "The starting number"},
},
"required": ["n"],
},
},
}
]
messages = [
{"role": "user", "content": "Use the collatz_steps tool to find how many steps it takes for 20250709 to reach 1."}
]
# Handle tool calls until we get a final response
while True:
completion = client.chat.completions.create(
model="grok-4.5",
messages=messages,
tools=tools,
)
message = completion.choices[0].message
if not message.tool_calls:
break
messages.append(message)
for tc in message.tool_calls:
args = json.loads(tc.function.arguments)
result = collatz_steps(args["n"])
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": str(result),
})
# Final call with structured output
completion = client.beta.chat.completions.parse(
model="grok-4.5",
messages=messages,
response_format=CollatzResult,
)
result = completion.choices[0].message.parsed
print(f"Starting number: {result.starting_number}")
print(f"Steps to reach 1: {result.steps}")import OpenAI from "openai";
// CollatzResultSchema defined above
function collatzSteps(n) {
let steps = 0;
while (n !== 1) {
n = n % 2 === 0 ? n / 2 : 3 * n + 1;
steps++;
}
return steps;
}
const client = new OpenAI({
apiKey: process.env.XAI_API_KEY,
baseURL: "https://api.x.ai/v1",
});
const tools = [
{
type: "function",
function: {
name: "collatz_steps",
description: "Compute the number of steps for a number to reach 1 in the Collatz sequence",
parameters: {
type: "object",
properties: {
n: { type: "integer", description: "The starting number" },
},
required: ["n"],
},
},
},
];
let messages = [
{ role: "user", content: "Use the collatz_steps tool to find how many steps it takes for 20250709 to reach 1." }
];
// Handle tool calls until we get a final response
while (true) {
const completion = await client.chat.completions.create({
model: "grok-4.5",
messages,
tools,
});
const message = completion.choices[0].message;
if (!message.tool_calls) {
break;
}
messages.push(message);
for (const tc of message.tool_calls) {
const args = JSON.parse(tc.function.arguments);
const result = collatzSteps(args.n);
messages.push({
role: "tool",
tool_call_id: tc.id,
content: String(result),
});
}
}
// Final call with structured output
const completion = await client.chat.completions.create({
model: "grok-4.5",
messages,
response_format: {
type: "json_schema",
json_schema: {
name: "collatz_result",
schema: CollatzResultSchema,
strict: true,
},
},
});
const result = JSON.parse(completion.choices[0].message.content);
console.log("Starting number:", result.starting_number);
console.log("Steps to reach 1:", result.steps);替代方案:将 response_format 与 sample() 或 stream() 一起使用
使用 xAI Python SDK 时,有另一种方法可以检索结构化输出。除了使用 parse() 方法外,您还可以在创建聊天时将 Pydantic 模型直接传递给 response_format 参数,然后使用 sample() 或 stream() 获取响应。
工作原理
当您将 Pydantic 模型传递给 response_format 时,SDK 会自动:
- 将您的 Pydantic 模型转换为 JSON schema
- 将模型的输出限制为符合该 schema
- 将响应作为符合 Pydantic 模型的 JSON 字符串返回到
response.content
然后您手动将 JSON 字符串解析为 Pydantic 模型实例。
主要区别
| 方法 | 方法 | 返回值 | 解析 |
|---|---|---|---|
使用 parse() | chat.parse(Model) | (Response, Model) 元组 | 自动 - SDK 为您解析 |
使用 response_format | chat.sample() 或 chat.stream() | 带有 JSON 字符串的 Response | 手动 - 您解析 response.content |
何时使用每种方法
- 使用
parse()当您想要最简单、最便捷的体验和自动解析时 - 使用
response_format+sample()或stream()当您:- 想要对解析过程有更多控制
- 需要在解析前处理原始 JSON 字符串
- 想要使用流式结构化输出
- 正在与期望使用
sample()或stream()的现有代码集成时
使用 response_format 的示例
import os
from datetime import date
from enum import Enum
from pydantic import BaseModel, Field
from xai_sdk import Client
from xai_sdk.chat import system, user
# Pydantic Schemas
class Currency(str, Enum):
USD = "USD"
EUR = "EUR"
GBP = "GBP"
class LineItem(BaseModel):
description: str = Field(description="Description of the item or service")
quantity: int = Field(description="Number of units", ge=1)
unit_price: float = Field(description="Price per unit", ge=0)
class Address(BaseModel):
street: str = Field(description="Street address")
city: str = Field(description="City")
postal_code: str = Field(description="Postal/ZIP code")
country: str = Field(description="Country")
class Invoice(BaseModel):
vendor_name: str = Field(description="Name of the vendor")
vendor_address: Address = Field(description="Vendor's address")
invoice_number: str = Field(description="Unique invoice identifier")
invoice_date: date = Field(description="Date the invoice was issued")
line_items: list[LineItem] = Field(description="List of purchased items/services")
total_amount: float = Field(description="Total amount due", ge=0)
currency: Currency = Field(description="Currency of the invoice")
client = Client(api_key=os.getenv("XAI_API_KEY"))
# Pass the Pydantic model to response_format instead of using parse()
chat = client.chat.create(
model="grok-4.5",
response_format=Invoice, # Pass the Pydantic model here
)
chat.append(system("Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format."))
chat.append(
user("""
Vendor: Acme Corp, 123 Main St, Springfield, IL 62704
Invoice Number: INV-2025-001
Date: 2025-02-10
Items: - Widget A, 5 units, $10.00 each - Widget B, 2 units, $15.00 each
Total: $80.00 USD
""")
)
# Use sample() instead of parse() - returns Response object
response = chat.sample()
# The response.content is a valid JSON string conforming to your schema
print(response.content)
# Output: {"vendor_name": "Acme Corp", "vendor_address": {...}, ...}
# Manually parse the JSON string into your Pydantic model
invoice = Invoice.model_validate_json(response.content)
assert isinstance(invoice, Invoice)
# Access fields of the parsed invoice object
print(invoice.vendor_name)
print(invoice.invoice_number)
print(invoice.total_amount)流式结构化输出
您也可以将 stream() 与 response_format 一起使用来获取流式结构化输出。这些块将逐步构建 JSON 字符串:
import os
from pydantic import BaseModel, Field
from xai_sdk import Client
from xai_sdk.chat import system, user
class Summary(BaseModel):
title: str = Field(description="A brief title")
key_points: list[str] = Field(description="Main points from the text")
sentiment: str = Field(description="Overall sentiment: positive, negative, or neutral")
client = Client(api_key=os.getenv("XAI_API_KEY"))
chat = client.chat.create(
model="grok-4.5",
response_format=Summary, # Pass the Pydantic model here
)
chat.append(system("Analyze the following text and provide a structured summary."))
chat.append(user("The new product launch exceeded expectations with record sales..."))
# Stream the response - chunks contain partial JSON
for response, chunk in chat.stream():
print(chunk.content, end="", flush=True)
# Parse the complete JSON string into your model
summary = Summary.model_validate_json(response.content)
print(f"Title: {summary.title}")
print(f"Sentiment: {summary.sentiment}")