前言如果你已经会调用大模型 API但一到真实项目就开始遇到这些问题模型返回的 JSON 不稳定字段有时缺失、有时类型不对。Prompt、工具函数、参数校验、业务规则混在一起代码越来越散。多用户对话时不知道当前用户、数据库连接、权限信息应该放在哪里。想写 Agent但又不想一上来就进入复杂工作流框架。Pydantic AI 很适合作为第一站。如果本文对你有帮助欢迎点赞、收藏方便后续查阅它不是一个“大而全”的 Agent 平台而是一个偏 Python 工程化的 Agent 框架。它把 Pydantic 擅长的类型校验、结构化数据、依赖注入思路放进大模型应用里让你用接近 FastAPI 的手感写 LLM Agent。本文是系列第 1 篇重点讲清 Pydantic AI 的入门逻辑Agent 是什么。system_prompt/instructions解决什么问题。deps和RunContext为什么重要。如何注册工具函数。如何用 Pydantic Model 约束结构化输出。如何用output_validator做业务校验。多轮对话和流式输出的基本写法。本文参考本地示例目录/Users/lanny/Code/labs/agno_demo/learn_pydantic_ai示例环境Python 3.12 pydantic-ai-slim[logfire,openai] 2.1.0补充说明Pydantic AI 当前官方文档更常使用instructions本地示例代码大量使用system_prompt。在pydantic_ai2.1.0中Agent(...)同时支持这两个参数。初学时不用被名字绕晕先理解它们都属于“告诉 Agent 应该怎样回答”的系统级指令。背景或问题普通大模型调用一般长这样responseclient.chat.completions.create(modelxxx,messages[{role:system,content:你是一个客服},{role:user,content:帮我查订单 A001},],)print(response.choices[0].message.content)这段代码能跑但离真实业务还有距离。生产里更常见的问题是返回结果能不能稳定变成 Python 对象模型要查数据时如何调用后端函数当前用户身份、租户、权限、数据库连接怎么传进去结果格式对了但业务规则错了谁来兜底多轮对话时历史消息由谁保存Pydantic AI 的核心价值就是把这些问题拆成清晰的工程组件。核心思路Pydantic AI 里最重要的对象是Agent。你可以把它理解成一个容器Agent 模型 model 指令 system_prompt / instructions 依赖 deps 工具 tools 输出类型 output_type 输出验证器 output_validator一次 Agent 运行大概是这样用户输入一句话。Agent 带上系统指令、依赖上下文、工具定义和历史消息。模型判断是否需要调用工具。框架执行你写的 Python 工具函数。工具结果回到模型。模型生成最终输出。Pydantic 校验输出结构。输出验证器做业务校验。业务代码拿到强类型结果。实现步骤1. 最小 Agent先跑通一次调用最小写法只需要创建一个Agent然后调用run_sync()frompydantic_aiimportAgentfromlearn_pydantic_ai.llmimportget_model agentAgent(get_model())resultagent.run_sync(用一句话介绍你自己。)print(result.output)print(result.usage)这里有两个关键点result.output是最终回答默认类型是str。result.usage可以查看 token 用量。本地示例把模型初始化封装在llm.pyfrompydantic_ai.models.openaiimportOpenAIChatModelfrompydantic_ai.providers.openaiimportOpenAIProviderdefget_model(model_id:strQwen/Qwen3-8B)-OpenAIChatModel:returnOpenAIChatModel(model_id,providerOpenAIProvider(base_urlhttps://api.siliconflow.cn/v1,api_keySILICONFLOW_API_KEY,),)这说明 Pydantic AI 不只绑定 OpenAI。只要供应商兼容 OpenAI API或者 Pydantic AI 提供对应 provider就可以切换模型。2. 系统提示词给 Agent 设定规则system_prompt适合写固定规则agentAgent(get_model(),system_prompt你是一个专业的中文技术助理回答简洁优先给可运行代码。,)resultagent.run_sync(Pydantic AI 是什么)print(result.output)这一步解决的是“Agent 的基础行为”。但真实业务里很多规则不是固定的。例如当前用户是谁、用户使用什么语言、当前坐席是谁、租户 ID 是多少。这就需要依赖注入。3. 依赖注入把运行时上下文传进去先定义一个依赖对象fromdataclassesimportdataclassdataclassclassUserContext:username:strlanguage:str创建 Agent 时声明依赖类型frompydantic_aiimportAgent,RunContext chat_agentAgent(get_model(),deps_typeUserContext)再通过动态系统提示词读取ctx.depschat_agent.system_promptdefpersonalized_prompt(ctx:RunContext[UserContext])-str:depsctx.depsreturn(f你正在和用户{deps.username}对话f请始终使用「{deps.language}」回答。)运行时传入不同依赖resultawaitchat_agent.run(讲个冷笑话。,depsUserContext(username小明,language中文),)deps的好处很直接类型安全RunContext[UserContext]让 IDE 能补全ctx.deps.username。易测试单元测试里可以传假的 deps不需要真连数据库。易隔离每个请求有自己的 deps不容易串用户。易替换以后从内存 dict 换成 PostgreSQL、Redis、HTTP Client不需要重写 Agent。4. 工具调用让模型调用 Python 函数Agent 和普通聊天最大的区别是模型可以在需要时调用工具。Pydantic AI 常见工具注册方式有两种agent.tool_plain普通函数不需要上下文。agent.tool第一个参数是RunContext可以读取依赖。例如一个掷骰子 Agentimportrandomfrompydantic_aiimportAgent,RunContext dice_agentAgent(get_model(),deps_typestr,system_prompt(你是一个掷骰子游戏。先调用 roll_dice 掷骰子再调用 get_player_name 获取玩家名字然后判断是否猜中。),)dice_agent.tool_plaindefroll_dice()-str:掷一个六面骰子返回点数。returnstr(random.randint(1,6))dice_agent.tooldefget_player_name(ctx:RunContext[str])-str:获取当前玩家的名字。returnctx.deps调用resultdice_agent.run_sync(我猜这次会掷出 6。,deps小红)print(result.output)工具函数要注意四件事函数名要清晰。参数类型要明确。docstring 要认真写它也是给模型看的工具说明。工具内部必须做业务校验不能把权限判断交给模型。5. 结构化输出别再只写“请返回 JSON”很多 LLM 应用最怕模型返回散文。你想要{city:London,country:United Kingdom}模型却回你2012 年奥运会在英国伦敦举办。Pydantic AI 的解法是直接把输出类型声明成 Pydantic Model。frompydanticimportBaseModel,FieldclassCityLocation(BaseModel):city:strField(description城市名称)country:strField(description国家名称)agentAgent(get_model(),output_typeCityLocation)resultagent.run_sync(2012 年奥运会在哪里举办)output:CityLocationresult.outputprint(output.city)print(output.country)此时result.output不再是普通字符串而是一个CityLocation对象。这就是 Pydantic AI 和普通 prompt 写法的重要区别不是靠“请严格返回 JSON”这种自然语言承诺而是把输出变成可校验的数据契约。6. 输出验证器结构合法不等于业务合法Pydantic 能检查字段和类型但业务规则还要我们自己管。例如年龄字段是整数但200岁明显不合理frompydantic_aiimportModelRetryclassUserProfile(BaseModel):name:strage:intstrict_agentAgent(get_model(),output_typeUserProfile,system_prompt从用户输入中提取姓名和年龄并以结构化数据返回。,)strict_agent.output_validatordefvalidate_age(ctx:RunContext[None],value:UserProfile)-UserProfile:ifvalue.age0orvalue.age150:raiseModelRetry(f年龄{value.age}不合理必须在 0 到 150 之间请重新提取。)returnvalueModelRetry的含义是这次输出不合格把错误原因反馈给模型让它重新生成。输出验证器适合做敏感信息拦截。金额范围校验。枚举值业务校验。分类结果二次检查。输出中是否包含禁止字段。不过要记住输出验证器是最后一道防线不是安全系统的全部。涉及权限、退款、删除数据等高风险动作必须在工具函数和后端服务里做强校验。7. 多轮对话Agent 本身不保存用户记忆Pydantic AI 的 Agent 可以全局复用但它本身不保存某个用户的聊天记忆。要做多轮对话需要显式传入message_historyagentAgent(get_model(),system_prompt你是一个乐于助人的中文助手。)result1awaitagent.run(我叫李四记住我的名字。)result2awaitagent.run(我刚才告诉你我叫什么,message_historyresult1.new_messages(),)print(result2.output)常用方法有两个result.new_messages()本轮新增消息。result.all_messages()包含历史和本轮的全部消息。生产系统里历史消息通常要存到 Redis、PostgreSQL 或 MongoDB再按session_id取出传给message_history。8. 流式输出聊天产品里的打字机效果长回答如果一次性返回体验会比较生硬。Pydantic AI 支持流式输出agentAgent(get_model(),system_prompt你是一个讲故事的能手用中文回答。)asyncwithagent.run_stream(讲一个 100 字以内的微型科幻故事。)asresult:asyncfortextinresult.stream_text():print(text)full_textawaitresult.get_output()注意流式相关 API 是异步的需要在async函数里使用。常见问题与避坑1. 工具描述不要写得太省模型会根据函数名、参数名、类型注解和 docstring 判断是否调用工具。不建议这样写defquery(x:str)-str:查询。更好的写法是defget_order_status(order_id:str)-str:根据订单号查询订单当前状态。参数 order_id 是订单号如 A001。2. 不要把业务权限交给模型判断模型可以判断“用户好像想退款”但不能决定“是否真的允许退款”。正确做法是模型提出工具调用意图。工具函数读取ctx.deps。后端根据用户身份、订单状态、金额、权限做强校验。高风险动作加人工确认或二次确认。3. 结构化输出不等于业务正确output_type能保证结构稳定但不能保证内容一定对。例如classRefundDecision(BaseModel):approved:boolreason:str模型可以返回合法结构但approvedTrue是否真的允许退款必须由业务规则判断。4. 脚本和 Web 服务的调用方式不同脚本里可以用agent.run_sync(...)异步 Web 服务里优先用awaitagent.run(...)否则可能遇到事件循环冲突。总结Pydantic AI 的基础入门逻辑可以概括成Agent - system_prompt / instructions 定规则 - deps 注入运行时上下文 - tool 连接业务函数 - output_type 约束结构化输出 - output_validator 做业务兜底 - message_history 管理多轮上下文它的优势不是“替你设计所有 Agent 流程”而是让你用普通 Python 后端工程的方式把大模型、工具函数、类型校验和业务规则组织起来。下一篇会继续往前走用 Pydantic AI 写一个电商客服 Agent并把它部署到 FastAPI 服务里顺带讲 Logfire 监控以及它和 LangChain、LangGraph 到底怎么选。参考资料Pydantic AI 官方文档Pydantic AI Agents 文档Pydantic AI Dependencies 文档Pydantic AI Output 文档
保姆级 Pydantic AI 入门(一):Agent、依赖注入、工具调用与结构化输出
前言如果你已经会调用大模型 API但一到真实项目就开始遇到这些问题模型返回的 JSON 不稳定字段有时缺失、有时类型不对。Prompt、工具函数、参数校验、业务规则混在一起代码越来越散。多用户对话时不知道当前用户、数据库连接、权限信息应该放在哪里。想写 Agent但又不想一上来就进入复杂工作流框架。Pydantic AI 很适合作为第一站。如果本文对你有帮助欢迎点赞、收藏方便后续查阅它不是一个“大而全”的 Agent 平台而是一个偏 Python 工程化的 Agent 框架。它把 Pydantic 擅长的类型校验、结构化数据、依赖注入思路放进大模型应用里让你用接近 FastAPI 的手感写 LLM Agent。本文是系列第 1 篇重点讲清 Pydantic AI 的入门逻辑Agent 是什么。system_prompt/instructions解决什么问题。deps和RunContext为什么重要。如何注册工具函数。如何用 Pydantic Model 约束结构化输出。如何用output_validator做业务校验。多轮对话和流式输出的基本写法。本文参考本地示例目录/Users/lanny/Code/labs/agno_demo/learn_pydantic_ai示例环境Python 3.12 pydantic-ai-slim[logfire,openai] 2.1.0补充说明Pydantic AI 当前官方文档更常使用instructions本地示例代码大量使用system_prompt。在pydantic_ai2.1.0中Agent(...)同时支持这两个参数。初学时不用被名字绕晕先理解它们都属于“告诉 Agent 应该怎样回答”的系统级指令。背景或问题普通大模型调用一般长这样responseclient.chat.completions.create(modelxxx,messages[{role:system,content:你是一个客服},{role:user,content:帮我查订单 A001},],)print(response.choices[0].message.content)这段代码能跑但离真实业务还有距离。生产里更常见的问题是返回结果能不能稳定变成 Python 对象模型要查数据时如何调用后端函数当前用户身份、租户、权限、数据库连接怎么传进去结果格式对了但业务规则错了谁来兜底多轮对话时历史消息由谁保存Pydantic AI 的核心价值就是把这些问题拆成清晰的工程组件。核心思路Pydantic AI 里最重要的对象是Agent。你可以把它理解成一个容器Agent 模型 model 指令 system_prompt / instructions 依赖 deps 工具 tools 输出类型 output_type 输出验证器 output_validator一次 Agent 运行大概是这样用户输入一句话。Agent 带上系统指令、依赖上下文、工具定义和历史消息。模型判断是否需要调用工具。框架执行你写的 Python 工具函数。工具结果回到模型。模型生成最终输出。Pydantic 校验输出结构。输出验证器做业务校验。业务代码拿到强类型结果。实现步骤1. 最小 Agent先跑通一次调用最小写法只需要创建一个Agent然后调用run_sync()frompydantic_aiimportAgentfromlearn_pydantic_ai.llmimportget_model agentAgent(get_model())resultagent.run_sync(用一句话介绍你自己。)print(result.output)print(result.usage)这里有两个关键点result.output是最终回答默认类型是str。result.usage可以查看 token 用量。本地示例把模型初始化封装在llm.pyfrompydantic_ai.models.openaiimportOpenAIChatModelfrompydantic_ai.providers.openaiimportOpenAIProviderdefget_model(model_id:strQwen/Qwen3-8B)-OpenAIChatModel:returnOpenAIChatModel(model_id,providerOpenAIProvider(base_urlhttps://api.siliconflow.cn/v1,api_keySILICONFLOW_API_KEY,),)这说明 Pydantic AI 不只绑定 OpenAI。只要供应商兼容 OpenAI API或者 Pydantic AI 提供对应 provider就可以切换模型。2. 系统提示词给 Agent 设定规则system_prompt适合写固定规则agentAgent(get_model(),system_prompt你是一个专业的中文技术助理回答简洁优先给可运行代码。,)resultagent.run_sync(Pydantic AI 是什么)print(result.output)这一步解决的是“Agent 的基础行为”。但真实业务里很多规则不是固定的。例如当前用户是谁、用户使用什么语言、当前坐席是谁、租户 ID 是多少。这就需要依赖注入。3. 依赖注入把运行时上下文传进去先定义一个依赖对象fromdataclassesimportdataclassdataclassclassUserContext:username:strlanguage:str创建 Agent 时声明依赖类型frompydantic_aiimportAgent,RunContext chat_agentAgent(get_model(),deps_typeUserContext)再通过动态系统提示词读取ctx.depschat_agent.system_promptdefpersonalized_prompt(ctx:RunContext[UserContext])-str:depsctx.depsreturn(f你正在和用户{deps.username}对话f请始终使用「{deps.language}」回答。)运行时传入不同依赖resultawaitchat_agent.run(讲个冷笑话。,depsUserContext(username小明,language中文),)deps的好处很直接类型安全RunContext[UserContext]让 IDE 能补全ctx.deps.username。易测试单元测试里可以传假的 deps不需要真连数据库。易隔离每个请求有自己的 deps不容易串用户。易替换以后从内存 dict 换成 PostgreSQL、Redis、HTTP Client不需要重写 Agent。4. 工具调用让模型调用 Python 函数Agent 和普通聊天最大的区别是模型可以在需要时调用工具。Pydantic AI 常见工具注册方式有两种agent.tool_plain普通函数不需要上下文。agent.tool第一个参数是RunContext可以读取依赖。例如一个掷骰子 Agentimportrandomfrompydantic_aiimportAgent,RunContext dice_agentAgent(get_model(),deps_typestr,system_prompt(你是一个掷骰子游戏。先调用 roll_dice 掷骰子再调用 get_player_name 获取玩家名字然后判断是否猜中。),)dice_agent.tool_plaindefroll_dice()-str:掷一个六面骰子返回点数。returnstr(random.randint(1,6))dice_agent.tooldefget_player_name(ctx:RunContext[str])-str:获取当前玩家的名字。returnctx.deps调用resultdice_agent.run_sync(我猜这次会掷出 6。,deps小红)print(result.output)工具函数要注意四件事函数名要清晰。参数类型要明确。docstring 要认真写它也是给模型看的工具说明。工具内部必须做业务校验不能把权限判断交给模型。5. 结构化输出别再只写“请返回 JSON”很多 LLM 应用最怕模型返回散文。你想要{city:London,country:United Kingdom}模型却回你2012 年奥运会在英国伦敦举办。Pydantic AI 的解法是直接把输出类型声明成 Pydantic Model。frompydanticimportBaseModel,FieldclassCityLocation(BaseModel):city:strField(description城市名称)country:strField(description国家名称)agentAgent(get_model(),output_typeCityLocation)resultagent.run_sync(2012 年奥运会在哪里举办)output:CityLocationresult.outputprint(output.city)print(output.country)此时result.output不再是普通字符串而是一个CityLocation对象。这就是 Pydantic AI 和普通 prompt 写法的重要区别不是靠“请严格返回 JSON”这种自然语言承诺而是把输出变成可校验的数据契约。6. 输出验证器结构合法不等于业务合法Pydantic 能检查字段和类型但业务规则还要我们自己管。例如年龄字段是整数但200岁明显不合理frompydantic_aiimportModelRetryclassUserProfile(BaseModel):name:strage:intstrict_agentAgent(get_model(),output_typeUserProfile,system_prompt从用户输入中提取姓名和年龄并以结构化数据返回。,)strict_agent.output_validatordefvalidate_age(ctx:RunContext[None],value:UserProfile)-UserProfile:ifvalue.age0orvalue.age150:raiseModelRetry(f年龄{value.age}不合理必须在 0 到 150 之间请重新提取。)returnvalueModelRetry的含义是这次输出不合格把错误原因反馈给模型让它重新生成。输出验证器适合做敏感信息拦截。金额范围校验。枚举值业务校验。分类结果二次检查。输出中是否包含禁止字段。不过要记住输出验证器是最后一道防线不是安全系统的全部。涉及权限、退款、删除数据等高风险动作必须在工具函数和后端服务里做强校验。7. 多轮对话Agent 本身不保存用户记忆Pydantic AI 的 Agent 可以全局复用但它本身不保存某个用户的聊天记忆。要做多轮对话需要显式传入message_historyagentAgent(get_model(),system_prompt你是一个乐于助人的中文助手。)result1awaitagent.run(我叫李四记住我的名字。)result2awaitagent.run(我刚才告诉你我叫什么,message_historyresult1.new_messages(),)print(result2.output)常用方法有两个result.new_messages()本轮新增消息。result.all_messages()包含历史和本轮的全部消息。生产系统里历史消息通常要存到 Redis、PostgreSQL 或 MongoDB再按session_id取出传给message_history。8. 流式输出聊天产品里的打字机效果长回答如果一次性返回体验会比较生硬。Pydantic AI 支持流式输出agentAgent(get_model(),system_prompt你是一个讲故事的能手用中文回答。)asyncwithagent.run_stream(讲一个 100 字以内的微型科幻故事。)asresult:asyncfortextinresult.stream_text():print(text)full_textawaitresult.get_output()注意流式相关 API 是异步的需要在async函数里使用。常见问题与避坑1. 工具描述不要写得太省模型会根据函数名、参数名、类型注解和 docstring 判断是否调用工具。不建议这样写defquery(x:str)-str:查询。更好的写法是defget_order_status(order_id:str)-str:根据订单号查询订单当前状态。参数 order_id 是订单号如 A001。2. 不要把业务权限交给模型判断模型可以判断“用户好像想退款”但不能决定“是否真的允许退款”。正确做法是模型提出工具调用意图。工具函数读取ctx.deps。后端根据用户身份、订单状态、金额、权限做强校验。高风险动作加人工确认或二次确认。3. 结构化输出不等于业务正确output_type能保证结构稳定但不能保证内容一定对。例如classRefundDecision(BaseModel):approved:boolreason:str模型可以返回合法结构但approvedTrue是否真的允许退款必须由业务规则判断。4. 脚本和 Web 服务的调用方式不同脚本里可以用agent.run_sync(...)异步 Web 服务里优先用awaitagent.run(...)否则可能遇到事件循环冲突。总结Pydantic AI 的基础入门逻辑可以概括成Agent - system_prompt / instructions 定规则 - deps 注入运行时上下文 - tool 连接业务函数 - output_type 约束结构化输出 - output_validator 做业务兜底 - message_history 管理多轮上下文它的优势不是“替你设计所有 Agent 流程”而是让你用普通 Python 后端工程的方式把大模型、工具函数、类型校验和业务规则组织起来。下一篇会继续往前走用 Pydantic AI 写一个电商客服 Agent并把它部署到 FastAPI 服务里顺带讲 Logfire 监控以及它和 LangChain、LangGraph 到底怎么选。参考资料Pydantic AI 官方文档Pydantic AI Agents 文档Pydantic AI Dependencies 文档Pydantic AI Output 文档