Pydantic AI 把引用变成统一字段
2.61.0 将多家模型的来源信息归一到 TextPart.citations,但结构化输出和流式时序仍需单独处理。
正文
Pydantic AI 2.61.0 把不同模型返回的来源信息归一到了 TextPart.citations,应用可以用同一套代码读取来源、摘录和它们对应的回答片段;但这不是“所有模型都自动有引用”,也不是把结构化输出强行变成可追溯结果。真正需要改的是结果渲染和消息持久化:文本回答可以挂引用,结构化工具输出通常不能;流式事件里引用还可能晚于文本到达,必须以最终消息为准。
这次具体增加了什么
Pydantic AI 2.61.0 的发布说明列出了结构化引用、多个提供方的引用归一化,以及在 Vercel AI 适配器中保留引用等变更。对应实现把引用放在文本消息的 TextPart.citations 上,而不是让业务代码分别解析 Anthropic 的文档引用、Gemini 的 grounding 元数据或 OpenAI 的 annotation。对应用来说,最大的变化不是模型突然更会回答,而是“模型为什么这样回答”从供应商私有字段变成了框架层可读取的数据结构。
一个引用由来源和锚点组成。来源可以是网页,也可以是用户上传的文档或模型提供方文件库中的文件;来源还可能带有标题、URL 和摘录。锚点描述引用对应回答中的哪一段:有的提供方能够给出回答文本中的字符范围,有的只知道某个链接或标记,有的完全不给位置。位置缺失时,应用不能把引用错误地绑定到某一句话,应该把它展示为“本次回答使用的来源”,而不是制造精确到句子的假象。
最小读取逻辑可以保持在框架层,不依赖具体提供方:
from pydantic_ai import Agent, TextPart
from pydantic_ai.capabilities import NativeTool
from pydantic_ai.native_tools import WebSearchTool
agent = Agent(
'openai:your-model',
capabilities=[NativeTool(WebSearchTool())],
)
result = agent.run_sync('查询项目依赖的官方文档,并说明版本限制。')
for part in result.response.parts:
if isinstance(part, TextPart):
for citation in part.citations or []:
for source in citation.sources:
print(source.url, source.title, source.excerpts)
这段代码的重点是读取 TextPart,而不是假设答案一定只有一个字符串。应用如果在服务端直接取 result.output,往往会错过引用;如果在前端只渲染纯文本,也会把已经得到的来源信息丢掉。可以先在 模型与平台目录 中确认实际接入的模型能力,再决定界面是否展示可点击来源、摘录或调试元数据。
不同提供方不是同一种引用
统一字段解决的是读取接口,不会抹平上游语义。Pydantic AI 的引用文档把提供方差异直接列了出来,接入时仍应按来源类型和锚点能力设计界面。
Anthropic 的文档引用面向用户提供的文本、PDF 和自定义内容块。官方文档说明,启用引用后,响应会返回支撑回答的文档位置;纯文本可以按字符范围定位,PDF 可以按页码范围定位,自定义内容则按内容块索引定位。文档切分粒度也会影响引用粒度:默认的纯文本和 PDF 会按句子切分,如果检索系统已经把内容切成适合展示的 RAG 块,可以使用自定义内容块来控制边界。这样做的实际影响是,引用质量不只由模型决定,还取决于送入模型的文档边界。
Gemini API 的 Google Search 能返回 grounding 相关信息,包含搜索结果片段与回答支撑关系;OpenAI 的 Web Search 则在消息内容中提供 annotations,其中的 url_citation 描述被引用的 URL、标题和位置。Pydantic AI 将这些差异映射成相同的 Citation 结构,但保留提供方返回的额外数据。文档明确提醒,Google 的置信度等信息会留在 provider_details['citations'] 中,字段形状属于提供方协议,应用不应把它当作跨供应商稳定接口。
Amazon Bedrock 的 API 参考则把 CitationsContentBlock 定义为同时包含生成文本和引用信息的内容块,引用数组指向支撑相关内容的源文档。Pydantic AI 的支持表进一步限定了适用范围:Bedrock 上不是所有模型都支持这一设置,Anthropic 模型的文档引用和其他 Bedrock 模型不能简单视为同一能力。接入层如果允许用户切换提供方,应把“支持引用”作为能力检查结果,而不是仅根据模型名称推断。
因此,统一渲染器至少需要处理三种状态:有 URL 且有锚点、有来源但没有锚点、只有提供方原始元数据。第一种可以在回答对应位置显示链接;第二种适合在回答末尾显示来源卡片;第三种应该保留在调试日志或审计记录中,先确认字段含义后再面向用户展示。不要因为对象里存在 sources 就宣称每条事实都已经被精确证明,多个来源也可能只是提供方归并出的相近材料。
结构化输出是最容易踩的边界
引用挂在文本上,结构化输出通常通过工具调用参数承载,因此二者并不天然兼容。Pydantic AI 文档明确说明,使用结构化输出时,包含 str 与结构化类型的联合输出也会走工具调用参数;工具调用参数不能承载 TextPart.citations。在 Anthropic 以及 Bedrock 的 Anthropic 模型上,引用还可能需要把标记写入非文本字段,这会破坏本应干净的 JSON 字段,所以框架会关闭这类请求的文档引用,某些原生结构化输出组合则由提供方直接拒绝。
这意味着“我需要 JSON,又需要每个字段都有来源”不能只靠打开 Citations() 解决。可以按业务选择以下方案:
- 答案需要可读引用时使用文本输出。 让模型返回文本和引用,再由应用解析或展示引用;适合知识问答、研究摘要和面向人的解释。
- 下游必须消费严格 JSON 时把来源建模为业务字段。 例如让模型输出
claims、evidence和source_id,再由应用用检索结果或工具返回值校验这些字段。这里的来源关系是应用协议,不应误称为框架自动生成的TextPart.citations。 - 两种结果都要时拆成两个步骤。 第一步生成结构化候选,第二步以文本形式对候选和原始文档做带引用的核验。这样会增加一次模型调用和状态管理,但比把引用标记混进 JSON 更可控。
还要注意工具选择路径。Pydantic AI 的文档指出,Anthropic 在模型自行选择工具时,引用标记可能进入工具参数;如果工具参数最终会成为邮件正文、数据库内容或用户可见字段,就可能把内部标记一起写出去。对于强制工具调用、文档引用和需要干净参数的组合,应该先在测试环境验证请求是否被框架拦截或被提供方返回 400,而不是等生产数据出现不可解析的标记。
流式渲染不能只看文本增量
引用的到达时序是另一个容易被忽略的变化。Pydantic AI 的文档说明,Anthropic、Bedrock 和 OpenAI Responses 可能在文本块结束之后补充引用,Google 和 OpenAI Chat 可能在整个响应结束时才提供引用。也就是说,用户已经看到完整答案时,前端还可能没有拿到最终来源;如果在收到最后一个文本增量后立即关闭连接,引用就会被截断。
流式接口应把“文本可见”和“消息完成”分开处理。文本增量可以立即显示,引用则在最终响应或文本块完成事件中合并;服务端应保留最终消息对象,至少等到模型响应结束、工具步骤结束以及框架完成引用归一化之后再持久化。前端如果只接收纯文本事件,可以增加一个完成事件,携带规范化后的引用列表;不要让前端根据 URL 或 [1] 标记自行猜测引用关系。
消息历史也有边界。Pydantic AI 会保存引用,但只有部分提供方会在历史回传时继续发送引用,例如 Anthropic 的 Web Search 引用和带消息 ID 的 OpenAI Responses 网页或文件引用。发送给其他提供方、文本被编辑或文本被合并后的引用,可能只保留在本地历史,回传给模型时变成普通文本。编辑保存过的回答时,应同时清除对应的 citations 和 provider_details['citations'];如果只改文字不清元数据,旧的字符位置可能仍然看起来合法,却已经指向错误内容。
对于使用 Vercel AI 适配器的应用,2.61.0 还补上了引用在前端消息历史中的保留路径。实际接入仍应检查适配器和 UI 版本,因为 Pydantic AI 文档同时指出,AG-UI 适配器不会把引用发送到前端。不能仅凭服务端对象中存在引用,就推断浏览器端一定能显示。
一套可落地的接入检查
第一步是定义产品语义。回答中的链接、来源列表、原文摘录和审计元数据不是同一件事。面向用户的页面应过滤来源 URL、标题和摘录,避免把用户上传文档中的敏感内容直接写进日志;内部调试则可以保留提供方原始 provider_details,但要记录模型、提供方、适配器版本和请求时间。
第二步是做能力矩阵。至少记录“是否产生引用”“是否有回答锚点”“是否支持文档引用”“是否能回传历史”“流式引用在哪个事件到达”五列。用同一套测试提示分别跑 Anthropic、Gemini、OpenAI、Bedrock 和实际使用的中转入口。中转或兼容接口即使接受相同请求,也可能丢掉 annotation、grounding 或文档位置;这属于响应兼容性问题,应在 相关平台页面 和具体站点文档中逐项核对,而不是按接口名称推断。
第三步是覆盖失败路径。测试没有来源、来源没有位置、文本被合并、流式提前取消、工具参数包含引用标记、结构化输出与引用同时开启、历史换提供方以及编辑回答后重新发送等场景。验证标准不应只是“页面出现了一个链接”,还要确认链接确实对应来源、锚点没有超出文本范围、引用不会泄漏用户文档、JSON 仍能被严格解析。
第四步是把结果存储成可迁移的数据。推荐保存原始消息、规范化引用、提供方原始字段和生成时的版本信息;不要只保存渲染后的 [1]。如果应用未来从 Pydantic AI 切换到另一套 SDK,至少还能根据 URL、文档 ID、摘录和锚点重新生成 UI。对于已有的流式故障,还可以参考站内的 Pydantic AI 流式请求卡死排查,区分“引用没有到达”和“流本身没有正确释放”这两类不同问题。
结论很具体:2.61.0 让多家模型的引用可以通过 TextPart.citations 统一读取,适合把来源处理从供应商分支收敛到应用层;但它没有消除提供方能力差异,也没有让结构化输出自动获得可验证证据。把文本引用、结构化数据、流式结束事件和消息历史分别建模,才是这次升级对开发者真正有价值的用法。
参考来源
- Pydantic AI 2.61.0 发布说明
- Pydantic AI 结构化引用文档
- Pydantic AI 引用归一化 PR #7434
- Anthropic Citations 官方文档
- Google Gemini API:Grounding with Google Search
- OpenAI Web Search 官方文档
- Amazon Bedrock CitationsContentBlock API 参考
以上来源于 2026 年 10 月 27 日打开并核对原文;Pydantic AI 版本结论以 2.61.0 发布说明和对应版本文档为准。