This guide walks you through creating a custom agent from scratch, step by step.
pip install -r requirements.txtTools are regular Python functions decorated with @tool. The decorator captures metadata that the LLM uses to understand what the tool does and how to call it.
from src.tool_registry import ToolRegistry, tool
registry = ToolRegistry()
@registry.register
@tool(
description="Fetch the current price of a stock by ticker symbol",
param_descriptions={
"ticker": "Stock ticker symbol (e.g., 'AAPL', 'GOOGL')",
},
tags=["finance"],
)
def get_stock_price(ticker: str) -> str:
# In production, call a real API here
prices = {"AAPL": 178.50, "GOOGL": 141.20, "MSFT": 378.90}
price = prices.get(ticker.upper())
if price is None:
return f"Unknown ticker: {ticker}"
return f"{ticker.upper()}: ${price:.2f}"Key points:
from src.agent import Agent, AgentConfig
from src.llm_client import OpenAIClient # or MockLLMClient for testing
config = AgentConfig(
name="stock-analyst",
system_prompt="""You are a financial analyst assistant.
When asked about stocks, use the get_stock_price tool to look up current prices.
Always provide context with your answers (market trends, comparisons).
If you don't have data for a stock, say so clearly.""",
max_steps=15,
temperature=0.1,
model="gpt-4o",
)System prompt tips:
from src.guardrails import GuardrailChain, ContentFilter, RateLimiter, LengthGuardrail
guardrails = GuardrailChain()
# Block attempts to extract API keys or credentials
guardrails.add(ContentFilter(
blocked_patterns=[r"api.?key", r"secret", r"password"],
), input_only=True)
# Prevent runaway loops
guardrails.add(RateLimiter(max_calls=20, window_seconds=60))
# Block excessively long inputs (prompt injection defense)
guardrails.add(LengthGuardrail(max_chars=10_000), input_only=True)from src.memory.buffer import ConversationBuffer
from src.memory.long_term import LongTermMemory
from src.memory.summarizer import ConversationSummarizer
# Short-term: keeps recent conversation in context
summarizer = ConversationSummarizer()
buffer = ConversationBuffer(
max_turns=30,
max_tokens=6000,
summarize_fn=summarizer,
)
# Long-term: remembers facts across conversations
long_term = LongTermMemory(persist_path="./data/analyst_memory.json")
long_term.store("User prefers concise bullet-point responses", category="preference", importance=0.9)agent = Agent(
config=config,
llm=OpenAIClient(api_key="YOUR_API_KEY_HERE"),
tools=registry,
memory=buffer,
guardrails=guardrails,
event_handler=lambda e: print(f" [{e.event_type}] {e.content}"),
)
# Single-turn interaction
result = agent.run("What's the current price of AAPL?")
print(result)
# Multi-turn (memory preserves context)
result2 = agent.run("How does that compare to MSFT?")
print(result2)For simple Q&A, the default agent loop is fine. For complex multi-step tasks:
from src.planning.react import ReActPlanner
planner = ReActPlanner(
agent_config=config,
llm=OpenAIClient(api_key="YOUR_API_KEY_HERE"),
tools=registry,
max_iterations=8,
)
result = planner.solve("Compare AAPL, GOOGL, and MSFT prices and recommend the best value")
print(planner.format_trace()) # See the reasoning stepsfrom src.planning.plan_execute import PlanAndExecutePlanner
planner = PlanAndExecutePlanner(
llm=OpenAIClient(api_key="YOUR_API_KEY_HERE"),
tools=registry,
max_replans=2,
)
result = planner.solve("Research the top 5 tech stocks, analyze their prices, and write a summary report")| Pattern | Best For | Overhead |
|---|---|---|
| Basic Agent | Single-tool calls, Q&A | Lowest |
| ReAct | Multi-step reasoning, information gathering | Low |
| Plan-and-Execute | Complex projects, tasks with dependencies | Medium |
| Supervisor/Worker | Tasks requiring multiple specializations | Highest |
1. Too many tools: Agents get confused with 15+ tools. Group related functionality into fewer, more capable tools.
2. Vague system prompts: "Be helpful" is not enough. Specify the domain, available tools, and expected behavior.
3. No max_steps limit: Always set one. LLMs can get stuck in loops.
4. Ignoring tool errors: If a tool returns an error, the agent might try again with the same bad inputs. Consider adding retry logic in the tool itself.
5. Not testing with MockLLMClient: Real API calls are slow and expensive for iterating on agent design. Script the expected behavior with the mock first.
Get the full AI Agent Framework and unlock everything.
Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.
Access all interactive tools with complete data, all workload profiles, and the full scenario library.
Downloadable source code, configuration files, and working examples from every chapter.
Free updates for life. Every new chapter, tool, and improvement included.