Skip to main content

Agent Patterns

15 copy-paste patterns for building agents, from minimal single-agent to multi-agent systems.


1. Minimal ReAct Agent (5 lines)​

The simplest possible agent: a single LLM with no tools.

from synapsekit.agents import ReActAgent
from synapsekit.llms.openai import OpenAILLM

agent = ReActAgent(llm=OpenAILLM(model="gpt-4o-mini"))
result = await agent.run("What is 42 squared?")
print(result.output)

2. FunctionCalling Agent with 3 Tools​

Give an agent structured tools with automatic JSON schema generation.

from synapsekit.agents import FunctionCallingAgent
from synapsekit.agents.tools import WebSearchTool, CalculatorTool, WikipediaTool
from synapsekit.llms.openai import OpenAILLM

agent = FunctionCallingAgent(
llm=OpenAILLM(model="gpt-4o"),
tools=[WebSearchTool(), CalculatorTool(), WikipediaTool()],
max_steps=10,
)

result = await agent.run("What is the population of Tokyo multiplied by 2?")
print(result.output)

3. Agent with Web Search and File Output​

Search the web and save the structured result to a file.

from synapsekit.agents import FunctionCallingAgent
from synapsekit.agents.tools import TavilySearchTool, FileWriteTool
from synapsekit.llms.openai import OpenAILLM

agent = FunctionCallingAgent(
llm=OpenAILLM(model="gpt-4o"),
tools=[
TavilySearchTool(api_key_env="TAVILY_API_KEY"),
FileWriteTool(base_dir="/tmp/agent-output"),
],
max_steps=8,
)

result = await agent.run(
"Research the latest LLM benchmarks from 2026 and write a summary to benchmarks.md"
)
print(result.output)

4. Agent with Database Query​

Give an agent read-only access to a SQL database.

from synapsekit.agents import FunctionCallingAgent
from synapsekit.agents.tools import SQLTool
from synapsekit.llms.openai import OpenAILLM
import os

sql_tool = SQLTool(
connection_string=os.environ["DATABASE_URL"],
allowed_operations=["SELECT"], # read-only
)

agent = FunctionCallingAgent(
llm=OpenAILLM(model="gpt-4o"),
tools=[sql_tool],
max_steps=5,
)

result = await agent.run("How many users signed up in the last 30 days?")
print(result.output)

5. Streaming Agent with Thought Process Visible​

Stream both the internal reasoning steps and the final answer in real time.

from synapsekit.agents import ReActAgent
from synapsekit.agents.tools import WebSearchTool
from synapsekit.llms.openai import OpenAILLM

agent = ReActAgent(
llm=OpenAILLM(model="gpt-4o"),
tools=[WebSearchTool()],
verbose=True,
)

async for event in agent.stream("What are the top AI news stories this week?"):
if event.type == "thought":
print(f"[Thinking] {event.content}")
elif event.type == "action":
print(f"[Tool] {event.tool}: {event.input}")
elif event.type == "observation":
print(f"[Result] {event.content[:100]}")
elif event.type == "final":
print(f"\n[Answer] {event.content}")

6. Agent with Memory Between Runs​

Persist the agent's conversation history across process restarts.

from synapsekit.agents import ReActAgent
from synapsekit.agents.tools import WebSearchTool
from synapsekit.memory import SQLiteConversationMemory
from synapsekit.llms.openai import OpenAILLM
import os

memory = SQLiteConversationMemory(
path=os.path.expanduser("~/.synapsekit/agent-memory.db"),
session_id="user-123",
)

agent = ReActAgent(
llm=OpenAILLM(model="gpt-4o"),
tools=[WebSearchTool()],
memory=memory,
)

# First session
result1 = await agent.run("My name is Alice. Remember that.")

# Later session — same session_id, history is loaded from SQLite
result2 = await agent.run("What is my name?")
print(result2.output) # "Your name is Alice."

7. Agent with BudgetGuard Cost Cap​

