Custom tools
Developer-defined Python functions for specialized business logic.
Custom tools are developer-defined Python functions that extend agent capabilities at runtime. Once created, a custom tool becomes part of the agent's available toolset. The agent autonomously determines when and how to invoke the tool based on its instructions, context, and user input.
Common use cases
Custom tools are most often used to:
- Access internal knowledge stores — proprietary data sources or repositories unique to your business
- Transform or enrich data — clean, format, or augment text, numerical, or structured data
- Execute specialized business logic — operations tailored to specific organizational needs
Quick start
1. Define your Python function
Each custom tool must be implemented in its own .py file. The following example creates a simple tool that adds two integers:
def add(a: int, b: int) -> int:
"""Add two numbers together.
This function takes two integers and returns their sum.
Use this function when you need to perform a simple addition
operation on exactly two integer inputs.
Args:
a (int): First number.
b (int): Second number.
Returns:
int: The total of a and b.
"""
return a + b2. Create the custom function
from seekrai import SeekrFlow
client = SeekrFlow()
add_function = client.agents.custom_functions.create(
file_path="relative-path-to/add.py"
)
print(f"Function ID: {add_function.id}")file_path accepts a relative or absolute path to the .py file containing your function.
3. Create a code interpreter tool with the custom function
Custom functions run within the code interpreter. Create a code interpreter tool and include the function IDs in its configuration:
from seekrai.types import CreateRunPython, RunPythonConfig
code_tool = client.tools.create(
CreateRunPython(
name="addition_tool",
description="Perform basic addition and return the answer.",
config=RunPythonConfig(
function_ids=[add_function.id]
)
)
)
print(f"Tool created: {code_tool.id}")Parameters
| Parameter | Required | Description |
|---|---|---|
name | Yes | A unique name for the tool. |
description | Yes | Description that helps the agent understand when to use this tool. |
function_ids | No | List of custom function IDs to include. Without this parameter, the tool functions as a standard code interpreter. |
4. Link to an agent
from seekrai.types import CreateAgentRequest
agent = client.agents.create(
CreateAgentRequest(
name="addition-agent",
instructions="Agent that performs basic addition and returns the answer.",
model_id="meta-llama/Llama-3.3-70B-Instruct",
tool_ids=[code_tool.id]
)
)
print(f"Agent ID: {agent.id}")5. Run the agent
Run the agent as you would for any other tool configuration. For details, see Create and manage agents.
Docstring requirements
Your function must contain a valid docstring in Google-style format. A Google-style docstring has four components:
- Short description: A brief, one-line description of what the function does.
- Long description (optional): A more detailed explanation of the function's purpose, behavior, and when to use it.
- Args: Documentation for each parameter, including the parameter name, type, and description.
- Returns: Documentation of the return value, including its type and description.
Manage custom functions
Update a custom function
from seekrai import SeekrFlow
client = SeekrFlow()
updated_function = client.agents.custom_functions.update(
function_id=add_function.id,
file_path="relative-path-to/add.py"
)An agent that is currently Active must be demoted and re-promoted for the updated function to take effect:
import time
from seekrai.types import AgentStatus
# Demote the agent
agent = client.agents.demote(agent.id)
while True:
agent = client.agents.retrieve(agent.id)
if agent.status == AgentStatus.INACTIVE:
break
time.sleep(5)
print(f"Agent demoted: {agent.id}")
# Re-promote the agent
agent = client.agents.promote(agent.id)
while True:
agent = client.agents.retrieve(agent.id)
if agent.status == AgentStatus.ACTIVE:
break
time.sleep(5)
print(f"Agent promoted: {agent.id}")Delete a custom function
result = client.agents.custom_functions.delete(function_id=add_function.id)
if result.deleted:
print("Custom function deleted successfully.")List all custom functions
functions = client.agents.custom_functions.list_functions(
limit=20,
order="desc",
offset=0
)
for fn in functions:
print(f"Function ID: {fn.id}, Description: {fn.description}")All parameters are optional:
| Parameter | Description |
|---|---|
limit | Maximum number of functions to return. |
order | Sort direction ("asc" or "desc") based on creation time. |
offset | Number of records to skip before returning results. |
Retrieve a custom function
fn = client.agents.custom_functions.retrieve(function_id=add_function.id)
print(f"ID: {fn.id}, Description: {fn.description}")Best practices
Write clear, purposeful docstrings
The reasoning model relies heavily on function docstrings to determine when and how to invoke your custom tools. Well-crafted docstrings directly impact tool performance and reliability.
- Function description: Include both a short and long description. The short description should describe when to invoke the function. The long description should specify the conditions or scenarios when the agent should use this tool versus other available options.
- Parameter documentation: Provide clear, specific descriptions for each parameter that help the reasoning model map conversation context to each argument. Avoid generic descriptions like "input value" — instead use contextual descriptions like "user's email address from the conversation" or "the product ID mentioned in the request."
- Return value documentation: Clearly describe what the function returns and how the agent should interpret or use the result in its response.
Effective docstring:
def get_user_order_history(user_email: str, days_back: int = 30) -> List[Dict]:
"""Retrieve a user's recent order history from the e-commerce database.
Use this function when a user asks about their past orders, purchase history,
or needs information about previous transactions. Only invoke when you have
confirmed the user's email address.
Args:
user_email (str): The customer's email address as provided in the conversation
days_back (int): Number of days to look back for orders (default: 30)
Returns:
List[Dict]: List of order dictionaries containing order_id, date, items, and total
"""Ineffective docstring:
def get_user_order_history(user_email: str, days_back: int = 30) -> List[Dict]:
"""Gets orders.
This function gets user orders from the database. Use it for orders.
Args:
user_email (str): Email
days_back (int): Days
Returns:
List[Dict]: Orders
"""Test functions locally before deployment
SeekrFlow does not validate Python syntax or runtime behavior when you create a custom function — it will be created successfully even if the code contains errors. However, when the agent attempts to invoke a faulty function during a conversation, it will fail.
Before creating a custom function, always:
- Verify the function runs without syntax errors
- Test with representative input data
- Handle expected edge cases (null values, empty strings, invalid inputs)
Use external services for complex logic
Custom tools support single function definitions only — helper functions and nested functions within the same file are not supported. For workflows requiring multiple functions or complex business logic, create an external service that your custom tool can call.
Imports and package support
SeekrFlow uses Pyodide to execute custom tools in a secure sandboxed environment.
- Natively supported packages: Pyodide includes many popular Python packages out of the box. See the complete list of pre-installed packages.
- External packages: For packages not natively supported, Pyodide automatically attempts to install them at runtime using
micropip. This works for pure Python packages — those that do not require compiled extensions or system-level dependencies.
Package best practices
- Use natively supported packages when possible for faster execution and guaranteed compatibility.
- Verify pure Python compatibility for external packages — check that they have wheels available on PyPI and do not depend on C extensions.
- Test thoroughly when using external packages, as installation failures will cause your custom tool to fail at runtime.
- If a required package does not compile in Pyodide, create an external service that uses the package and call it from your custom tool via API.
Updated about 1 month ago
