Answer
How to Build or Customize an AI Agent
Building an AI agent involves choosing a framework, defining the agent's role and tools, managing state, and handling the reasoning loop. Here's a practical guide.
Step 1: Define the Agent's Purpose
Before writing code, answer:
- What task should the agent accomplish?
- What tools does it need?
- Should it be stateful (remember context) or stateless?
- What are the boundaries (what it should NOT do)?
Step 2: Build a Simple Agent (from scratch)
pythonfrom anthropic import Anthropic import json client = Anthropic() # Define tools the agent can use tools = [ { "name": "read_file", "description": "Read the contents of a file", "input_schema": { "type": "object", "properties": {"path": {"type": "string", "description": "File path"}}, "required": ["path"] } }, { "name": "write_file", "description": "Write content to a file", "input_schema": { "type": "object", "properties": { "path": {"type": "string"}, "content": {"type": "string"} }, "required": ["path", "content"] } } ] def execute_tool(tool_name: str, tool_input: dict) -> str: if tool_name == "read_file": with open(tool_input["path"]) as f: return f.read() elif tool_name == "write_file": with open(tool_input["path"], "w") as f: f.write(tool_input["content"]) return f"Written to {tool_input['path']}" def run_agent(task: str, system_prompt: str) -> str: messages = [{"role": "user", "content": task}] while True: response = client.messages.create( model="claude-opus-4-6", max_tokens=4096, system=system_prompt, tools=tools, messages=messages ) if response.stop_reason == "end_turn": return response.content[0].text if response.stop_reason == "tool_use": # Process tool calls messages.append({"role": "assistant", "content": response.content}) tool_results = [] for block in response.content: if block.type == "tool_use": result = execute_tool(block.name, block.input) tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": result }) messages.append({"role": "user", "content": tool_results}) # Run the agent result = run_agent( task="Read the file config.json and create a summary.md", system_prompt="You are a helpful file management assistant." )
Step 3: Use a Framework (LangGraph)
pythonfrom langgraph.graph import StateGraph, END from langgraph.prebuilt import create_react_agent from langchain_anthropic import ChatAnthropic from langchain_core.tools import tool llm = ChatAnthropic(model="claude-opus-4-6") @tool def get_weather(city: str) -> str: '''Get weather for a city''' return f"Weather in {city}: 22°C, sunny" @tool def search_news(topic: str) -> str: '''Search recent news on a topic''' return f"Recent news about {topic}: [retrieved results]" # Create agent with tools agent = create_react_agent(llm, tools=[get_weather, search_news]) # Run result = agent.invoke({ "messages": [("user", "What's the weather in Paris and any news about AI today?")] })
Step 4: Customize Behavior
pythonfrom langgraph.graph import StateGraph, END from typing import TypedDict class AgentState(TypedDict): messages: list task_complete: bool attempts: int def custom_agent_logic(state: AgentState) -> AgentState: '''Custom reasoning with guardrails''' if state["attempts"] >= 5: state["task_complete"] = True # Stop after 5 attempts return state # Your custom logic here response = llm.invoke(state["messages"]) state["messages"].append(response) state["attempts"] += 1 if "DONE" in response.content: state["task_complete"] = True return state graph = StateGraph(AgentState) graph.add_node("agent", custom_agent_logic) graph.add_conditional_edges("agent", lambda s: END if s["task_complete"] else "agent") graph.set_entry_point("agent") app = graph.compile()
Checklist for Building Agents
- Define clear task boundaries and stopping conditions
- Implement max iterations to prevent infinite loops
- Add error handling for tool failures
- Log all tool calls for debugging
- Test with adversarial inputs
- Add human-in-the-loop for destructive operations
- Monitor token usage (costs grow fast with agents)