Hard-cap how much an agent can spend per run.

from synapsekit.agents import ReActAgent
from synapsekit.agents.tools import WebSearchTool, CalculatorTool
from synapsekit.llms.openai import OpenAILLM
from synapsekit import BudgetGuard, BudgetLimit, BudgetExceededError

guard = BudgetGuard(BudgetLimit(per_request=0.10)) # 10 cents max per run

agent = ReActAgent(
llm=OpenAILLM(model="gpt-4o", budget_guard=guard),
tools=[WebSearchTool(), CalculatorTool()],
max_steps=10,
)

try:
result = await agent.run("Analyse all the top 100 companies and rank them by revenue")
except BudgetExceededError:
print("This query is too expensive — try a more focused question.")

8. Agent with ContentFilter Guardrails​

Block harmful input before it reaches the LLM.

from synapsekit.agents import ReActAgent
from synapsekit.guardrails import ContentFilter, ContentPolicy
from synapsekit.llms.openai import OpenAILLM

agent = ReActAgent(llm=OpenAILLM(model="gpt-4o-mini"), tools=[])

content_filter = ContentFilter(
policy=ContentPolicy(block_hate_speech=True, block_violence=True)
)

async def safe_agent(user_input: str) -> str:
check = await content_filter.check(user_input)
if check.blocked:
return f"I can't help with that: {check.reason}"
result = await agent.run(user_input)
return result.output

response = await safe_agent("How do I build a RAG pipeline?")

9. Multi-Agent: Supervisor + 2 Workers​

A supervisor delegates subtasks to specialised workers and synthesises their outputs.

from synapsekit.agents import FunctionCallingAgent, SupervisorAgent
from synapsekit.agents.tools import WebSearchTool, CalculatorTool
from synapsekit.llms.openai import OpenAILLM

llm = OpenAILLM(model="gpt-4o")

researcher = FunctionCallingAgent(
name="researcher",
llm=llm,
tools=[WebSearchTool()],
system_prompt="You are a research specialist. Find accurate, up-to-date information.",
)

analyst = FunctionCallingAgent(
name="analyst",
llm=llm,
tools=[CalculatorTool()],
system_prompt="You are a data analyst. Perform quantitative analysis and calculations.",
)

supervisor = SupervisorAgent(
llm=llm,
workers=[researcher, analyst],
system_prompt="Delegate tasks to the right worker and synthesise their outputs.",
)

result = await supervisor.run("What was NVIDIA's revenue growth rate over the last 3 years?")
print(result.output)

10. Multi-Agent: HandoffChain​

Pass the task sequentially from one agent to the next, each specialising in a different step.

from synapsekit.agents import FunctionCallingAgent
from synapsekit.multi_agent import HandoffChain
from synapsekit.agents.tools import WebSearchTool, FileWriteTool
from synapsekit.llms.openai import OpenAILLM

llm = OpenAILLM(model="gpt-4o")

researcher = FunctionCallingAgent(
name="researcher", llm=llm, tools=[WebSearchTool()],
system_prompt="Research the topic thoroughly.",
)
writer = FunctionCallingAgent(
name="writer", llm=llm, tools=[],
system_prompt="Write a clear, structured report from the research.",
)
editor = FunctionCallingAgent(
name="editor", llm=llm, tools=[FileWriteTool(base_dir="/tmp")],
system_prompt="Polish the report and save it as report.md.",
)

chain = HandoffChain(agents=[researcher, writer, editor])
result = await chain.run("Write a report on the state of open-source LLMs in 2026")
print(result.output)

11. Multi-Agent: Crew with Roles​

Define a crew of agents with explicit roles, backstories, and a shared task.

from synapsekit.multi_agent import Crew, CrewMember, Task
from synapsekit.agents.tools import WebSearchTool
from synapsekit.llms.openai import OpenAILLM

llm = OpenAILLM(model="gpt-4o")

