电话

18600577194

当前位置: 首页 > 资讯观点 > 软件开发

如何为自定义开发工作流扩展你的AI代理

标签: AI编程 2025-12-14 

开源的魅力在于它能催生创新,所以北京心玥软件公司发现了Goose,一个我能参与贡献的开源AI开发代理。在此之前,我用过的很多AI开发工具都是闭源的,限制用户只能用产品提供的那些功能。  

看其他工程师和Goose互动时,我惊喜地发现任何人都能扩展它的功能。这意味着我可以拿Goose的基础功能,根据自己的用例定制它,不管这些用例多不寻常。我见过工程师用它干实事,比如总结代码仓库、和GitHub命令行交互,也见过实验性用法,比如让Goose上网找毛绒玩具,或者开启语音交互。  

被这些可能性鼓舞着,我开始琢磨自己做个工具包。我野心不小,想让Goose帮我:  

• 协助训练解读手语的AI模型  

• 生成表情包  

• 为终端实时编程音乐创建Sonic Pi集成  

• 帮忙规划感恩节晚餐  

可惜这些尝试全失败了。一开始我以为是我不是Python高手,但很快意识到——我没完全搞懂怎么创建工具包,要是连我都这样,别人可能也跟我一样摸不着头脑。这篇博客里,我就用一个简单实用的例子——待办事项管理器,分享怎么创建你的第一个工具包。  

什么是工具包?🤔

官方定义

根据Goose官方文档,工具包是插件,能“给Goose提供可调用的工具(函数),还能选把额外上下文加载到系统提示里(比如‘GitHub命令行工具叫gh,你应该用它跑git命令’)。工具包几乎无所不能,从调用外部API、截屏到总结当前项目都行。”  

我的理解

把工具包想象成你手机上的应用。手机基础能打电话,但你还能下载应用扩展功能——玩游戏、拍照、听歌都行。同理,Goose的基础功能是管理会话、控制版本,但工具包能给它加这些本事:  

• 截屏调试  

• 和GitHub命令行交互  

• 管理Jira项目  

• 总结代码仓库  

想看看工具包实际怎么用?翻我上一篇博客,里面用屏幕工具包修UI bug。  

怎么创建自己的工具包 📝

第一步:安装Goose

在终端运行这些命令装Goose:  

brew install pipx  
pipx ensurepath  
pipx install goose-ai

第二步:复刻Goose插件仓库

Goose插件仓库是你工具包的“家”。按README里的说明操作,先把仓库复刻下来。  

第三步:启动开发会话

现在可以创建一个Goose开发会话。这和普通启动会话不一样——你要启动一个用Goose来构建的会话:  

uv run goose session start  

注意:可能需要先装uv。  

第四步:创建工具包文件

在目录goose-plugins/src/goose_plugins/toolkits里,创建一个叫mytodo.py的文件。  

第五步:设置样板代码

往mytodo.py里加这些代码:  

from goose.toolkit.base import tool, Toolkit  
class MyTodoToolkit(Toolkit):  
    """一个简单的待办事项工具包,用来管理任务。"""  
    def __init__(self, *args: tuple, **kwargs: dict) -> None:  
        super().__init__(*args, **kwargs)  
        # 初始化任务列表,每个任务是带'description'和'completed'字段的字典  
        self.tasks = []

到现在,这个文件干了这些事:  

• 导入了基础工具包库  

• 创建了MyTodoToolkit类,继承自Toolkit类  

• 初始化了一个空列表,用来存任务清单  

第六步:添加工具包的第一个动作

先写个添加任务的方法,往mytodo.py里加这段代码:  

@tool  
def add_task(self, task: str) -> str:  
    """往待办清单里加个新任务。  
    Args:  
        task (str): 要添加到清单的任务描述。  
    """  
    self.tasks.append({"description": task, "completed": False})  
    self.notifier.log(f"Added task: '{task}'")  
    return f"Added task: '{task}'"

拆解一下这个方法:  

• @tool装饰器:把函数标记为工具,让Goose能自动识别和管理它。  

• 方法定义:创建add_task方法。工具包里写方法,就是定义它能干的特定动作——这里我们要让任务管理工具包能加任务。  

• 文档字符串:Goose强制要求这个。它不只是文档,Goose用它来:  

  • 验证方法功能  

  • 检查参数是否匹配  

  Goose的创造者Bradley Axen说,文档字符串常帮Goose在处理命令时给出更精准的结果。  

第七步:完善工具包

现在可以加更多方法,处理删除任务、更新任务、列出所有任务、列出已完成任务。下面是完整的mytodo.py文件:  

