
- Create a
schemaspackage with a PydanticTicketmodel. - Move domain logic into a
toolspackage. - Add a typed
create_tickettool that returns a validated, structured ticket. - Keep the agent orchestrator thin—use tools for lookup, checks, and ticket creation.
1) Ticket schema (Pydantic)
Use a Pydantic model to enforce a consistent ticket shape on the Python side. The LLM does not need to produce the exact JSON; instead, the agent will call a tool which builds and validates the Pydantic model, returning a reliable dict. Exampleschemas/ticket.py:
- Pydantic docs: https://docs.pydantic.dev/
2) Typed tool input: CreateTicketArgs
Define a typed input model for the create-ticket tool. This ensures the tool receives validated input and makes intent explicit. ExampleCreateTicketArgs (place this inside tools/helpdesk_tools.py or a shared module):
3) Tool implementations (lookup, service status, create ticket)
Move domain logic intotools/helpdesk_tools.py. For the demo we use simple in-memory backends and return structured dicts indicating “status” plus result payload or error message.
Example excerpts for tools/helpdesk_tools.py:
- These implementations always return a small structured dict with a
statusfield. This makes it easy for the agent to branch on success vs error. - In a real system, replace fake backends with database calls, API clients, or ticketing system integrations.
4) Wrapping tools for the agent
In your agent module you wrap these functions with FunctionTool so the agent can call them. The agent keeps instruction and orchestration logic, while tools encapsulate domain behavior and validation. Exampleagent.py excerpt:
Using tool-level structured outputs (tools return validated dicts produced from Pydantic models) provides strong guarantees about data shape while still letting the LLM decide when and how to call those tools.
5) Example run (console)
A typical session shows the agent calling tools, receiving structured outputs, and creating a ticket:- The agent called
check_service_statusand received a structured response:{status: "success", service: "vpn", status_text: "degraded"}. - Given the impact, the agent built a
CreateTicketArgspayload and invokedcreate_ticket. create_ticket_implinstantiated theTicketPydantic model, validated it, and returnedticket.model_dump()as a structured result.- The resulting dict can be logged, stored in a database, or sent to a ticketing API.
- Refactored the demo into
schemas/andtools/. - Added a Pydantic
Ticketmodel and a typed create-ticket tool that returns a validated ticket dictionary. - Moved domain logic into tools and left the agent focused on orchestration and instructions.
- This pattern—tool-level structured outputs + typed inputs—maintains LLM flexibility while guaranteeing reliable data shapes for downstream systems.
- Add grounding sources (internal IT policies, runbooks) so the agent consults authoritative documentation.
- Integrate a real user directory and ticketing API to replace demo backends.
- Add telemetry and audit logging for ticket creation and tool calls.
- Pydantic: https://docs.pydantic.dev/
- Python datetime docs: https://docs.python.org/3/library/datetime.html
- Python uuid docs: https://docs.python.org/3/library/uuid.html
- (ADK) Consult your ADK provider docs for FunctionTool and agent runtime specifics.