crew = Crew(
members=[
CrewMember(
role="Senior Research Analyst",
goal="Find comprehensive information on the given topic",
backstory="Expert at synthesising complex technical research",
tools=[WebSearchTool()],
llm=llm,
),
CrewMember(
role="Technical Writer",
goal="Transform research into clear documentation",
backstory="Specialist in making complex topics accessible",
llm=llm,
),
],
task=Task(
description="Research and document the differences between RAG and fine-tuning",
expected_output="A structured markdown document with clear sections",
),
)

result = await crew.run()
print(result.output)

12. Agent via MCP Server​

Connect your agent to an external tool server using the Model Context Protocol.

from synapsekit.agents import FunctionCallingAgent
from synapsekit.agents.mcp import MCPClient
from synapsekit.llms.anthropic import AnthropicLLM

# Connect to a local or remote MCP server
mcp = MCPClient(server_url="http://localhost:3000/mcp")
await mcp.connect()

tools = await mcp.list_tools() # discover available tools

agent = FunctionCallingAgent(
llm=AnthropicLLM(model="claude-3-5-sonnet-20241022"),
tools=tools,
max_steps=10,
)

result = await agent.run("List all open GitHub issues labeled 'bug'")
print(result.output)
await mcp.disconnect()

13. Agent with @eval_case Quality Test​

Write automated quality tests for your agent using the eval framework.

from synapsekit.agents import FunctionCallingAgent
from synapsekit.agents.tools import CalculatorTool
from synapsekit.llms.openai import OpenAILLM
from synapsekit.evaluation import eval_case

agent = FunctionCallingAgent(
llm=OpenAILLM(model="gpt-4o-mini"),
tools=[CalculatorTool()],
)

@eval_case(metric="answer_relevancy", threshold=0.9)
async def test_calculator_agent():
result = await agent.run("What is 15% of 240?")
return result.output

@eval_case(metric="faithfulness", threshold=0.85)
async def test_no_hallucination():
result = await agent.run("Multiply 123 by 456")
return result.output

# Run with: pytest tests/ -q

14. Testing Agent with MockLLM (No API Calls)​

Write fast, deterministic unit tests without making real LLM API calls.

import pytest
from synapsekit.agents import FunctionCallingAgent
from synapsekit.agents.tools import CalculatorTool
from synapsekit.llms.mock import MockLLM

@pytest.mark.asyncio
async def test_agent_uses_calculator():
mock_llm = MockLLM(responses=[
# First call: agent decides to use the calculator
'{"tool": "calculator", "input": {"expression": "42 * 7"}}',
# Second call: agent formulates the final answer
"The result is 294.",
])

agent = FunctionCallingAgent(
llm=mock_llm,
tools=[CalculatorTool()],
)

result = await agent.run("What is 42 times 7?")
assert "294" in result.output
assert mock_llm.call_count == 2

15. Agent Node in a Graph Workflow​

Embed an agent as a node inside a larger state graph workflow.

from synapsekit.graph import StateGraph
from synapsekit.agents import FunctionCallingAgent
from synapsekit.agents.tools import WebSearchTool
from synapsekit.llms.openai import OpenAILLM

llm = OpenAILLM(model="gpt-4o")
research_agent = FunctionCallingAgent(llm=llm, tools=[WebSearchTool()], max_steps=5)

async def research_node(state: dict) -> dict:
result = await research_agent.run(state["topic"])
return {**state, "research": result.output}

async def summarise_node(state: dict) -> dict:
summary = await llm.complete(
f"Summarise this research in 3 bullet points:\n\n{state['research']}"
)
return {**state, "summary": summary}

workflow = StateGraph()
workflow.add_node("research", research_node)
workflow.add_node("summarise", summarise_node)
workflow.add_edge("research", "summarise")
workflow.set_entry_point("research")
workflow.set_finish_point("summarise")

graph = workflow.compile()
result = await graph.run({"topic": "Advances in multimodal LLMs in 2026"})
print(result["summary"])