Defining Custom Tools (Python)
Relevant source files
The following files were used as context for generating this wiki page:
In the Loom ecosystem, tools are functions that an AI agent can execute to interact with its environment, such as searching the web, reading files, or running shell commands. The Python SDK provides a powerful and flexible system for defining these tools, which serve as the agent's "Hands" while the cognitive loop acts as the "Brain". This separation allows developers to easily extend an agent's capabilities by writing standard Python functions and making them available to the agent's reasoning process.
This document covers the mechanisms for defining, describing, and managing custom tools within the loom-py SDK, from simple function decorators to detailed, structured descriptors that enhance an LLM's ability to use them effectively.
Sources: ROADMAP.md, loom-py/README.md, core/src/tools/README.md
Core Concepts and Architecture
Loom's architecture separates the agent's "Brain" (Python) from its "Hands" (Rust Core). Tool definitions reside in Python, allowing for rapid development, while the secure execution and system integration are handled by the stable Rust Core. All tools, whether native to Rust or defined in Python, ultimately conform to a common Tool trait within the core runtime.
The diagram below illustrates this separation of responsibilities.
This diagram shows the flow from defining a tool in Python to its execution in the Rust Core.
Sources: ROADMAP.md, core/src/tools/README.md
Defining Tools with the @tool Decorator
The simplest way to expose a Python function as a tool is by using the @tool decorator. This approach automatically generates the necessary schema for the LLM to understand and use the function.
from loom import Agent, tool
@tool("web.search", description="Search the web for information")
def web_search(query: str) -> dict:
return {"query": query, "results": ["example.com"]}
agent = Agent(
agent_id="py-agent-1",
tools=[web_search],
# ...
)
The @tool decorator takes the tool's name and a description. The function's type hints are used to infer the parameters for the tool schema.
Sources: loom-py/README.md:80-84
Advanced Tool Definition with Descriptors
For more fine-grained control over how a tool is presented to the LLM, you can use the ToolDescriptor and ToolParameter classes. This allows you to specify detailed descriptions, examples, default values, and categorize tools.
Sources: loom-py/src/loom/context/prompting/tool_descriptor.py
ToolParameter Class
The ToolParameter class describes a single parameter for a tool.
| Attribute | Type | Description |
|---|---|---|
name | str | The name of the parameter. |
type | str | The data type (e.g., "string", "number", "boolean"). |
description | str | A description of what the parameter is for. |
required | bool | Whether the parameter is required. Defaults to False. |
default | Optional[Any] | A default value if the parameter is not provided. |
examples | list[Any] | A list of example values for the parameter. |
Sources: loom-py/src/loom/context/prompting/tool_descriptor.py:11-20
ToolDescriptor Class
The ToolDescriptor provides a complete, structured description of a tool.
| Attribute | Type | Description |
|---|---|---|
name | str | The unique name of the tool (e.g., fs:read_file). |
description | str | A detailed description of the tool's purpose. |
parameters | list[ToolParameter] | A list of parameters the tool accepts. |
examples | list[str] | A list of full usage examples in JSON format. |
category | Optional[str] | A category for grouping related tools (e.g., "filesystem", "shell"). |
Sources: loom-py/src/loom/context/prompting/tool_descriptor.py:37-44
The following class diagram shows the relationship between ToolDescriptor and ToolParameter.
Managing Tools with ToolRegistry
The ToolRegistry is a central component for managing tool definitions. It allows for the registration, discovery, and formatting of tools to be included in the LLM's system prompt.
Registration and Formatting Flow
The following diagram illustrates how tools are registered and then formatted for use in a prompt.
Registration Methods
You can register tools using a full ToolDescriptor or a simplified helper method.
from loom.context import ToolRegistry, ToolDescriptor
registry = ToolRegistry()
# Register with a full descriptor
desc = ToolDescriptor(
name="fs:read",
description="Read a file",
category="filesystem"
)
registry.register(desc)
# Register with the simple helper
registry.register_simple("shell:run", "Execute a shell command")
Sources: loom-py/tests/unit/test_tool_descriptor.py:104-113
Formatting for Prompts
The format_for_prompt method provides several options for presenting tools to the LLM, which is crucial for effective tool use.
# registry contains 'fs:read' and 'shell:run'
# Compact format (default)
compact_format = registry.format_for_prompt(detailed=False)
# Output:
# • fs:read() - Read a file
# • shell:run() - Execute a shell command
# Grouped by category
grouped_format = registry.format_for_prompt(group_by_category=True)
# Output:
# FILESYSTEM:
# • fs:read() - Read a file
# SHELL:
# • shell:run() - Run a shell command
Sources: loom-py/tests/unit/test_tool_descriptor.py:136-155
Providing Usage Examples with FewShotLibrary
To improve the LLM's ability to use tools correctly, you can provide few-shot examples. The FewShotLibrary is designed for this purpose. It stores curated examples of successful tool usage patterns in a ReAct (Reasoning-Action) format.
FewShotExample: A dataclass holding a singlegoal,thought,action, andobservation.FewShotLibrary: A collection ofFewShotExampleinstances, organized by category. It can select relevant examples based on the current goal.
The create_default_library() function initializes a library with pre-built examples for common tools like fs:search, shell:run, and web:search.
from loom.context.prompting.few_shot import FewShotExample, get_default_library
# Get the default library
library = get_default_library()
# Get examples relevant to a goal
relevant_examples = library.get_relevant("Find all TODO comments in Python files")
# Format examples for the prompt
prompt_text = library.format_examples(relevant_examples)
The formatted examples guide the LLM on how to structure its thoughts and actions.
Sources: loom-py/src/loom/context/prompting/few_shot.py
End-to-End Tool Usage Flow
The following sequence diagram shows the complete lifecycle of a tool call, from the agent's goal to the final observation.
This diagram illustrates how the CognitiveAgent orchestrates the interaction between the LLM and the tool execution backend.
Sources: loom-py/src/loom/cognitive/README.md, core/src/tools/README.md
Conclusion
The Loom Python SDK offers a dual approach to defining custom tools for AI agents. Developers can start quickly with the simple @tool decorator for exposing Python functions or use the more expressive ToolDescriptor and ToolRegistry classes for fine-grained control over tool presentation. Complemented by the FewShotLibrary, this system provides a robust framework for extending agent capabilities, ensuring that LLMs can understand and utilize the available tools effectively and reliably through the secure Rust Core.