Building the Agent End to End

From Design to Code

Now you will implement your agent using your design doc.

Step 1: Set Up Your Tools

Implement or connect each tool. Make sure each tool works.

Anatomy of a tool:

def search_tool(query, num_results=5):
  """Search the web.
  
  Args:
    query: Search terms
    num_results: How many results to return
  
  Returns:
    List of dicts with 'title', 'snippet', 'url'
  """
  # Your implementation
  results = api_call(query, num_results)
  return results

def fetch_webpage_tool(url):
  """Fetch and parse a web page.
  
  Args:
    url: The URL to fetch
  
  Returns:
    Dict with 'title', 'text', 'url'
  """
  # Your implementation
  page = fetch_and_parse(url)
  return page

Register your tools:

tools = {
  "search": search_tool,
  "fetch": fetch_webpage_tool,
  "summarize": summarize_tool
}

Test each tool:

# Test search
result = search_tool("python programming")
print(result)  # Should be a list of dicts

# Test fetch
result = fetch_webpage_tool("https://example.com")
print(result)  # Should be a dict with 'text' key

Step 2: Wire the Orchestration Loop

Implement the core loop: think, act, observe, repeat.

For agentic loop pattern:

def run_agent(query):
  state = AgentState(query)
  conversation = []
  
  # System message tells the model what tools are available
  system_message = f"""You are a research assistant. You have these tools:
  - search: Search the web for information
  - fetch: Get the full text of a webpage
  - summarize: Summarize a text
  
  Use these tools to answer the user's question: {query}
  """
  
  for iteration in range(max_iterations):
    # Think: Ask the model what to do next
    response = model.chat(
      system=system_message,
      messages=conversation
    )
    
    # Parse the response to extract tool call
    if "<tool_use>" in response:
      tool_call = parse_tool_call(response)
      tool_name = tool_call["name"]
      params = tool_call["params"]
      
      # Act: Call the tool
      if tool_name not in tools:
        result = {"error": f"Unknown tool: {tool_name}"}
      else:
        result = tools[tool_name](**params)
      
      # Observe: Add to conversation
      conversation.append({
        "role": "assistant",
        "content": response
      })
      conversation.append({
        "role": "user",
        "content": f"Tool {tool_name} returned: {json.dumps(result)}"
      })
      
      state.iteration_count += 1
    else:
      # Model returned a final answer
      return response
  
  return "Max iterations reached"

For predefined workflow pattern:

def run_agent_workflow(query):
  state = AgentState(query)
  
  # Step 1: Search
  print("Step 1: Searching for sources...")
  sources = search_tool(query, num_results=3)
  state.sources = [s["url"] for s in sources]
  
  # Step 2: Fetch and read
  print("Step 2: Reading sources...")
  for url in state.sources:
    content = fetch_webpage_tool(url)
    state.add_source(url, content["text"])
  
  # Step 3: Summarize
  print("Step 3: Summarizing findings...")
  all_text = " ".join(state.facts.values())
  summary = summarize_tool(all_text)
  
  return summary

Step 3: Add State Management

Keep track of what the agent has done.

class AgentState:
  def __init__(self, query):
    self.query = query
    self.sources = []
    self.facts = {}
    self.iteration_count = 0
    self.total_cost = 0.0
  
  def get_summary(self):
    return f"Query: {self.query}, Sources found: {len(self.sources)}, Cost: ${self.total_cost:.2f}"

# Track state throughout the agent run
state = AgentState("What is machine learning?")
for iteration in range(max_iterations):
  state.iteration_count += 1
  # ... run agent step ...
  print(state.get_summary())

Step 4: Add Error Handling

Do not let the agent crash. Handle errors gracefully.

from enum import Enum

class ToolError(Exception):
  pass

def call_tool_safe(tool_name, params):
  try:
    if tool_name not in tools:
      return {"error": f"Unknown tool: {tool_name}"}
    
    result = tools[tool_name](**params)
    
    if result is None:
      return {"error": f"{tool_name} returned None"}
    
    return result
  
  except Exception as e:
    return {"error": f"{tool_name} failed: {str(e)}"}

Step 5: Add Guardrails

Add the safety limits you designed.

class CircuitBreaker:
  def __init__(self, max_iterations=15, max_cost=1.0):
    self.max_iterations = max_iterations
    self.max_cost = max_cost
    self.iteration_count = 0
    self.total_cost = 0.0
  
  def check(self, cost):
    self.iteration_count += 1
    self.total_cost += cost
    
    if self.iteration_count > self.max_iterations:
      raise Exception(f"Max iterations ({self.max_iterations}) exceeded")
    if self.total_cost > self.max_cost:
      raise Exception(f"Cost limit (${self.max_cost}) exceeded")

# Use in agent loop
cb = CircuitBreaker(max_iterations=15, max_cost=1.0)

for iteration in range(100):
  # ... call tool ...
  cost = calculate_cost(result)
  cb.check(cost)

Step 6: Test with Example Inputs

Run your agent on real examples. Does it work?

Good test input: "Find articles about renewable energy and summarize them."

Bad test input: "Tell me everything about everything." (too vague)

# Test your agent
query = "Find information about renewable energy"
result = run_agent(query)
print(f"Result: {result}")
print(f"State: {state.get_summary()}")

Tools for Building

LangGraph (https://langchain-ai.github.io/langgraph/) Structured orchestration. Define nodes and edges. Supports agentic loops and workflows.

OpenAI Assistants API (https://platform.openai.com/docs/assistants) Built-in agent framework. Tools are defined in JSON. The API handles the loop.

Anthropic tool use Use Claude with tool_use blocks for agent orchestration.

Common Issues and Fixes

Issue: Tool calls are not parsed correctly. Fix: Add print statements to see what the model returned. Adjust the parsing logic.

Issue: The agent picks the wrong tool. Fix: Rewrite tool descriptions. Make them more specific. Add examples.

Issue: The agent loops or gets stuck. Fix: Add max iteration limit. Add error messages that tell the model to try something different.

Issue: Outputs are too long or too short. Fix: Add constraints to the system message ("Keep responses under 100 words").

Issue: Agent is too expensive. Fix: Add cost cap. Reduce num_results in search. Use a cheaper model.

Checklist: Implementation Complete

  • All tools are implemented and tested
  • Orchestration loop is working
  • State is tracked correctly
  • Error handling is in place
  • Guardrails are implemented
  • Agent works on test inputs
  • Cost is within budget
  • Iteration count is under limit

Once you pass all checks, move to testing and evaluation.

Discussion

  • Loading…

← Back to Tutorials