跳转至

结构化输出

仅在提示词中要求“请返回 JSON”并不可靠:模型可能附加解释文字、套上代码块,甚至擅自修改字段名。 output_type 用明确的契约取代模糊的期望:运行的最终答案会被解析并校验为声明的类型;若校验失败, 系统会在有限次数的修复尝试后明确报错。

from pydantic import BaseModel

from lovia import Agent, Runner


class Brief(BaseModel):
    title: str
    bullets: list[str]


agent = Agent(name="summarizer", model="<model>", output_type=Brief)

result = await Runner.run(agent, "给 Python 开发者总结 lovia。")
print(result.output.title)          # 类型化访问:result.output 是 Brief

接受哪些类型

任何 lovia 能构建 JSON Schema、并能转换为目标值的类型都可以:

  • Pydantic model(能力最强:约束、自定义 validator)
  • dataclass 和 TypedDict
  • 普通类型和容器,如 list[str]dict[str, int]Literal[...]、union、 intbool
  • str:默认值,表示自由文本,不做任何解析

运行过程中模型仍然可以调用工具;契约只作用于结束运行的最终消息。

每次运行覆盖

agent 上的 output_type 是默认值;某次运行也可以覆盖:

result = await Runner.run(agent, "返回一份发布 checklist。", output_type=list[str])

覆盖项作用于整次运行。经过 handoff 后,目标 agent 也继承这个覆盖; 如果没有覆盖,每个 agent 使用自己声明的 output_type

如何向模型提供 Schema

lovia 会按 provider 自动选择两种策略:

  • 原生接口:支持结构化输出的 provider(OpenAI response_format、Anthropic output format)会在请求里收到 JSON Schema,并由服务端约束。
  • Prompt fallback:其他 provider 会让 lovia 在 system prompt 后追加一个 “Output format” 块,要求模型只回复一个符合 schema 的 JSON 文档。它放在 system prompt 中,而不是合成工具里,所以不管上下文长度和工具数量如何,这个要求都能保持可见。

无论哪种方式,解析都会先宽松、再严格:先尝试原始文本,再剥掉 markdown code fence, 再从周围说明文字中提取第一个平衡的 JSON object/array。之后才按你的类型校验。

自动修复

最终消息解析或校验失败时,由 Agent 的 output_repair 策略决定后续处理方式:

  • True(默认):Runner 追加一条包含校验错误的用户提示词,让模型重新生成 一次。第二次失败才抛异常。
  • False:快速失败,立即抛 OutputValidationError
  • 一个 OutputRepairStrategy:你自己的策略:
class PatientRepair:
    def build_prompt(self, exc, attempt):
        if attempt > 3:
            return None            # 放弃:重新抛出错误
        return f"第 {attempt} 次失败:{exc}。只回复 JSON。"

agent = Agent(..., output_type=Brief, output_repair=PatientRepair())

build_prompt 接收 OutputValidationError 和 1-based 尝试次数;返回 None 表示停止重试。每次修复都会消耗一个正常 turn(计入 max_turns 和预算)。

OutputValidationError 携带 raw(模型实际输出的片段)和 output_type_name, 通常足够你只靠日志定位长期不匹配的问题。

注意事项

  • output_type=str 表示“没有契约”,不是“校验它是字符串”。此时一切都是字符串, 修复永远不会触发。
  • schema 越复杂,模型越容易不稳定。 深层嵌套 union 和开放式 dict[str, Any] 字段会在解析器出问题前就降低模型遵从度。扁平、明确、带字段描述的 model 最容易通过校验。
  • 空回复也会进入修复流程(因为没有内容可解析)。如果你看到修复循环最后以 OutputValidationError 结束,且 raw 为空,请检查 finish_reason,通常是 max_tokens 截断,不是模型不听话。

延伸阅读