Pydantic Bank Support Agent
Build a bank support agent with Pydantic AI and Mistral AI
In this cookbook, we'll demonstrate how to build a bank support agent using Mistral AI and PydanticAI, which offers the following features:
- Structured Responses: Pydantic ensures that outputs conform to a predefined schema, providing consistent and validated responses.
- External Dependencies: Enhance AI interactions by integrating external dependencies, such as databases, through a type-safe dependency injection system.
- Dynamic Context: System prompt functions allow the injection of runtime information, like a customer's name, into the agent's context, enabling personalized interactions.
- Tool Integration: The agent can invoke tools for real-time information retrieval, enhancing its capabilities beyond static responses.
Example in this cookbook is adapted from https://ai.pydantic.dev/.
Collecting pydantic-ai Downloading pydantic_ai-0.0.14-py3-none-any.whl.metadata (11 kB) Requirement already satisfied: nest_asyncio in /usr/local/lib/python3.10/dist-packages (1.6.0) Collecting pydantic-ai-slim==0.0.14 (from pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading pydantic_ai_slim-0.0.14-py3-none-any.whl.metadata (2.8 kB) Requirement already satisfied: eval-type-backport>=0.2.0 in /usr/local/lib/python3.10/dist-packages (from pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (0.2.0) Collecting griffe>=1.3.2 (from pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading griffe-1.5.1-py3-none-any.whl.metadata (5.1 kB) Requirement already satisfied: httpx>=0.27.2 in /usr/local/lib/python3.10/dist-packages (from pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (0.28.1) Collecting logfire-api>=1.2.0 (from pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading logfire_api-2.9.0-py3-none-any.whl.metadata (947 bytes) Requirement already satisfied: pydantic>=2.10 in /usr/local/lib/python3.10/dist-packages (from pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (2.10.3) Collecting groq>=0.12.0 (from pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading groq-0.13.1-py3-none-any.whl.metadata (14 kB) Collecting json-repair>=0.30.3 (from pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading json_repair-0.32.0-py3-none-any.whl.metadata (11 kB) Collecting mistralai>=1.2.5 (from pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading mistralai-1.2.5-py3-none-any.whl.metadata (27 kB) Requirement already satisfied: openai>=1.54.3 in /usr/local/lib/python3.10/dist-packages (from pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (1.57.4) Collecting anthropic>=0.40.0 (from pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading anthropic-0.42.0-py3-none-any.whl.metadata (23 kB) Collecting google-auth>=2.36.0 (from pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading google_auth-2.37.0-py2.py3-none-any.whl.metadata (4.8 kB) Requirement already satisfied: requests>=2.32.3 in /usr/local/lib/python3.10/dist-packages (from pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (2.32.3) Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from anthropic>=0.40.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (3.7.1) Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from anthropic>=0.40.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (1.9.0) Requirement already satisfied: jiter<1,>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from anthropic>=0.40.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (0.8.2) Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from anthropic>=0.40.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (1.3.1) Requirement already satisfied: typing-extensions<5,>=4.10 in /usr/local/lib/python3.10/dist-packages (from anthropic>=0.40.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (4.12.2) Requirement already satisfied: cachetools<6.0,>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from google-auth>=2.36.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (5.5.0) Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.10/dist-packages (from google-auth>=2.36.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (0.4.1) Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.10/dist-packages (from google-auth>=2.36.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (4.9) Collecting colorama>=0.4 (from griffe>=1.3.2->pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading colorama-0.4.6-py2.py3-none-any.whl.metadata (17 kB) Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx>=0.27.2->pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (2024.12.14) Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx>=0.27.2->pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (1.0.7) Requirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx>=0.27.2->pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (3.10) Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx>=0.27.2->pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (0.14.0) Collecting httpx>=0.27.2 (from pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading httpx-0.27.2-py3-none-any.whl.metadata (7.1 kB) Collecting jsonpath-python<2.0.0,>=1.0.6 (from mistralai>=1.2.5->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading jsonpath_python-1.0.6-py3-none-any.whl.metadata (12 kB) Requirement already satisfied: python-dateutil<3.0.0,>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from mistralai>=1.2.5->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (2.8.2) Collecting typing-inspect<0.10.0,>=0.9.0 (from mistralai>=1.2.5->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading typing_inspect-0.9.0-py3-none-any.whl.metadata (1.5 kB) Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai>=1.54.3->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (4.67.1) Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2.10->pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (0.7.0) Requirement already satisfied: pydantic-core==2.27.1 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2.10->pydantic-ai-slim==0.0.14->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (2.27.1) Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.32.3->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (3.4.0) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.32.3->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (2.2.3) Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->anthropic>=0.40.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (1.2.2) Requirement already satisfied: pyasn1<0.7.0,>=0.4.6 in /usr/local/lib/python3.10/dist-packages (from pyasn1-modules>=0.2.1->google-auth>=2.36.0->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (0.6.1) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil<3.0.0,>=2.8.2->mistralai>=1.2.5->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) (1.17.0) Collecting mypy-extensions>=0.3.0 (from typing-inspect<0.10.0,>=0.9.0->mistralai>=1.2.5->pydantic-ai-slim[anthropic,groq,mistral,openai,vertexai]==0.0.14->pydantic-ai) Downloading mypy_extensions-1.0.0-py3-none-any.whl.metadata (1.1 kB) Downloading pydantic_ai-0.0.14-py3-none-any.whl (9.7 kB) Downloading pydantic_ai_slim-0.0.14-py3-none-any.whl (77 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 77.1/77.1 kB 3.4 MB/s eta 0:00:00 Downloading anthropic-0.42.0-py3-none-any.whl (203 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 203.4/203.4 kB 7.5 MB/s eta 0:00:00 Downloading google_auth-2.37.0-py2.py3-none-any.whl (209 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 209.8/209.8 kB 14.4 MB/s eta 0:00:00 Downloading griffe-1.5.1-py3-none-any.whl (127 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 127.1/127.1 kB 9.8 MB/s eta 0:00:00 Downloading groq-0.13.1-py3-none-any.whl (109 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 109.1/109.1 kB 8.7 MB/s eta 0:00:00 Downloading json_repair-0.32.0-py3-none-any.whl (19 kB) Downloading logfire_api-2.9.0-py3-none-any.whl (71 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 71.9/71.9 kB 5.2 MB/s eta 0:00:00 Downloading mistralai-1.2.5-py3-none-any.whl (260 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 260.0/260.0 kB 17.8 MB/s eta 0:00:00 Downloading httpx-0.27.2-py3-none-any.whl (76 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 76.4/76.4 kB 5.5 MB/s eta 0:00:00 Downloading colorama-0.4.6-py2.py3-none-any.whl (25 kB) Downloading jsonpath_python-1.0.6-py3-none-any.whl (7.6 kB) Downloading typing_inspect-0.9.0-py3-none-any.whl (8.8 kB) Downloading mypy_extensions-1.0.0-py3-none-any.whl (4.7 kB) Installing collected packages: mypy-extensions, logfire-api, jsonpath-python, json-repair, colorama, typing-inspect, httpx, griffe, google-auth, pydantic-ai-slim, mistralai, groq, anthropic, pydantic-ai Attempting uninstall: httpx Found existing installation: httpx 0.28.1 Uninstalling httpx-0.28.1: Successfully uninstalled httpx-0.28.1 Attempting uninstall: google-auth Found existing installation: google-auth 2.27.0 Uninstalling google-auth-2.27.0: Successfully uninstalled google-auth-2.27.0 ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. google-colab 1.0.0 requires google-auth==2.27.0, but you have google-auth 2.37.0 which is incompatible. Successfully installed anthropic-0.42.0 colorama-0.4.6 google-auth-2.37.0 griffe-1.5.1 groq-0.13.1 httpx-0.27.2 json-repair-0.32.0 jsonpath-python-1.0.6 logfire-api-2.9.0 mistralai-1.2.5 mypy-extensions-1.0.0 pydantic-ai-0.0.14 pydantic-ai-slim-0.0.14 typing-inspect-0.9.0
If you're running pydantic-ai in a jupyter notebook or Colab, you will need nest-asyncio to manage conflicts between event loops that occur between jupyter's event loops and pydantic-ai's:
Type your API Key··········
Example 1: Basic Q&A with Mistral
Let's start with a straightforward example using Pydantic AI for a basic Q&A with Mistral.
We'll define an agent powered by Mistral with a system prompt designed to ensure concise responses. When we ask about the origin of “hello world,” the model will provide a brief, one-sentence answer.
"Hello, World!" originated from a 1974 Bell Labs internal memorandum by Brian Kernighan, demonstrating the C programming language.
Example 2: Bank support agent
In this more complex example, we build a bank support agent.
Step 1: Define a database
In more advanced AI workflows, your model may require external data, such as information from a database. Here, we define a fictional database class that retrieves a customer's name and balance. In a real-world scenario, this class could connect to a live database. The agent can use these methods to respond to customer inquiries effectively.
Step 2: Define the bank support agent
In this step, we define how the support agent works by setting up its input dependencies and expected output format.
Code Breakdown:
1. Input Dependencies:
• SupportDependencies specifies what the agent needs to function:
• customer_id (the ID of the customer being helped).
• db (a connection to the database).
2. Expected Response Format:
• SupportResult defines the response structure, ensuring consistency:
• support_advice: A string containing advice given to the customer.
• block_card: A boolean indicating whether the customer’s card should be blocked.
• risk: An integer from 0 to 10, representing the assessed risk level.
3. Agent Initialization:
• We create the support_agent using the Agent class:
• model: The underlying AI model.
• deps_type: Specifies the required input dependencies (SupportDependencies).
• result_type: Defines the expected output structure (SupportResult).
• system_prompt: A prompt guiding the agent to act as a bank support representative. The prompt ensures customer-specific responses by including the customer’s name.
Why This Design Matters:
- By defining input dependencies and output formats, we guarantee that the agent always receives the correct data and produces predictable results. This makes integration into larger systems easier and supports clear, actionable responses.
Step 3: Add a dynamic system prompt
This code attaches a dynamic system prompt function. Before the model sees the user's query, it gets a special system prompt enriched with the customer's name.
Step 4: Defining tools that the agent can use
By decorating customer_balance with @support_agent.tool, we're telling the model it can call this function to retrieve the customer's balance. This transforms the model from a passive text generator into an active problem solver that can interact with external resources.
Step 5: Run agent
When asked about the customer’s balance, the agent uses the injected dependencies and tools to return a structured response.
support_advice='Hi John, your current balance is $123.45.' block_card=False risk=0
support_advice='Hello John, Your account balance is $123.45. We will block your card immediately and send you a new one. You can call us at 1-800-123-4567 if you need further assistance.' block_card=True risk=5
You can check the results and message history including the system prompt and the tool usage:
{'_all_messages': [ModelRequest(parts=[SystemPromptPart(content="You are a support agent in our bank, give the customer support and judge the risk level of their query. Reply using the customer's name.", part_kind='system-prompt'), SystemPromptPart(content="The customer's name is 'John'", part_kind='system-prompt'), UserPromptPart(content='I just lost my card!', timestamp=datetime.datetime(2024, 12, 20, 23, 21, 25, 269653, tzinfo=datetime.timezone.utc), part_kind='user-prompt')], kind='request'),
, ModelResponse(parts=[ToolCallPart(tool_name='customer_balance', args=ArgsJson(args_json='{"include_pending": false}'), tool_call_id='nli5z3jgK', part_kind='tool-call')], timestamp=datetime.datetime(2024, 12, 20, 23, 21, 25, tzinfo=datetime.timezone.utc), kind='response'),
, ModelRequest(parts=[ToolReturnPart(tool_name='customer_balance', content='$123.45', tool_call_id='nli5z3jgK', timestamp=datetime.datetime(2024, 12, 20, 23, 21, 25, 835008, tzinfo=datetime.timezone.utc), part_kind='tool-return')], kind='request'),
, ModelResponse(parts=[ToolCallPart(tool_name='final_result', args=ArgsJson(args_json='{"risk": 5, "support_advice": "Hello John, Your account balance is $123.45. We will block your card immediately and send you a new one. You can call us at 1-800-123-4567 if you need further assistance.", "block_card": true}'), tool_call_id='pSBlsz5SY', part_kind='tool-call')], timestamp=datetime.datetime(2024, 12, 20, 23, 21, 26, tzinfo=datetime.timezone.utc), kind='response'),
, ModelRequest(parts=[ToolReturnPart(tool_name='final_result', content='Final result processed.', tool_call_id='pSBlsz5SY', timestamp=datetime.datetime(2024, 12, 20, 23, 21, 27, 162246, tzinfo=datetime.timezone.utc), part_kind='tool-return')], kind='request')],
, '_new_message_index': 0,
, 'data': SupportResult(support_advice='Hello John, Your account balance is $123.45. We will block your card immediately and send you a new one. You can call us at 1-800-123-4567 if you need further assistance.', block_card=True, risk=5),
, '_usage': Usage(requests=2, request_tokens=673, response_tokens=110, total_tokens=783, details=None)}