Most “AI agent” tutorials show you a chatbot. This one shows you a production-ready, structured-output, audit-logged agent that actually meets enterprise expectations.
The Pattern: Tool-Use Agent with Bounded Outputs
- 01Define a strict output schema
Pydantic model — the agent CANNOT respond outside this shape.
- 02Define typed tools
Each tool has a JSON schema; the LLM picks tools, code executes them.
- 03Wrap every call in retry + validation
Schema validation on output; exponential backoff on transient errors.
- 04Log inputs, prompts, outputs immutably
Append to S3 Object Lock for audit and replay.
- 05Fall back to human-review on validation fail
No “best effort” outputs.
The Agent (LangChain + Claude, Production-Ready)
from pydantic import BaseModel, Field
from langchain_anthropic import ChatAnthropic
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain.tools import tool
class TicketTriageResult(BaseModel):
"""Output schema — the agent CANNOT respond outside this shape."""
category: str = Field(description="One of: bug, feature, billing, security, other")
priority: str = Field(description="One of: P0, P1, P2, P3")
suggested_owner: str = Field(description="Team to route to")
summary: str = Field(description="One-sentence summary")
confidence: float = Field(ge=0, le=1)
@tool
def fetch_customer_tier(customer_id: str) -> str:
"""Look up the support tier for a customer."""
# in production: query CRM
return "enterprise"
@tool
def fetch_recent_tickets(customer_id: str) -> list[dict]:
"""Return the last 5 tickets for a customer."""
return [] # in production: query ticket DB
def triage(ticket_text: str, customer_id: str) -> TicketTriageResult:
llm = ChatAnthropic(model="claude-3-7-sonnet-20250219", temperature=0)
structured = llm.with_structured_output(TicketTriageResult)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a support triage assistant. Use tools to gather context, then respond with the structured triage decision. Be conservative — escalate to P0 only when revenue or security impact is clear."),
("user", "Customer: {customer_id}nTicket:n{ticket_text}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, [fetch_customer_tier, fetch_recent_tickets], prompt)
executor = AgentExecutor(agent=agent, tools=[fetch_customer_tier, fetch_recent_tickets], verbose=False)
raw = executor.invoke({"customer_id": customer_id, "ticket_text": ticket_text})
# Validation gate
result = structured.invoke(raw["output"])
audit_log(ticket_text, customer_id, raw, result)
return result
Ready to optimize your cloud or AI footprint?
Book a free 30-minute architecture review. We will deliver a written cost-and-architecture audit within 48 hours.
Need help with enterprise AI agent LangChain?
Ohveda runs free 30-minute architecture reviews. We will identify your top opportunities in writing within 48 hours — at no cost.