Custom Tools
Tools are packaged as Plugins. A Plugin acts as a container that can include multiple individual tools, their logic, and their external dependencies. In the portal interface, an uploaded plugin is referred to as a Tool Group.
For system tools and how to add them, see System Tools List
Prerequisites
- Basic understanding of AI agents
- Familiarity with YAML configuration
- Python programming knowledge for custom tool implementation
- Access to the Flametree portal for plugin management
How to Create Custom Tool
To create custom tool, follow these steps:
- Create tool configuration and implementation – Define the tool in YAML (describe its purpose and required inputs) and implement the business logic in Python that executes the function and returns the result.
- Upload Plugin to the portal
- Add tools to workflow in appropriate states
- Test in Playground before production
Tip: Pay attention to the examples of ready-made custom tools
Create Tool Configuration and Implementation
Before you begin, review the recommended file structure for a custom tool:
my_crm_plugin/
├── crm_integration.yaml # Tool configuration
├── requirements.txt # External Python dependencies
└── submodules/ # Folder for your Python code
└── crm_logic.py # Implementation logic
Tool Configuration (YAML)
The YAML file defines the interface for the AI agent. It tells the agent what the tool does and what parameters it needs.
Example crm_integration.yaml:
kind: Plugin
name: CRM Integration Plugin
description: Tools for posting leads to external CRM
dependencies:
- submodules/crm_logic.py
- requirements.txt # If you have a requirements.txt, the system installs packages automatically
tools:
- kind: Tool
object_name: PostLeadToCRM
name: post_lead
description: Sends client contact details to the corporate CRM system. Use this when the user confirms their contact info.
implementation: crm_logic.post_lead
parameters:
- name: client_name
type: string
description: Name of the client
- name: client_email
type: string
description: Email of the client
See the Reference: Tool Configuration Fields section below for a complete description of all required and optional YAML fields.
External Dependencies
If your tool uses external libraries such as pytz, pandas, and others, list them in the requirements.txt file and specify their versions.
pytz==2024.1
aiohttp==3.9.3
Tool Implementation (Python)
A tool implementation is an async Python function. The system maps the YAML parameters directly to the function arguments.
from loguru import logger
async def create_ticket(issue_summary: str, issue_details: str, **kwargs):
"""
Implementation logic for CreateSupportTicket.
YAML parameters (issue_summary, issue_details) are passed as direct arguments.
System state is passed via **kwargs.
"""
# 1. Log the action
logger.info(f"Processing ticket: {issue_summary}")
# 2. Execute business logic (e.g., API call)
ticket_id = "TICKET-123"
# 3. Return a string to the Agent
return f"Support ticket created successfully with ID: {ticket_id}"
The function must:
- Use
async def - Accept parameters that match the YAML definition.
- Include
**kwargsto receive system-provided arguments - Return a string that describes the result. The agent uses this value to continue the conversation.
⚠️ Note: A tool can also return a structured object. This allows you to send a system message directly to the user instead of continuing the agent flow.
The system automatically injects standard arguments into **kwargs at runtime. These provide access to the session state, conversation history, and more. See the Reference: Standard Arguments section below for the complete list.
Troubleshooting
If the agent calls the tool but it fails or does not behave as expected:
- Check the Python implementation for syntax or runtime errors.
- Verify that all required parameters are defined in the YAML configuration.
- Ensure the implementation function is declared using
async def. - Review agent logs for detailed error messages.
Best Practices
Writing Tool Descriptions
| Do’s ✅ | Don’ts ❌ |
|---|---|
| - Describe what the tool does, not when to use it - Be specific about the tool's action - Use clear, simple language | - Include usage instructions in the description - Make descriptions too long or complex |
💡 Tip: Tool parameters are strictly validated based on their YAML definition. Clear and accurate parameter descriptions help the agent call tools correctly.
Good Example:
description: Creates a support ticket in Jira with the provided issue details
Not So Good Example:
description: Use this when customer has a problem and needs help. This tool will create a ticket but only if the issue is serious enough.
When to Use Tools Instructions
Put instructions about when to use tools in the workflow state description, not in the tool description:
description: |
## Tool Usage Guidelines
- Use CreateTicket only after confirming the issue with the customer
- Always get customer contact information before creating a ticket
Naming Conventions
object_name: PascalCase, no spaces. For example,SendEmailTemplatename: Same asobject_namefor consistency- Parameters: lowercase with underscores. For example,
customer_email
Design Principles
- Separate logic and actions — use skills for logic, tools for concrete actions
- Describe only what the tool does — usage conditions go in the workflow
- Design tools and skills as reusable components
- Avoid duplicate
object_namevalues — they will cause conflicts
Handling Similar Tools
When you have similar tools, for example, Get Active Cards vs Get Inactive Cards:
- Keep descriptions similar in structure
- Clearly highlight the key difference
- Maintain consistent parameter naming
Reference: Tool Configuration Fields
Each tool definition in the YAML file requires the following fields:
Required Fields
| Field | Description |
|---|---|
kind | Must be set to Tool |
object_name | Unique identifier for the tool. Use PascalCase and no spaces |
name | Name the agent uses to invoke the tool |
description | Clear description of what the tool does |
parameters | List of input parameters. Use an empty list ([]) if the tool takes no input |
implementation | Fully qualified reference to the Python function that implements the tool |
Parameter Fields
| Field | Description |
|---|---|
name | Parameter identifier |
type | Data type (string, integer, boolean, and others.) |
description | Clear description of the expected value |
optional | Indicates whether the parameter is optional. Defaults to false |
How to define and receive tool parameters:
If a tool requires input ,for example, an email address, ID, or date, define each parameter in the parameters list.
parameters:
- name: email
type: string
description: "Email address to send the notification to"
- name: customer_name
type: string
description: "Customer name"
💡 Tip: All YAML parameters are automatically passed to the Python function as named arguments (**kwargs).
Reference: Standard Arguments
These standard keyword arguments are injected at runtime into every tool's kwargs. They allow you to control the session, access memory, and read configuration.
| Argument | Purpose | Type |
|---|---|---|
agent_name | Identifies which agent triggered the tool | str |
raw_agent_answer | Original JSON string of tool arguments before parsing | str |
stack | Agent's local stack — used to push result containers that control flow (send message, finish, interrupt) | Stack |
heap | Session-scoped shared dictionary for storing and reading arbitrary data | dict |
scratchpad | Agent's working memory — records of thoughts, observations, and messages | ScratchpadBridge or ScratchpadMemory |
conversation_memory | Full chat history between the user and the system | RecordsMemory |
system_parameters | Runtime session parameters (e.g. client_id) | dict |
environment_json | JSON-encoded string of environment configuration | str |
flags_config | Session lifecycle flags (first run, ended, interrupt behavior) | FlagsConfig |
flow_config | Complete flow configuration object — the most powerful entry point, contains references to all session-level objects | FlowConfig |
agent_config | Configuration of the calling agent (prompt template, available tools, speech settings) | AgentConfig |
available_skills_dict | Dictionary of all loaded skills keyed by name | dict[str, Skill] |
knowledge_vdb | FAISS-backed knowledge base for document search | KnowledgeVDB |
tools_vdb | FAISS-backed tool index for semantic tool search (non-None only for search_tool) | ToolsVDB | None |
automatic_operation | Whether the agent is running in automatic mode (no human in the loop) | bool |
tool_call_id | LLM-assigned identifier for this tool call (tool-calling agents only) | str |
tool_call_type | Type field from the LLM tool call (tool-calling agents only) | str |
Note on tool-calling vs traditional agents: The arguments
tool_call_idandtool_call_typeare available only when the tool is invoked by aBaseAgentToolCallingsubclass. They are not passed byBaseAgentsubclasses.
Argument Details
agent_name
Identifies which agent triggered the tool.
Example: StatefulToolCallingAgent, SingleStatefulOutboundAgent
In tool-calling mode the calling agent is typically StatefulToolCallingAgent. In traditional mode it is SingleStatefulOutboundAgent. Form-related tools may also be called from FormToolCallingAgent or SingleFormAgent.
raw_agent_answer
The original JSON string of tool arguments exactly as generated by the LLM, before parsing.
Example: '{"message": "Hello!", "currency": "EUR"}'
By the time implementation() runs, the system has already parsed this JSON into individual keyword arguments. In most cases, you do not need to use this value directly.
Use it for:
- Debugging
- Logging raw LLM output
- Inspecting malformed arguments
stack
The agent’s execution stack. This is a list-like asynchronous data structure used to push containers that control conversation flow.
Use stack to:
- Send a message to the user
- End the agent loop
- Interrupt processing
- Trigger state transitions
This is the primary mechanism for a tool to influence flow behavior.
Example:
from src.utils.containers import AskUserContainer
await stack.push(AskUserContainer("CasualAnswer", "Here is your balance: 1500 EUR"))
Common container types to push:
| Container | Effect |
|---|---|
AskUserContainer("CasualAnswer", text) | Send a message to the user and wait for response |
FinalAnswerContainer(...) | Send a final answer and stop the execution |
InterruptProcessContainer(...) | Interrupt the current process |
ProceedContainer(...) | Delegate the task downstream |
StateContainer(...) | Trigger a state transition |
heap
A session-scoped shared dictionary that persists for the lifetime of the session.
Any tool can read from or write to heap. Use it for inter-tool or inter-agent communication within the same session.
Use heap for:
- Temporary shared state
- Cross-tool coordination
- Passing values between workflow steps
Example:
# Store data for later use by another tool or agent
heap["last_transfer_id"] = "TXN-12345"
# Read the state machine
statemachine = heap.get("statemachine")
scratchpad
The agent's working memory that stores the sequence of messages (AI messages, tool results, human messages) forming the agent's reasoning context.
In most cases tools do not need to touch the scratchpad directly. Returning a string from implementation() automatically creates an Observation. Use scratchpad only when you need custom multi-record updates or direct message manipulation.
Access the scratchpad only if you need:
- Custom multi-record updates
- Direct manipulation of reasoning messages
- Advanced control over agent memory behavior
conversation_memory
The complete chat history between the user and the system, stored as a RecordsMemory instance.
While the scratchpad contains reasoning records, conversation_memory stores the actual user–system dialogue.
Use it when you need access to:
- Full message history
- Prior user inputs
- Context outside the current reasoning step
Example:
records = conversation_memory.get_records() # list[str] of recent messages
formatted = conversation_memory.get_formatted_memory() # single formatted string
system_parameters
Runtime parameters for the current session. Typically includes client identifiers and session metadata.
Use this argument to access:
- Client identifiers
- Session identifiers
- Other runtime metadata
Example:
client_id = system_parameters.get("client_id")
session_id = system_parameters.get("session_id")
environment_json
A JSON-encoded string that contains environment configuration parameters.
These values correspond to flow_config.environment_parameters, but are pre-serialized.
Use this argument if you need raw JSON instead of the parsed dictionary.
flags_config
Session lifecycle flags that indicate the current state of the session.
Fields:
| Field | Type | Default | Description |
|---|---|---|---|
interrupt_at_final_answer | bool | True | Whether the agent loop should stop when a FinalAnswerContainer is pushed |
first_run | bool | True | True during the first run of the session (before the first user message is processed) |
is_session_ended | bool | False | True if the session has been explicitly ended |
Example:
if flags_config.first_run:
return "Welcome! This is your first interaction."
Use flags_config to implement conditional logic based on session lifecycle state.
flow_config
The complete flow configuration object. This is the most powerful argument available to tools.
It provides access to nearly all session-level components.
Key Fields:
| Field | Type | Description |
|---|---|---|
global_heap | dict | Session-scoped shared data (same object as the heap argument) |
global_continuous_heap | dict | Persistent across session resets; contains "_session_id" |
conversation_memory | RecordsMemory | Chat history (same object as the conversation_memory argument) |
environment_parameters | dict | Environment config (parsed version of environment_json) |
system_parameters | dict | Runtime params (same object as the system_parameters argument) |
flags_config | FlagsConfig | Same object as the flags_config argument |
global_event_manager | BaseEventManager | Pub/sub event system |
current_state | StateConfig | None | Current state machine state |
form_info | list[FormInfoField] | None | Form field definitions |
Example — accessing session ID:
session_id = flow_config.global_continuous_heap.get("_session_id")
Use flow_config only when you need cross-cutting access to multiple session-level objects.
agent_config
Configuration of the calling agent. The values are potentially overridden by the current state machine state.
Includes:
- name
- available_tools
- prompt_template
- Speech or output settings
available_skills_dict
A dictionary of loaded skill instances, keyed by skill name.
Primarily used by internal tools such as SearchTool.
knowledge_vdb
Vector database for searching knowledge connected to your agent.
Example:
docs = knowledge_vdb.get_relevant_documents(query="What are the transfer fees?", k=5)
for doc in docs:
print(doc.page_content, doc.metadata)
Use this argument to perform semantic document retrieval.
tools_vdb
Vector database indexing tool descriptions for semantic tool search. This is set to a non-None value only when the tool being called is search_tool.
automatic_operation
Indicates whether the agent runs in automatic mode.
- True — no human is in the loop
- False — human interaction is expected
Use this flag when tool behavior must differ between automated and interactive execution.
tool_call_id and tool_call_type
Identifier and type fields from the LLM’s structured tool call response.
These arguments are available only in tool-calling mode.
They are primarily used internally to associate tool responses with the corresponding LLM call.