Agents
Autonomous AI systems with reasoning, tool orchestration, and memory
When you need an AI system that can analyze research papers, decide which sources to consult, retrieve relevant context from previous work, and synthesize everything into coherent insights — you need more than predefined steps. You need reasoning. You need tool selection. You need autonomous decision-making.
AGNT5 agents solve this problem. Agents are LLM-powered systems that reason about tasks, dynamically select tools, and maintain memory across conversations. You define capabilities and goals, and the agent figures out how to accomplish them.
Example
from agnt5 import Agent, tool
@tool.function(auto_schema=True)
def search_papers(query: str, year_from: int = 2020) -> list[dict]:
"""Search academic papers by query and year."""
return arxiv_search(query, year_from)
@tool.function(auto_schema=True)
def extract_key_findings(paper_text: str) -> list[str]:
"""Extract key findings from a research paper."""
return llm_extract(paper_text)
agent = Agent(
name="research_assistant",
model="openai/gpt-4o",
instructions="""You are a research assistant specializing in AI.
Research process:
1. Search for relevant recent papers
2. Extract key findings from each
3. Identify common themes
4. Synthesize into a comprehensive summary""",
tools=[search_papers, extract_key_findings]
)
result = await agent.run("What are the latest developments in AI alignment?")
When you call it:
from agnt5 import Client
client = Client()
result = client.run_agent("research_assistant", {
"query": "What are the latest developments in AI alignment?"
})
AGNT5:
- Agent reads the query and decides it needs to search papers
- Calls
search_papers("AI alignment", 2020)— gets 15 papers - Decides to extract findings from the most relevant 5 papers
- Calls
extract_key_findings5 times in parallel - Identifies themes across the findings
- Synthesizes a comprehensive summary
The agent reasons through each step, chooses which tools to use, and adapts based on results. You didn’t script this flow — the LLM orchestrated it.
This is autonomous orchestration: the agent plans, executes, and adapts without predefined steps.
Agent Instructions
Agents follow instructions you provide. Clear instructions produce focused behavior:
from agnt5 import Agent
agent = Agent(
name="code_reviewer",
model="openai/gpt-4o",
instructions="""You are an expert code reviewer for Python.
Review process:
1. Analyze code for complexity, duplication, and style
2. Check for security vulnerabilities
3. Suggest improvements with code examples
4. Prioritize: security > correctness > performance > style
Be constructive and explain your reasoning.""",
tools=[analyze_complexity, check_security, suggest_refactoring]
)
review = await agent.run("Review this authentication module")
Instructions provide:
- Role definition — What the agent is (research assistant, code reviewer, data analyst)
- Process guidance — Steps to follow or approach to take
- Constraints — What to prioritize, avoid, or limit
- Output format — How to structure responses
Instructions shape how the agent reasons, but the agent decides when and how to use tools dynamically.
Agent Tools
Tools are the capabilities you give agents. Agents select and execute tools based on their reasoning:
from agnt5 import Agent, tool
@tool.function(auto_schema=True)
def search_documentation(query: str, language: str = "python") -> list[dict]:
"""Search programming language documentation."""
return doc_search(query, language)
@tool.function(auto_schema=True)
def execute_code(code: str, language: str = "python") -> dict:
"""Execute code and return output."""
return code_runner.run(code, language)
@tool.function(auto_schema=True)
def analyze_error(error_message: str, code: str) -> dict:
"""Analyze error message and suggest fixes."""
return error_analyzer.analyze(error_message, code)
agent = Agent(
name="coding_assistant",
model="openai/gpt-4o",
instructions="""You are a coding assistant.
Use search_documentation to find API references.
Use execute_code to test examples.
Use analyze_error when code fails.""",
tools=[search_documentation, execute_code, analyze_error]
)
result = await agent.run("Write a function to parse JSON, then test it")
The agent:
- Searches documentation for JSON parsing APIs
- Writes a function based on the docs
- Executes the code to test it
- If errors occur, analyzes them and fixes the code
Tools are implemented as functions (AGNT5’s atomic unit). When an agent calls a tool, it’s executing a durable, checkpointed function.
Human-in-the-Loop Tools
Agents can pause workflow execution to request input from users using built-in HITL tools:
from agnt5 import Agent, AskUserTool, RequestApprovalTool, workflow, WorkflowContext
@workflow(chat=True)
async def deployment_workflow(ctx: WorkflowContext, changes: dict) -> dict:
# Agent with approval capability
deploy_agent = Agent(
name="deployment_assistant",
model="openai/gpt-4o",
instructions="""You help deploy code safely.
Process:
1. Analyze deployment risks
2. Use request_approval to get user confirmation
3. If approved, proceed with deployment
4. If rejected, suggest alternatives""",
tools=[RequestApprovalTool(ctx)]
)
result = await deploy_agent.run(
f"Review and deploy: {changes}",
context=ctx
)
return {"response": result.output}
AskUserTool - Agent requests text input:
@workflow(chat=True)
async def research_workflow(ctx: WorkflowContext, topic: str) -> dict:
research_agent = Agent(
name="researcher",
model="openai/gpt-4o",
instructions="""Research assistant that asks for clarification.
If the topic is broad, use ask_user to ask what specific
aspect the user wants to focus on.""",
tools=[AskUserTool(ctx), search_papers]
)
result = await research_agent.run(
f"Research: {topic}",
context=ctx
)
return {"findings": result.output}
RequestApprovalTool - Agent requests approval:
@workflow(chat=True)
async def content_workflow(ctx: WorkflowContext, article: str) -> dict:
editor_agent = Agent(
name="editor",
model="openai/gpt-4o",
instructions="""Content editor that reviews before publishing.
Use request_approval before any publishing action.""",
tools=[RequestApprovalTool(ctx), analyze_content]
)
result = await editor_agent.run(
f"Review and publish: {article}",
context=ctx
)
return {"status": result.output}
How HITL tools work:
- Agent reasons and decides to call
ask_userorrequest_approval - Tool pauses the workflow via
ctx.wait_for_user() - Platform returns pause information to frontend (HTTP 202)
- User responds through chat interface
- Workflow resumes with cached user response
- Agent receives response and continues reasoning
The agent autonomously decides when to ask for input based on its instructions and reasoning — no hardcoded pause points.
Important constraints:
- HITL tools only work within workflows (
@workflowdecorator required) - Must pass
WorkflowContextto tool constructor - Workflows must be marked with
chat=Truefor session support - Tools follow same deterministic replay as all workflow operations
See Human-in-the-Loop Workflows for complete details on pause/resume mechanics.
Agent Memory
Agents can maintain long-term knowledge across conversations:
from agnt5 import Agent, Memory
memory = Memory(service=VectorMemoryService())
# Store persistent knowledge
await memory.store("user_expertise", "Expert in distributed systems and Go")
await memory.store("coding_preferences", "Prefers small functions, comprehensive tests")
agent = Agent(
name="coding_assistant",
model="openai/gpt-4o",
instructions="You are a coding assistant. Adapt to user's experience and preferences.",
tools=[code_generation, review_code],
memory=memory
)
# Agent recalls stored knowledge automatically
result = await agent.run("Help me implement a consensus algorithm")
# Uses stored expertise level and preferences
Memory provides:
- Persistent knowledge — Facts that survive beyond single conversations
- Context retrieval — Automatically recalls relevant information
- Preference learning — Remembers user preferences and patterns
- Knowledge accumulation — Builds understanding over time
Memory is backed by entities (AGNT5’s stateful objects), so knowledge persists across crashes and redeploys.
Agent Sessions
Sessions provide conversation context. Multiple turns in a session share history:
from agnt5 import Agent, Session
session = Session(
id="research-session-001",
user_id="researcher-123",
metadata={"project": "ai-safety"}
)
agent = Agent(
name="research_assistant",
model="openai/gpt-4o",
instructions="You are a research assistant. Track conversation history.",
tools=[search_papers, summarize_paper],
session=session
)
# First turn
result1 = await agent.run("Find papers on AI alignment from 2023")
# Second turn — agent has context from first turn
result2 = await agent.run("Now analyze the top 3 for common themes")
# Third turn — full conversation history available
result3 = await agent.run("Compare these themes to the 2022 papers we discussed last week")
Sessions enable:
- Conversational continuity — Agent remembers previous turns
- Multi-turn reasoning — Build on earlier conclusions
- Context sharing — Multiple agents can share a session
Sessions use entities for state management, providing single-writer consistency per session.
Streaming Agents
Stream agent execution in real-time for responsive UX:
async for event in agent.stream("Analyze this dataset", session=session):
match event.type:
case "thinking":
print(f"🤔 Reasoning: {event.content}")
case "tool_call":
print(f"🔧 Calling {event.tool_name}({event.arguments})")
case "tool_result":
print(f"✓ Result: {event.result}")
case "response":
print(f"💬 Response: {event.content}")
case "error":
print(f"❌ Error: {event.error}")
Streaming shows the agent’s reasoning process as it happens. Users see tool calls, intermediate results, and final responses in real-time.
Multi-Agent Coordination
Agents can work together through two patterns: agents as tools and explicit handoffs.
Agents as Tools
Use specialist agents as tools for a coordinator:
from agnt5 import Agent
# Specialist agents
data_analyst = Agent(
name="data_analyst",
model="openai/gpt-4o",
instructions="Analyze datasets and extract statistical insights.",
tools=[calculate_statistics, generate_visualizations]
)
researcher = Agent(
name="researcher",
model="openai/gpt-4o",
instructions="Search academic literature and summarize findings.",
tools=[search_papers, extract_insights]
)
# Coordinator uses specialists as tools
coordinator = Agent(
name="coordinator",
model="openai/gpt-4o",
instructions="""You coordinate research projects.
Use data_analyst for statistical analysis.
Use researcher for literature review.""",
tools=[data_analyst, researcher] # Pass agents directly as tools
)
result = await coordinator.run(
"Analyze our user engagement data and compare it to industry research"
)
The coordinator decides when to invoke each specialist. Results flow back to the coordinator for synthesis.
Explicit Handoffs
Transfer control from one agent to another:
from agnt5 import Agent, handoff
# Specialist agents
technical_support = Agent(
name="technical_support",
model="openai/gpt-4o",
instructions="Diagnose and resolve technical issues.",
tools=[run_diagnostics, apply_fixes]
)
billing_support = Agent(
name="billing_support",
model="openai/gpt-4o",
instructions="Handle billing questions and process refunds.",
tools=[lookup_invoice, process_refund]
)
# Triage agent with handoff capability
triage = Agent(
name="triage",
model="openai/gpt-4o",
instructions="Classify support requests and hand off to specialists.",
handoffs=[
handoff(technical_support, "Transfer to technical support"),
handoff(billing_support, "Transfer to billing support")
]
)
# User's request
result = await triage.run("I was charged twice for my subscription", session=session)
# Automatically routes to billing_support and returns its response
When to use each pattern:
- Agents as tools: Coordinator synthesizes results from multiple specialists
- Explicit handoffs: Route to specialist and delegate full responsibility
Both patterns share context through sessions. State set in one agent is available to others in the same session.
Agent Planning
Preview an agent’s execution plan before running:
# Get plan without executing
plan = agent.plan("Research quantum computing applications in cryptography")
print(f"Estimated steps: {len(plan.steps)}")
for step in plan.steps:
print(f"- {step.type}: {step.description}")
if step.tool:
print(f" Tool: {step.tool.name}")
# Review and execute if approved
if user_approves(plan):
result = await agent.run("Research quantum computing applications in cryptography")
Planning shows what the agent intends to do. Useful for:
- Verification — Check the agent’s approach before execution
- Cost estimation — Estimate LLM calls and tool executions
- Debugging — Understand why an agent chose a particular strategy
Calling Agents
From Client SDK
from agnt5 import Client
client = Client()
# Run agent and wait for result
result = client.run_agent("research_assistant", {
"query": "Analyze recent trends in transformer architectures"
})
From Workflows
Agents integrate with workflows for hybrid orchestration:
from agnt5 import workflow, WorkflowContext
@workflow()
async def research_and_report(ctx: WorkflowContext, topic: str) -> dict:
# Step 1: Agent conducts research (autonomous)
research = await ctx.task(research_agent.run, topic)
# Step 2: Generate visualizations (scripted)
charts = await ctx.task(generate_charts, research["data"])
# Step 3: Agent writes report using visualizations (autonomous)
report = await ctx.task(writer_agent.run, {
"findings": research,
"charts": charts
})
return {"report": report, "supporting_data": research}
This combines autonomous agent reasoning with scripted workflow steps. Agents handle open-ended tasks, workflows ensure durable orchestration.
When to Use Agents
Use agents when you need:
- Autonomous reasoning — Task requires dynamic decision-making
- Adaptive tool selection — Don’t know which tools will be needed in advance
- Conversational interaction — Multi-turn dialogue with context
- Long-term memory — Persistent knowledge across sessions
Use workflows instead when:
- Predefined steps with explicit control flow
- No LLM reasoning required
- Cost-sensitive operations (workflows avoid unnecessary LLM calls)
Use functions instead when:
- Single, deterministic operation
- No tool orchestration needed
- Stateless computation
Agents as Foundation
Agents sit at the top of AGNT5’s abstraction hierarchy:
- Functions — Atomic operations (search, analyze, generate)
- Entities — Persistent state (memory, session history)
- Workflows — Scripted orchestration (multi-step processes)
- Agents — Autonomous reasoning (dynamic tool selection and planning)
What makes agents powerful is how they combine these primitives:
- Tools are functions — durable, retryable, observable
- Memory uses entities — persistent state with single-writer consistency
- Sessions use entities — conversation history that survives crashes
- Agent execution can be orchestrated in workflows — hybrid autonomy
When you give an agent tools, you’re giving it access to AGNT5 functions. When it remembers context, it’s using entities. When you orchestrate multiple agents, you can use workflows. The abstractions compose.
This is different from standalone LLM frameworks in two ways: every operation is durable, and agents integrate with scripted orchestration. An agent’s tool calls are checkpointed. Its memory persists across failures. You can combine agent reasoning with workflow reliability.
When an agent crashes mid-execution, when a tool call times out, when you need to coordinate autonomous agents with deterministic steps — the platform handles recovery. This is what makes agent-based applications reliable: reasoning is autonomous, but execution is durable. That’s the foundation for production AI systems.