AI Agents¶
amsdal_ml includes two agent implementations that combine LLM reasoning with tool execution to answer complex questions.
DefaultQAAgent (ReAct)¶
The ReAct agent follows a Reasoning + Acting loop: it thinks about the question, decides which tool to call, observes the result, and repeats until it has an answer.
from amsdal_ml.agents.default_qa_agent import DefaultQAAgent
from amsdal_ml.agents.python_tool import PythonTool
from amsdal_ml.ml_models.openai_model import OpenAIModel
async def search_customers(query: str) -> str:
"""Search the customer database."""
# your search logic
return results
agent = DefaultQAAgent(
model=OpenAIModel(model_name='gpt-4o'),
tools=[PythonTool(search_customers, name='search_customers', description='Search the customer database.')],
max_steps=6,
)
output = await agent.arun('Find all customers with overdue invoices')
print(output.answer)
print(output.used_tools) # list of tools that were called
Configuration¶
| Parameter | Default | Description |
|---|---|---|
max_steps |
6 |
Maximum reasoning steps before stopping |
per_call_timeout |
20.0 |
Timeout per tool call (seconds) |
on_parse_error |
RAISE |
What to do on parse errors: RAISE or RETRY |
enable_stop_guard |
True |
Enable guard against premature stopping |
How It Works¶
- The agent receives the question and a list of available tools with descriptions
- At each step, the LLM generates a Thought (reasoning) and an Action (tool call with arguments)
- The tool is executed and the Observation (result) is added to the agent's scratchpad
- Steps repeat until the LLM produces a Final Answer or
max_stepsis reached
FunctionalCallingAgent¶
Uses the LLM's native function-calling API (e.g., OpenAI tool_calls) instead of text-based parsing. More reliable for structured tool invocation.
from amsdal_ml.agents.functional_calling_agent import FunctionalCallingAgent
from amsdal_ml.agents.python_tool import PythonTool
from amsdal_ml.ml_models.openai_model import OpenAIModel
agent = FunctionalCallingAgent(
model=OpenAIModel(model_name='gpt-4o'),
tools=[PythonTool(search_customers, name='search_customers', description='Search the customer database.')],
max_steps=10,
)
output = await agent.arun('How many active customers do we have?')
print(output.answer)
Key Differences from ReAct¶
| Aspect | DefaultQAAgent (ReAct) | FunctionalCallingAgent |
|---|---|---|
| Tool invocation | Regex-parsed from LLM text | Native function calling API |
| Reliability | May need retries on parse errors | More reliable parsing |
| Message history | Scratchpad-based | Full message history |
| Streaming | Step-by-step streaming | Buffers full result, yields once |
| Multi-tool | One tool per step | Multiple tools per step |
Streaming¶
Both agents support streaming via astream():
async for chunk in agent.astream('Summarize customer activity'):
print(chunk, end='', flush=True)
AgentOutput¶
Both agents return an AgentOutput:
| Field | Type | Description |
|---|---|---|
answer |
str |
The agent's final answer |
used_tools |
list[str] |
Names of tools that were called |
citations |
list[dict[str, Any]] |
Source citations (if available) |
Attachments¶
Both agents support file attachments:
from amsdal_ml.fileio.base_loader import FileAttachment
attachment = FileAttachment(
type='file_id',
content='file-abc123',
mime_type='application/pdf',
)
output = await agent.arun(
'Summarize this document',
attachments=[attachment],
)