Skip to main content
Time to bring everything together and build a simple personal assistant agent from starter files. This hands-on lab demonstrates an agent loop that calls tools, maintains conversation memory, and handles errors. You will work with two starter files, implement three mock tools, and create an agent loop that executes tool calls and returns natural language responses. This guide assumes a Python 3.11+ virtual environment is active and the starter files exist at /root/code/tools_starter.py and /root/code/agent_starter.py. Copy each starter file to its working name and implement the TODOs inside.
A retro pixel-art poster with the headline "BUILD A PERSONAL ASSISTANT AGENT." It highlights components like "Agent Loop," "Tools," "Memory," and "Error Handling" and notes "Python 3.11 + venv active" with starter file names.

What you’ll build

  • A small agent loop that can call tools and integrate tool output into the conversation.
  • Three deterministic, mocked tools: check_calendar, search_web, and get_user_preferences.
  • A dispatcher to run tools and return results to the model.
  • Error handling and a bounded iteration loop to avoid runaway calls.

Files and components

Tip: Keep the tool metadata (TOOLS) and the Python handler names in sync. The model requests a function by name, and your execute_tool must map that exact name to the corresponding Python function.

Step 1 — Tools: create tools.py

  1. Copy tools_starter.py to tools.py.
  2. Add three tool definitions into the TOOLS list:
    • check_calendar — returns events for a date (YYYY-MM-DD) or today’s events if no date provided.
    • search_web — returns a short summary for a search query.
    • get_user_preferences — returns stored user preferences for a given category.
Each tool entry follows a function-schema pattern: a type plus a function object containing name, description, and parameters.
  1. Implement simple handler functions that return mocked, deterministic responses, and implement an execute_tool dispatcher to map tool names to their Python functions and invoke them with parsed arguments.
Example tools.py (copy into /root/code/tools.py and fill in as shown):

Step 2 — Agent: create agent.py

  1. Copy agent_starter.py to agent.py.
  2. Implement run_agent(user_message: str, history: list | None = None) -> str:
Key responsibilities of run_agent:
  • Build the initial messages list, starting with the system prompt.
  • Append optional conversation history, then the user’s message.
  • Run a bounded loop (use MAX_ITERATIONS) that calls the model until:
    • the model returns a final assistant message (finish reason stop), or
    • the model requests tool calls (finish reason like tool_call or tool_calls).
  • When the model requests tools:
    • Parse tool arguments (handle both dict and JSON string forms).
    • Call execute_tool and append the tool’s output as a role: "tool" message.
    • Continue the loop so the model can return a final answer that incorporates tool outputs.
  • Provide defensive handling for different SDK shapes (object vs dict) and unknown finish reasons.
Example agent.py:
Important: Never hard-code your API key. Set OPENAI_API_KEY and any OPENAI_API_BASE in your environment, and avoid committing keys to source control.

Step 3 — Run the agent

From the project directory (for example /root/code) run:
Add a small __main__ block to agent.py if it’s not present, to test single-step and multi-step interactions:
Example console output for the calendar query (mocked tool response):
The agent requested the check_calendar tool, received the mock schedule, and returned a natural language summary.

Step 4 — Multi-step task example

Test a multi-step query that requires the agent to call multiple tools in sequence. The agent will:
  1. Call search_web to summarize information about AI agents.
  2. Call check_calendar to check availability after 2 PM.
  3. Combine the results into a final assistant response.
Add or run this test in agent.py:
Example console output (mocked):
No extra orchestration code is required beyond the agent loop: the agent handles tool calling, executing results via execute_tool, and incorporating tool outputs back into the conversation.

Troubleshooting & tips

  • If the model never returns a final stop finish reason, the loop will eventually exit with the MAX_ITERATIONS guard to prevent infinite loops.
  • Ensure the TOOLS metadata is valid JSON-like structure (the function schema) — mismatches between declared parameters and the actual handler signature can produce TypeError.
  • Tool argument payloads may arrive as dicts or JSON strings; execute_tool should defensively handle both.
This lab demonstrates a simple production-like architecture: one agent, multiple tools, conversation memory, and basic error handling — a pattern you can extend with real APIs, authentication, and richer tool behavior.

Watch Video