DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Pydantic AI 调 Gemini 偶发 different event loop,先看客户端活了多久

Pydantic AI 调 Gemini 出现 different event loop 时,检查异步客户端是否被跨循环复用。核对包含已确认修复的兼容版本,并验证真实 worker 的连续、并发和取消场景。

Pydantic AI 调 Gemini 偶发 different event loop,先看客户端活了多久

单次调用 Gemini 正常,Celery worker 连续处理任务后偶尔出现:

is bound to a different event loop

堆栈如果经过 google-genai、httpx、httpcore,最后停在 asyncio 的 Event 或 Lock,值得先检查异步 HTTP 客户端是否跨了事件循环。加重试不一定有用:重试仍然可能拿到同一个已绑定旧 loop 的对象。

Pydantic AI 官方 issue #3240 的初始报告涉及从 1.1.0 升级到 1.4.0,后续有 Celery 并发场景反馈。维护者在结帖说明中将一类根因指向全局缓存的 AsyncClient,并确认 #4421 随 v1.100.0 移除了该缓存。这个版本结论只覆盖库里已确认的那条路径,不能据此判断所有同名异常都是同一个 bug。

先在实际 worker 里看版本

不要只在本地终端运行 pip show。记录发生错误的 worker 镜像中 pydantic-ai 或 pydantic-ai-slim、google-genai、httpx 的版本。如果仍在受影响的旧组合上,选择包含修复且与项目兼容的版本,在测试环境更新锁文件并回归。

源码升级之后,还要搜应用里的全局对象:模块级 AsyncClient、长期存活的 provider、封装后缓存的 agent,以及每个任务里新建 loop 的同步包装。应用自己复用的客户端,不会因为库删除了自己的缓存就自动变安全。

让一次任务拥有自己的异步资源

下方接口按 v1.100.0 的源码核对,采用 GoogleModel + GoogleProvider,用 agent 的异步上下文管理资源。适合一个同步任务入口拥有一次完整异步生命周期的场景;需安装带 Google 支持的 Pydantic AI,并配置 GOOGLE_API_KEY 和 GOOGLE_MODEL。运行会请求 Gemini,并可能产生费用。

import asyncio
import os
from pydantic_ai import Agent
from pydantic_ai.models.google import GoogleModel
from pydantic_ai.providers.google import GoogleProvider

async def run_one(prompt: str) -> str:
    provider = GoogleProvider(api_key=os.environ["GOOGLE_API_KEY"])
    model = GoogleModel(os.environ["GOOGLE_MODEL"], provider=provider)
    agent = Agent(model)
    async with agent:
        result = await agent.run(prompt)
        return result.output

def run_task(prompt: str) -> str:
    # 用于没有正在运行的事件循环的同步任务入口。
    return asyncio.run(run_one(prompt))

if __name__ == "__main__":
    print(run_task("只回答 OK"))
    print(run_task("只回答 OK"))

两个连续调用会分别创建和关闭 loop,也分别创建 provider。把 provider 提到模块顶部缓存起来,会破坏示例要验证的隔离关系。若自行注入 httpx.AsyncClient,同样要在该 loop 内创建,并在 loop 结束前关闭。

这个写法有连接重建成本。长期异步服务可以在一个稳定的 loop 里用应用生命周期管理客户端,没必要每次请求都重建。Celery 的进程、线程或其他并发池行为也不相同,应按实际部署方式决定对象的归属,避免在父进程预先创建连接再由子进程沿用。

怎样证明偶发错误真消失了

至少覆盖三个场景:同一 worker 连续处理多个任务;按照线上并发池运行并发任务;任务取消或失败后再处理新任务。关注未关闭客户端、连接池关闭后复用等警告。

本地先跑两个独立 loop 能暴露一部分生命周期问题,但不能代替真实 Celery 并发回归。若升级和隔离后仍失败,记录进程 ID、线程 ID、loop 标识及对象创建位置,再缩小到最小复现;不要把密钥或完整业务提示词写进诊断日志。

官方依据:问题 #3240 及维护者结帖说明、修复 #4421、Google 模型与 Provider 文档。