Can I register my own tool, using @register_tool? I think so, but it is not working.
Bug description I've defined my own class, and registered it as a tool, using the @register_tool decorator. However, di does not use the tool.
Auxillary information I've even tried to tell metagpt to use the particular tool, by name in the prompt, still it does not see it.
Environment information
- LLM type and model name: got-4-turbo-preview
- System version: Ubuntu 20.04
- Python version: 3.10
- packages version:
- installation method: pip install metagpt
Screenshots or logs
There are some conditions need to met before di using the tool:
- Proper comments for the tool. Here is a demo:
async def create_repo(self, idea:str) -> Path:
"""
Creates a new software repository with user requirements.
Args:
idea (str): User requirement.
Returns:
Path: The path to the newly created software repository.
Example:
To create a software repository for "snake game":
```python
todo = PrepareDocuments()
repo_path = await todo.create_repo("snake game")
print("Repository created at:", repo_path)
```
"""
self._override_context()
await self.run(with_messages=[Message(content=idea)])
return self.context.repo.workdir
The google docstring is used as the tool description during the di searches for a tool.
- The task description needs to match the tool description. Here is a demo:
import fire
from metagpt.roles.di.data_interpreter import DataInterpreter
async def main():
prompt = f"""This is a software requirement:\n```text\nwrite a snake game\n```\n .
1. Create a software repository.\n
2. Write a PRD.\n
3. Write a design.\n
Note: All required dependencies and environments have been fully installed and configured."""
di = DataInterpreter(tools=["GPTvGenerator", "PrepareDocuments", "WritePRD", "WriteDesign"])
await di.run(prompt)
if __name__ == "__main__":
fire.Fire(main)
Currently, the tool search ability is not perfect, if you want to test the process, you can specify the description of the tool to use in the prompt
@iorisa Thank you very much for your response. I'm guessing the run() call is local to your code, not absolutely necessary(not all examples implement the run()) - do correct me if I'm wrong. I've added the docstring, and made sure the task description includes a clear indication about the tool and the method to be used. I've indicated tools=["toolname"] , in the call to DataInterpreter()- my specific toolname. Still no luck, I can't get it to see the tool at all.
metagpt/tools/tool_recommend.py line 95, set a breakpoint and see:
register_tool should be called at initialization.
You can add your tool api into metagpt/tools/libs/__init__.py
@iorisa Thank you for the detailed response. From your messages, I'm understanding that each tool I create would need to go in as a patch to the source code and that there is no general purpose interface to add my tool , correct? Your suggestion to add to __init__.py seems to be alluding to that.
For now, I can work with that, but I'm hoping each time I do a pip install --upgrade metagpt, the patches don't get clobbered, which they will, I guess.
Maybe implementing this as a general purpose interface to register my own tool would be the way to go.
- You can register your tool api through
TOOL_REGISTRY.register_tool.
def register_tool(tags: list[str] = None, schema_path: str = "", **kwargs):
"""register a tool to registry"""
def decorator(cls):
# Get the file path where the function / class is defined and the source code
file_path = inspect.getfile(cls)
if "metagpt" in file_path:
# split to handle ../metagpt/metagpt/tools/... where only metapgt/tools/... is needed
file_path = "metagpt" + file_path.split("metagpt")[-1]
source_code = inspect.getsource(cls)
TOOL_REGISTRY.register_tool(
tool_name=cls.__name__,
tool_path=file_path,
schema_path=schema_path,
tool_code=source_code,
tags=tags,
tool_source_object=cls,
**kwargs,
)
return cls
return decorator
- About
pip install --upgrade metagpt: You can create a new project to solve your business problem, and put all your tool apis within it. Then using metaGPT throughimport. In this way, the upgrade of MetaGPT will not affect existing codes.
Hi, a very good question. In fact, you can register any tool any where in your local file.
Say you have a function or class in a.py which you want to use as a tool. You can register it as follow
from metagpt.tools.tool_registry import register_tool
@register_tool()
def your_function(arg1: str, arg2: int) -> dict:
"""
Some docstring, recommended to be Google-style
Args:
arg1 (str): ...
arg2 (int): ...
Returns:
dict: ...
"""
Then, in a script to use DataInterpreter, say b.py, you can make DataInterpreter to use your tool
import a # this is to make sure the tool is registered before running DataInterpreter
...
di = DataInterpreter(tools=["your_function"])
...
Additional references potentially helpful: https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/interpreter/machine_learning_with_tools.html#mechanism
@garylin2099 - thank you very much for the detailed message. I've done exactly what you said, still, no luck. The tool just doesn't get registered. I'll check again, in great detail to see if I've missed anything. Cheers.
@krish240574 @garylin2099 I solidified this example into example and the test passed
I've tried the exact code, that your example has, from custom_tool.py and here is the output : (the tool does not get registered) What am I missing? magic_function is defined as follows, in custom_tool.py : @register_tool() def magic_function(arg1: str, arg2: int) -> dict: """ The magic function that does something. Args: arg1 (str): ... arg2 (int): ... Returns: dict: ... """ return {"arg1": arg1 * 3, "arg2": arg2 * 5}
====================OUTPUT=====================
[
{
"task_id": "1",
"dependent_task_ids": [],
"instruction": "Call the magic function with arg1 set to 'A' and arg2 set to 2."
},
{
"task_id": "2",
"dependent_task_ids": ["1"],
"instruction": "Report the result of the function call."
}
]
2024-03-22 05:21:09.385 | INFO | metagpt.utils.cost_manager:update_cost:52 - Total running cost: $0.005 | Max budget: $10.000 | Current cost: $0.005, prompt_tokens: 244, completion_tokens: 84
2024-03-22 05:21:09.386 | INFO | metagpt.roles.role:_plan_and_act:494 - ready to take on task task_id='1' dependent_task_ids=[] instruction="Call the magic function with arg1 set to 'A' and arg2 set to 2." task_type='' code='' result='' is_success=False is_finished=False
2024-03-22 05:21:09.386 | INFO | metagpt.roles.di.data_interpreter:_write_code:79 - ready to WriteCodeWithoutTools
2024-03-22 05:21:12.723 | INFO | metagpt.utils.cost_manager:update_cost:52 - Total running cost: $0.006 | Max budget: $10.000 | Current cost: $0.006, prompt_tokens: 471, completion_tokens: 47
1 def magic_function(arg1, arg2):
2 return arg1 * arg2
3
4 result = magic_function('A', 2)
5 result
'AA'
2024-03-22 05:21:13.687 | INFO | metagpt.roles.role:_plan_and_act:494 - ready to take on task task_id='2' dependent_task_ids=['1'] instruction='Report the result of the function call.' task_type='' code='' result='' is_success=False is_finished=False
2024-03-22 05:21:13.687 | INFO | metagpt.roles.di.data_interpreter:_write_code:79 - ready to WriteCodeWithoutTools
2024-03-22 05:21:14.998 | INFO | metagpt.utils.cost_manager:update_cost:52 - Total running cost: $0.005 | Max budget: $10.000 | Current cost: $0.005, prompt_tokens: 494, completion_tokens: 11
The result of the function call is 'AA'.
The result of the function call is 'AA'.
After re-reading the issue, I understand. Our data interpreter has not been officially released to pypi. Currently, these work are in the main branch. You need to install the main branch.
- Install main branch:
git clone https://github.com/geekan/MetaGPT && cd MetaGPT && pip install --upgrade -e . - Execute the example:
python examples/di/custom_tool.py
Ahh, awesome! Let me try that out and get back to you.
This run looks really weird, after a tool is registered, why is the execution proceeding by generating code, to use the tool? SHould the agent not use the tool directly? This approach leads to so many errors and retries :
===================== Start run ================================ python custom_tool.py 2024-04-05 17:20:22.611 | INFO | metagpt.const:get_metagpt_package_root:29 - Package root set to /media/krishna/K/smith/MetaGPT Building prefix dict from the default dictionary ... Loading model from cache /tmp/jieba.cache Loading model cost 0.647 seconds. Prefix dict has been built successfully.
[
{
"task_id": "1",
"dependent_task_ids": [],
"instruction": "Call the magic function with arg1 'A' and arg2 2",
"task_type": "other"
}
]
2024-04-05 17:20:29.237 | INFO | metagpt.utils.cost_manager:update_cost:57 - Total running cost: $0.006 | Max budget: $10.000 | Current cost: $0.006, prompt_tokens: 412, completion_tokens: 54 2024-04-05 17:20:29.238 | INFO | metagpt.roles.role:_plan_and_act:486 - ready to take on task task_id='1' dependent_task_ids=[] instruction="Call the magic function with arg1 'A' and arg2 2" task_type='other' code='' result='' is_success=False is_finished=False 2024-04-05 17:20:29.238 | INFO | metagpt.tools.tool_recommend:recall_tools:195 - Recalled tools: ['magic_function']; Scores: [-5.988676009656986]
["magic_function"]
2024-04-05 17:20:30.454 | INFO | metagpt.utils.cost_manager:update_cost:57 - Total running cost: $0.002 | Max budget: $10.000 | Current cost: $0.002, prompt_tokens: 168, completion_tokens: 8 2024-04-05 17:20:30.455 | INFO | metagpt.tools.tool_recommend:recommend_tools:101 - Recommended tools: ['magic_function'] 2024-04-05 17:20:30.456 | INFO | metagpt.roles.di.data_interpreter:_write_code:153 - ready to WriteAnalysisCode
from /media/krishna/K/smith/MetaGPT/examples/di/custom_tool.py import magic_function
result = magic_function('A', 2)
print(result)
2024-04-05 17:20:32.381 | INFO | metagpt.utils.cost_manager:update_cost:57 - Total running cost: $0.006 | Max budget: $10.000 | Current cost: $0.006, prompt_tokens: 431, completion_tokens: 41
1 from /media/krishna/K/smith/MetaGPT/examples/di/custom_tool.py import magic_function
2
3 result = magic_function('A', 2)
4 print(result)
5
Cell In[1], line 1
from /media/krishna/K/smith/MetaGPT/examples/di/custom_tool.py import magic_function
^
SyntaxError: invalid syntax
2024-04-05 17:20:34.316 | INFO | metagpt.roles.di.data_interpreter:_write_code:153 - ready to WriteAnalysisCode
from media.krishna.K.smith.MetaGPT.examples.di.custom_tool import magic_function
result = magic_function('A', 2)
print(result)
2024-04-05 17:20:36.477 | INFO | metagpt.utils.cost_manager:update_cost:57 - Total running cost: $0.012 | Max budget: $10.000 | Current cost: $0.006, prompt_tokens: 519, completion_tokens: 36
1 from media.krishna.K.smith.MetaGPT.examples.di.custom_tool import magic_function
2
3 result = magic_function('A', 2)
4 print(result)
5
ModuleNotFoundError Traceback (most recent call last) Cell In[2], line 1 ----> 1 from media.krishna.K.smith.MetaGPT.examples.di.custom_tool import magic_function 3 result = magic_function('A', 2) 4 print(result)
ModuleNotFoundError: No module named 'media' 2024-04-05 17:20:37.498 | INFO | metagpt.roles.di.data_interpreter:_write_code:153 - ready to WriteAnalysisCode
import sys
sys.path.append('/media/krishna/K/smith/MetaGPT/examples/di')
from custom_tool import magic_function
result = magic_function('A', 2)
print(result)
2024-04-05 17:20:40.661 | INFO | metagpt.utils.cost_manager:update_cost:57 - Total running cost: $0.020 | Max budget: $10.000 | Current cost: $0.008, prompt_tokens: 632, completion_tokens: 47
1 import sys
2 sys.path.append('/media/krishna/K/smith/MetaGPT/examples/di')
3 from custom_tool import magic_function
4
5 result = magic_function('A', 2)
6 print(result)
7
,{'arg1': 'AAA', 'arg2': 10}