用 Pydantic AI 构建多工具天气 Agent:从函数工具链到 Gradio 聊天界面的完整实战 📅 发布时间:2026/9/13 10:43:05 👁 浏览次数: 用 Pydantic AI 构建多工具天气 Agent从函数工具链到 Gradio 聊天界面的完整实战【免费下载链接】pydantic-aiHow Python does AI. Agents, realtime voice, image generation, embeddings. Every model, every interface, typed end to end.项目地址: https://gitcode.com/GitHub_Trending/py/pydantic-ai本篇指南以 Pydantic AI 官方示例——天气 Agentweather_agent为蓝本完整讲解如何用多个函数工具让 LLM 按顺序调用先地理编码、再查天气、如何通过依赖注入传入 HTTP 客户端以及如何用run_stream流式输出并用 Gradio 在单个 Python 文件中搭出多轮聊天 UI。读完本文你将能独立复刻一个多工具协作 流式响应 聊天界面的完整 Agent 应用。示例概览一次需要多工具协作的天气查询天气 Agent 的核心场景非常典型用户可能同时询问多个地点的天气例如 What is the weather like in London and in Wiltshire?此时模型无法凭自身知识给出准确数据必须依次调用工具获取信息调用get_lat_lng工具把地点描述转换为经纬度调用get_weather工具根据经纬度查询天气与温度汇总结果流式输出最终回答。这正是 Pydantic AI 函数工具Function Tools的典型用法——把 LLM 无法内置的外部信息地理编码、天气数据通过工具调用来补齐让模型的行为更确定、更可靠。官方文档对 tools 有完整说明工具本质上就是 RAG 中的那个 R检索增强它让模型能够主动请求额外信息来完成回答。该示例同时演示了 Pydantic AI 的四项核心技术也是本文接下来逐一展开的主线函数工具tools见 toolsAgent 依赖注入dependencies见 dependencies流式文本响应streaming text见 output.md#streaming-text用 Gradio 构建 Agent 聊天 UI。运行环境与准备安装依赖该示例随pydantic-ai发行版一起分发。你可以通过克隆仓库或直接安装pydantic-ai来获得示例代码。运行示例需要安装examples可选依赖组其中包含logfire、httpx等运行示例所需的第三方库若已通过 pip/uv 安装pydantic-ai安装 extra 依赖pip/uv-add pydantic-ai[examples]若克隆了仓库则在仓库根目录执行uv sync --extra examples详见 setup.md 中的安装说明。配置 LLM 模型环境变量示例默认使用 OpenAI 模型openai:gpt-5-mini因此多数情况下只需设置export OPENAI_API_KEYyour-api-key如果使用 Google Gemini则设置export GEMINI_API_KEYyour-api-key更多模型配置方式见 models/overview.md。如果你不设置任何模型密钥Pydantic AI 还提供了 TestModel 等测试模型配合官方测试基础设施见 tests/models 目录可在无外部调用的情况下验证 Agent 行为。配置两个可选 API Key不配置也能跑为了让示例跑得更真实官方建议额外添加两个 API Key。注意如果任一 Key 缺失代码会自动回退到虚拟数据因此它们不是必需的环境变量用途数据源WEATHER_API_KEY天气数据tomorrow.io 天气 APIGEO_API_KEY地理编码地点 → 经纬度geocode.maps.co运行命令行版本在依赖安装完毕、环境变量就绪后直接运行python/uv-run -m pydantic_ai_examples.weather_agent其中python/uv-run表示两种等价写法python -m或uv run -m使用 uv 时。如果你想把示例复制到自己的目录中修改可以运行python/uv-run -m pydantic_ai_examples --copy-to examples/该命令由 examples/pydantic_ai_examples/main.py 提供实现会将该目录下的全部示例文件复制到目标目录目标目录不能已存在。源码拆解多工具 Agent 的实现完整源码位于 examples/pydantic_ai_examples/weather_agent.py下面逐段拆解其设计。1. 依赖注入用 dataclass 承载 HTTP 客户端dataclass class Deps: client: AsyncClientPydantic AI 的依赖系统遵循成熟的 Python 实践依赖可以是任意 Python 类型当依赖包含多个对象时dataclass 通常是首选容器详见 dependencies。这里把httpx.AsyncClient封装进Deps工具函数通过RunContext[Deps]的.deps属性即可访问它从而复用同一个连接池发起 HTTP 请求。从源码结构看这样设计的好处有二一是类型安全——RunContext用依赖类型做泛型参数写错类型时静态类型检查器会直接报错二是便于测试——可以在测试中通过Agent.override(deps...)替换依赖见 dependencies.md#overriding-dependencies例如把真实客户端换成 mock 端点。2. 创建 Agent模型、指令与重试weather_agent Agent( openai:gpt-5-mini, # Be concise, reply with one sentence. is enough for some models (like openai) to use # the below tools appropriately, but others like anthropic and gemini require a bit more direction. instructionsBe concise, reply with one sentence., deps_typeDeps, retries2, )三个关键参数第一个位置参数是模型标识符openai:gpt-5-miniprovider:model_name的格式让你可以随时替换为 Anthropic、Gemini 等其他提供商见 models/overview.mddeps_typeDeps声明依赖类型注意这里传的是类型而非实例它本身不在运行时使用作用是让 Agent 获得完整的类型检查retries2表示模型调用失败如临时网络错误时最多重试 2 次instructions指导模型行为。源码注释特别提醒对某些模型如 OpenAI一句 Be concise, reply with one sentence. 就足以让它们正确使用工具而 Anthropic、Gemini 等模型可能需要更明确的指令引导。3. 工具一get_lat_lng——带 RunContext 的异步工具class LatLng(BaseModel): lat: float lng: float weather_agent.tool async def get_lat_lng(ctx: RunContext[Deps], location_description: str) - LatLng: Get the latitude and longitude of a location. Args: ctx: The context. location_description: A description of a location. r await ctx.deps.client.get( https://demo-endpoints.pydantic.workers.dev/latlng, params{location: location_description}, ) r.raise_for_status() return LatLng.model_validate_json(r.content)要点通过weather_agent.tool装饰器注册工具。因为该工具需要访问依赖HTTP 客户端所以第一个参数是ctx: RunContext[Deps]这正是agent.tool与不需要上下文的agent.tool_plain的区别见 tools.md函数签名会被 Pydantic AI 自动解析为工具 JSON Schema除RunContext外的所有参数这里是location_description: str都会进入 Schema。同时 Pydantic AI 会借助 griffe 从 docstring 中提取参数描述并写入 Schema让模型知道每个参数的含义见 tools.md#function-tools-and-schema返回类型LatLng是一个 PydanticBaseModel工具返回值可以是任何 Pydantic 可序列化为 JSON 的类型见 tools.md#function-tool-output返回结构化模型还能为后续工具提供类型明确的输入。4. 工具二get_weather——并发请求 字典返回weather_agent.tool async def get_weather(ctx: RunContext[Deps], lat: float, lng: float) - dict[str, Any]: Get the weather at a location. Args: ctx: The context. lat: Latitude of the location. lng: Longitude of the location. temp_response, descr_response await asyncio.gather( ctx.deps.client.get( https://demo-endpoints.pydantic.workers.dev/number, params{min: 10, max: 30}, ), ctx.deps.client.get( https://demo-endpoints.pydantic.workers.dev/weather, params{lat: lat, lng: lng}, ), ) temp_response.raise_for_status() descr_response.raise_for_status() return { temperature: f{temp_response.text} °C, description: descr_response.text, }两个实现细节值得学习使用asyncio.gather并发发起温度和天气两个 HTTP 请求避免串行等待这是一个实战中非常有用的模式——一个工具内部需要多个上游数据时并发取回再聚合返回类型是dict[str, Any]即返回普通 JSON 字典也可以Pydantic AI 会将其作为工具结果回传给模型模型据此组织最终回答。源码注释明确说明演示端点返回的是随机数据NOTE: the responses here will be random, and are not related to the location description.因此不配置 API Key 时示例依然能完整演示工具调用链的机制只是数据没有真实语义。5. 主流程run depsasync def main(): async with AsyncClient() as client: logfire.instrument_httpx(client, capture_allTrue) deps Deps(clientclient) result await weather_agent.run( What is the weather like in London and in Wiltshire?, depsdeps ) print(Response:, result.output)logfire.configure(send_to_logfireif-token-present)如果环境里配置了 Logfire token 就上报可观测数据没有则静默跳过保证示例开箱即用logfire.instrument_pydantic_ai()会为 Agent 运行、工具调用自动埋点见 logfire.mdweather_agent.run(prompt, depsdeps)运行 Agent 时把Deps实例传入。整个调用链中模型先调用get_lat_lng(London)拿到坐标再把坐标传给get_weather(lat, lng)最后汇总成一句话回答——这就是LLM 需要按顺序调用多个工具才能回答问题的机制。流式输出原理从 run 到 run_stream天气 Agent 的命令行版用run()一次性拿到完整结果但在聊天 UI 中我们更希望逐字流式地呈现回答。官方文档 output.md#streaming-text 给出了标准用法async with agent.run_stream(Where does hello world come from?) as result: async for message in result.stream_text(): print(message)几个关键点Agent.run_stream()返回一个异步上下文管理器流结束后会自动关闭底层连接stream_text()每次 yield 的是到目前为止的完整文本而非增量片段并默认以 0.1 秒的debounce_by参数对到达的块做分组后再 yield——把长结构化响应按块聚合并逐块校验能显著降低逐 chunk 校验的开销传None则每个 chunk 到达即立即 yield如果需要增量片段可以使用stream_text(deltaTrue)此时每次 yield 的是新增文本但要注意使用deltaTrue时最终输出消息不会被写入result.all_messages()且会跳过输出校验器详见 output.md#streaming-text流式输出同样支持结构化输出与输出函数见 output.md#streaming-structured-output。天气 Agent 的 Gradio 版本正是run_stream的实战应用一边流式渲染最终回答一边实时把工具调用过程展示给用户。用 Gradio 构建多轮聊天 UIGradio 是纯 Python 的 AI Web 应用框架自带聊天组件和 Agent 支持因此整个 UI 可以只用一个 Python 文件实现。UI 完整源码位于 examples/pydantic_ai_examples/weather_agent_gradio.py。安装与启动pip install gradio6.7.0 python/uv-run -m pydantic_ai_examples.weather_agent_gradio注意 UI 代码对 Gradio 版本有要求源码中通过try: import gradio as gr捕获ImportError并提示 Please install gradio withpip install gradio. You must use python3.10.且使用了gr.Chatbot的retry/undo/example_select、gr.RetryData、gr.UndoData、gr.SelectData等较新 API因此请确保 Gradio 版本不低于 6.7.0。复用自己的 Agent模块复用from pydantic_ai_examples.weather_agent import Deps, weather_agent TOOL_TO_DISPLAY_NAME {get_lat_lng: Geocoding API, get_weather: Weather API} client AsyncClient() deps Deps(clientclient)UI 文件直接复用命令行版中定义的Deps和weather_agent只新增界面逻辑这是示例刻意展示的模块化写法Agent 的业务逻辑与 UI 展示彻底解耦。流式对话stream_from_agentasync def stream_from_agent(prompt: str, chatbot: list[dict], past_messages: list): chatbot.append({role: user, content: prompt}) yield gr.Textbox(interactiveFalse, value), chatbot, gr.skip() async with weather_agent.run_stream( prompt, depsdeps, message_historypast_messages ) as result: ...核心机制weather_agent.run_stream(prompt, depsdeps, message_historypast_messages)开启流式运行message_history传入历史消息实现多轮对话的上下文延续整个函数是异步生成器通过yield把界面状态输入框、聊天记录、历史消息增量推送给 Gradio交互期间先把输入框置为interactiveFalse防止重复提交回答结束后再恢复。实时展示工具调用过程天气 Agent 的 UI 有一大亮点把模型调用工具的中间过程直接渲染成带标题的聊天气泡用户能清楚看到 Agent 先调 Geocoding API 再调 Weather API的推理路径for message in result.new_messages(): for call in message.parts: if isinstance(call, ToolCallPart): call_args call.args_as_json_str() metadata { title: f️ Using {TOOL_TO_DISPLAY_NAME[call.tool_name]}, } if call.tool_call_id is not None: metadata[id] call.tool_call_id gr_message { role: assistant, content: Parameters: call_args, metadata: metadata, } chatbot.append(gr_message) if isinstance(call, ToolReturnPart): ... gr_message[content] f\nOutput: {json_content}实现细节result.new_messages()返回本轮新增的消息逐条遍历其中的partsToolCallPart表示模型发起的一次工具调用用call.tool_name映射到友好显示名Geocoding API / Weather API用call.args_as_json_str()展示调用参数并通过tool_call_id与后续的工具返回结果配对ToolReturnPart表示工具执行结果通过tool_call_id找到之前的气泡把输出结构化模型用model_dump_json()、普通值用json.dumps()追加到同一条消息中形成参数 → 输出的完整链路最终通过result.stream_text()流式把最终回答逐字渲染到最后一个 assistant 气泡中。多轮交互retry、undo 与示例问题chatbot.retry(handle_retry, [chatbot, past_messages], [prompt, chatbot, past_messages]) chatbot.undo(undo, [chatbot, past_messages], [prompt, chatbot, past_messages])UI 还实现了聊天产品的三个常用交互retry重试handle_retry从出错位置截断聊天记录与历史消息用原问题重新触发一次流式回答undo撤销undo回退到上一条消息并把被撤销的用户输入回填到输入框示例问题gr.Chatbot提供examples参数如 What is the weather like in Miami?点击示例通过select_data自动填入输入框。页面布局使用gr.Blocks组织顶部是 HTML 标题区Weather Assistant中部是gr.Chatbot聊天组件底部gr.Row中是单行输入框gr.Textboxprompt.submit(...)把回车提交绑定到stream_from_agent输入/聊天记录/历史消息三者联动刷新。past_messages用gr.State([])维护保证多轮对话间状态不丢失。启动后demo.launch()会在本地打开浏览器访问 UI。总结从 weather_agent.py 到 weather_agent_gradio.py这个示例完整覆盖了一条可复用的 Agent 开发链路用agent.toolRunContext注册需要依赖注入的工具、用 dataclass 承载共享的 HTTP 客户端、用run_streamstream_text实现流式输出、再用 Gradio 把工具调用过程和最终回答同时渲染成聊天界面。无论你接下来要做 RAG、SQL 生成还是其他多工具应用这套工具链 依赖 流式 UI的组合都可以直接照搬。想继续深入可以阅读仓库中的相关文档函数工具的完整机制见 tools.md、依赖注入的覆盖与测试见 dependencies.md、流式输出的更多模式结构化流式、delta、取消见 output.md#streaming-text更多示例RAG、SQL 生成、飞书/银行客服等见 examples 目录。【免费下载链接】pydantic-aiHow Python does AI. Agents, realtime voice, image generation, embeddings. Every model, every interface, typed end to end.项目地址: https://gitcode.com/GitHub_Trending/py/pydantic-ai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考