"""
使用 Pydantic AI 为银行构建客服代理的简洁示例:
Small but complete example of using Pydantic AI to build a support agent for a bank.
Run with:
uv run -m pydantic_ai_examples.bank_support
"""
from dataclasses import dataclass
from dotenv import load_dotenv
from pydantic import BaseModel
from pydantic_ai import Agent, RunContext
from pydantic import conint
load_dotenv()
class DatabaseConn:
"""This is a fake database for example purposes.
In reality, you'd be connecting to an external database
(e.g. PostgreSQL) to get information about customers.
"""
@classmethod
async def customer_name(cls, *, id: int) -> str | None:
if id == 123:
return 'John'
@classmethod
async def customer_balance(cls, *, id: int, include_pending: bool) -> float:
"""
获取客户资产数值
"""
if id == 123:
if include_pending:
return 123.45
else:
return 100.00
else:
raise ValueError('Customer not found')
@dataclass
class SupportDependencies:
customer_id: int
db: DatabaseConn
"""
继承了 BaseModel:
Pydantic AI 会把它的字段定义和 docstring 转成 JSON Schema,再作为“输出模板”提供给大模型.
模型拿到信息后知道必须产出一个对象,包含 support_advice(字符串,提示“返回给客户的建议”)、block_card(布尔,提示“是否冻结银行卡”)、risk(整数,提示“查询风险等级”)。
也就是说,字段名、类型和你写的注释都会一起传给 LLM,指示它各字段的用途。若注释写得清楚,模型就能理解每个属性需要填什么
"""
class SupportOutput(BaseModel):
support_advice: str
"""返回给客户的建议"""
block_card: bool
"""是否冻结银行卡"""
# risk: int
risk: conint(ge=0, le=5) # 对响应体做数值范围校验
"""查询风险等级"""
support_agent = Agent(
'deepseek:deepseek-chat',
# Register static instructions using a keyword argument to the agent.
# For more complex dynamically-generated instructions, see the example below.
deps_type=SupportDependencies,
output_type=SupportOutput,
# instructions: 指令
instructions=(
'你是我们银行的一名客服,请为客户提供支持服务,并评估他们这次咨询/请求的风险等级。请用客户的姓名来回复'
' 中文回答'
),
)
# 可以理解为 动态上下文(指令)增强器
@support_agent.instructions # 动态指令生成器: 每次会话开始前调用该协程,返回一段字符串追加到系统提示里,用来给 LLM 额外上下文或约束(如把客户姓名写进提示)。它只产出文本,不会在推理过程中被单独调用。
async def add_customer_name(ctx: RunContext[SupportDependencies]) -> str:
customer_name = await ctx.deps.db.customer_name(id=ctx.deps.customer_id)
return f"The customer's name is {customer_name!r}"
"""
agent 工具,根据注释信息判断是否使用它
**** Tools 它注册了一个可供 LLM 调用的工具,docstring 说明用途,模型在对话中需要查询余额时会调用该函数
"""
@support_agent.tool
async def customer_balance(
ctx: RunContext[SupportDependencies], include_pending: bool
) -> str:
"""返回客户的当前帐户余额."""
balance = await ctx.deps.db.customer_balance(
id=ctx.deps.customer_id,
include_pending=include_pending,
)
return f'${balance:.2f}'
if __name__ == '__main__':
deps = SupportDependencies(customer_id=123, db=DatabaseConn())
"""
deps:
我们的依赖对象
deps 就是向代理提供外部资源和上下文(如数据库连接、用户 ID),没有它,工具函数就无法知道要查哪个客户.
**** 这里通过 SupportDependencies 把 customer_id、db 等外部依赖注入到代理,随后在 add_customer_name、customer_balance 等函数里通过 ctx.deps 访问,实现依赖注入
"""
result = support_agent.run_sync('我卡里还有多少钱?', deps=deps)
print(result.output)
"""
support_advice='John,您的账户当前余额为$123.45。如果您需要任何其他帮助,请随时联系我们。' block_card=False risk=1
"""
result = support_agent.run_sync('I just lost my card!', deps=deps)
print(result.output)
"""
support_advice='John,我理解您丢失银行卡的担忧。我已经查看了您的账户信息,当前余额为$123.45。建议您立即采取以下措施:1)立即通过手机银行或网上银行临时冻结卡片;2)尽快前往就近网点办理正式挂失;3)检查最近交易记录是否有异常;4)申请补办新卡。在此期间,请密切留意账户变动,如有任何可疑交易请立即联系我们。' block_card=True risk=8
"""