from goose.toolkit.base import tool, Toolkit  
class MyTodoToolkit(Toolkit):  
    """一个简单的待办事项工具包,用来管理任务。"""  
    def __init__(self, *args: tuple, **kwargs: dict) -> None:  
        super().__init__(*args, **kwargs)  
        # 初始化任务列表,每个任务是带'description'和'completed'字段的字典  
        self.tasks = []  
    @tool  
    def add_task(self, task: str) -> str:  
        """往待办清单里加个新任务。  
        Args:  
            task (str): 要添加到清单的任务描述。  
        """  
        # 把任务存为带描述和完成状态的字典  
        self.tasks.append({"description": task, "completed": False})  
        self.notifier.log(f"Added task: '{task}'")  
        return f"Added task: '{task}'"  
    @tool  
    def list_tasks(self) -> str:  
        """列出待办清单里的所有任务。"""  
        if not self.tasks:  
            self.notifier.log("No tasks in the to-do list.")  
            return "No tasks in the to-do list. Give user instructions on how to add tasks."  
        task_list = []  
        for index, task in enumerate(self.tasks, start=1):  
            status = "✓" if task["completed"] else " "  
            task_list.append(f"{index}. [{status}] {task['description']}")  
        self.notifier.log("\n".join(task_list))  
        return f"Tasks listed successfully: {task_list}"  
    @tool  
    def remove_task(self, task_number: int) -> str:  
        """按编号从待办清单里删任务。  
        Args:  
            task_number (int): 要删的任务编号(从1开始)。  
        """  
        try:  
            removed_task = self.tasks.pop(task_number - 1)  
            self.notifier.log(f"Removed task: '{removed_task['description']}'")  
            return f"Removed task: '{removed_task['description']}'"  
        except IndexError:  
            self.notifier.log("Invalid task number. Please try again.")  
            return "User input invalid task number and needs to try again."  
    @tool  
    def mark_as_complete(self, task_number: int) -> str:  
        """按编号把任务标为已完成。  
        Args:  
            task_number (int): 要标记的任务编号(从1开始)。  
        Raises:  
            IndexError: 如果任务编号无效。  
        """  
        try:  
            self.tasks[task_number - 1]["completed"] = True  
            self.notifier.log(f"Marked task {task_number} as complete: '{self.tasks[task_number - 1]['description']}'")  
            return f"Marked task {task_number} as complete: '{self.tasks[task_number - 1]['description']}'"  
        except IndexError:  
            self.notifier.log("Invalid task number. Please try again.")  
            return "User input invalid task number and needs to try again."  
    @tool  
    def list_completed_tasks(self) -> str:  
        """列出所有已完成任务。"""  
        completed_tasks = [task for task in self.tasks if task["completed"]]  
        if not completed_tasks:  
            self.notifier.log("No completed tasks.")  
            return "No completed tasks. Provide instructions for marking tasks as complete."  
        task_list = []  
        for index, task in enumerate(completed_tasks, start=1):  
            task_list.append(f"{index}. [✓] {task['description']}")  
        self.notifier.log("\n".join(task_list))  
        return f"Tasks listed successfully: {task_list}"  
    @tool  
    def update_task(self, task_number: int, new_description: str) -> str:  
        """按编号更新任务描述。  
        Args:  
            task_number (int): 要更新的任务编号(从1开始)。  
            new_description (str): 任务的新描述。  
        Raises:  
            IndexError: 如果任务编号无效。  
        """  
        try:  
            old_description = self.tasks[task_number - 1]["description"]  
            self.tasks[task_number - 1]["description"] = new_description  
            self.notifier.log(f"Updated task {task_number} from '{old_description}' to '{new_description}'")  
            return f"Updated task {task_number} successfully."  
        except IndexError:  
            self.notifier.log("Invalid task number. Unable to update.")  
            return "Invalid task number. Unable to update."

第八步:让你的工具包对他人可用

现在要把工具包加到pyproject.toml里,让别人也能用:  

[project.entry-points."goose.toolkit"]  
developer = "goose.toolkit.developer:Developer"  
github = "goose.toolkit.github:Github"  
# 加这么一行——键会成为配置里用的名称  
mytodo = "goose_plugins.toolkits.mytodo:MyTodoToolkit"

格式是模块.子模块:类名:  

• goose_plugins是基础模块  

• toolkits是goose_plugins下的子模块  

• mytodo是toolkits下更深的子模块  

• MyTodoToolkit是类名  

第九步:启用你的工具包

把工具包加到~/.config/goose/profiles.yaml里就能用了:  

default:  
  provider: openai  
  processor: gpt-4o  
  accelerator: gpt-4o-mini  
  moderator: truncate  
  toolkits:  
  - name: developer  
    requires: {}  
  - name: mytodo  
    requires: {}

现在用自然语言提示它就行,比如加任务、标完成、更新任务。下面是个使用示例:  

参考我的拉取请求 👀

你可以看看我的PR,里面有完整实现。  

扩展你的AI代理www.bjxykj.cn

工具包的未来 🚀

工具包的创建方式还在发展。看看产品路线图计划,了解接下来会有什么新东西。