结构化输出
BoundModel.generate_structured() 从一次 LLM 调用中提取强类型、经过
校验的数据。传入一个 Pydantic 模型类,拿回一个已校验的实例——不用
解析 JSON、也不用手写 schema。
from pydantic import BaseModel
from cubepi.providers.base import TextContent, UserMessage
class Sentiment(BaseModel):
label: str
confidence: float
result = await model.generate_structured(
Sentiment,
messages=[UserMessage(content=[TextContent(text="I love this product!")])],
)
print(result) # label='positive' confidence=0.95
工作方式
底层 generate_structured() 做的事:
- 把 Pydantic 模型的 JSON schema 转换成一个合成的 tool 定义。
- 用
tools=[synthetic_tool]和tool_choice=tool_name调用generate(),强制模型调用这个 tool。 - 从响应里取出
ToolCall.arguments。 - 通过
output_type.model_validate()校验。
这和 pydantic-ai 默认使用的方式(ToolOutput 模式)一样。它对所有
provider 都能用,因为每个 LLM API 都支持 tool 调用。
参数
await model.generate_structured(
output_type, # Pydantic 模型类(必填)
messages, # list[Message](必填)
*,
system_prompt="", # 可选的 system prompt(永远会追加一段使用 tool 的提示)
tool_name="structured_output",
tool_description="Return the structured output",
max_output_tokens=None,
temperature=None,
max_retries=1, # Pydantic 校验失败时的重试次数(把错误回灌给模型)
)
| 参数 | 默认 | 说明 |
|---|---|---|
output_type | 必填 | Pydantic BaseModel 子类 |
messages | 必填 | 对话消息 |
system_prompt | "" | 自定义 system prompt(永远会追加一段使用 tool 的提示) |
tool_name | "structured_output" | 合成 tool 的名称 |
tool_description | "Return the structured output" | 发给模型的 tool 描述 |
max_retries | 1 | Pydantic 校验失败时的重试次数 |
max_output_tokens | None | 覆盖模型的默认 max tokens |
temperature | None | 覆盖模型的默认 temperature |
错误处理
generate_structured() 在两种情况下抛 StructuredOutputError:
from cubepi.providers.base import StructuredOutputError
try:
result = await model.generate_structured(MySchema, messages=[...])
except StructuredOutputError as e:
print(e) # "no tool call" 或 "validation failed after retries"
- No tool call:模型返回了文本而不是调用 tool。
- Validation failed:所有尝试都通不过 Pydantic 校验(首次 + 重试)。
校验失败时,错误会作为 UserMessage 回灌给模型,给它再一次机会
(最多 max_retries 次)。
tool_choice
generate_structured() 内部用 tool_choice 强制模型调用合成 tool。
你也可以在 stream() 和 generate() 上直接用 tool_choice:
reply = await model.generate(
messages=[...],
tools=[my_tool_def],
tool_choice="my_tool", # 强制使用这个 tool
)
可选值:
| 值 | 行为 |
|---|---|
None | Provider 默认(模型自决) |
"auto" | 模型自己决定是否调用 tool |
"required" | 必须调用某个 tool |
"none" | 不允许 tool 调用 |
"tool_name" | 强制使用指定名称的 tool |
tool_choice 在所有内置 provider(Anthropic、OpenAI、OpenAI Responses)
上都能用。每个 provider 会把这个值映射到它原生的 wire 格式。