refactor:重构内部工具的注册机制,并进行解耦#6071
Conversation
…th dedicated providers for computer-use runtimes.
…nto a new ComputerToolProvider and associated tool modules.
… LLM agentic capabilities with configurable tools and context management.
…rBot into agent-fix-clean
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 此拉取请求旨在通过引入一个解耦的工具提供者(ToolProvider)机制,全面重构Astrbot的内部工具注册和管理方式。这一改变将工具的定义、加载和系统提示词的注入从核心代理逻辑中分离出来,显著提升了代码的模块化、可扩展性和可维护性。通过将不同类型的工具(如计算机使用工具、定时任务工具)封装到各自的提供者中,系统能够更清晰地管理工具生命周期,并为未来的功能扩展奠定基础。同时,仪表盘界面也进行了相应更新,以更好地展示和管理这些工具。 Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Hey - 我发现了 3 个问题,并给出了一些高层次反馈:
source的取值('plugin','internal','mcp')现在在前后端都会使用;建议把它们集中定义为共享的常量/枚举(并加上类型),以避免不同组件之间出现偏差或拼写错误。- 在
ComputerToolProvider._sandbox_tools中,当 Shipyard 配置不完整时,该函数只会记录错误日志并返回空的工具列表;你可能需要将其作为更明确的运行时错误抛出,或提供某种回退行为,以便更容易定位缺失的 computer-use 工具问题。 - 对于 WebUI 中的 internal 工具,后端通过
getattr(tool, 'source', 'plugin')为source提供默认值,但前端把source视为可选字段;建议统一行为,始终返回非空的source值,使 UI 逻辑(包括 internal lock 的样式)在所有工具上表现一致。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `source` values ('plugin', 'internal', 'mcp') are now used across the backend and frontend; consider centralizing them as shared constants/enums (and typing them) to avoid drift or typos between components.
- In `ComputerToolProvider._sandbox_tools`, when Shipyard config is incomplete the function just logs an error and returns an empty tool list; you may want to surface this as a clearer runtime error or fall back behavior so the missing computer-use tools are easier to diagnose.
- For internal tools in the WebUI, the backend defaults `source` via `getattr(tool, 'source', 'plugin')` but the frontend treats `source` as optional; consider normalizing this to always return a non-empty `source` value so the UI logic (including the internal lock styling) behaves consistently for all tools.
## Individual Comments
### Comment 1
<location path="astrbot/core/tools/send_message.py" line_range="92" />
<code_context>
- context.context.event.unified_msg_origin,
- )
- # Use shell to check if the file exists in sandbox
- result = await sb.shell.exec(f"test -f {path} && echo '_&exists_'")
- if "_&exists_" in json.dumps(result):
- # Download the file from sandbox
</code_context>
<issue_to_address>
**🚨 issue (security):** Quote sandbox paths in shell command to avoid breakage and injection risk.
Passing `path` directly into `test -f {path}` will fail for paths with spaces or shell metacharacters and also introduces command injection risk. Please quote/escape the path (e.g. with `shlex.quote(path)`) or, preferably, avoid shell string construction altogether by using a non-shell API to test file existence. If you must use `sb.shell.exec`, ensure the path is safely quoted before execution.
</issue_to_address>
### Comment 2
<location path="astrbot/core/astr_main_agent.py" line_range="1037-1038" />
<code_context>
+ session_id=req.session_id or "",
+ )
+ # Respect WebUI tool enable/disable settings
+ _inactivated: set[str] = set(
+ sp.get("inactivated_llm_tools", [], scope="global", scope_id="global")
+ )
+ for _tp in config.tool_providers:
</code_context>
<issue_to_address>
**question (bug_risk):** Clarify how global inactivated tools interact with non-togglable internal tools.
Because internal tools are no longer user-togglable and `toggle_tool` rejects `source == "internal"`, any legacy entries for internal tools in `inactivated_llm_tools` will still disable them at runtime with no way for users to fix this via the UI. Please either (1) ignore `inactivated_llm_tools` for internal tools, or (2) migrate/clean internal tools out of that list so runtime behavior aligns with the new UX.
</issue_to_address>
### Comment 3
<location path="astrbot/core/tools/prompts.py" line_range="134-136" />
<code_context>
+ "{file_content}\nFile Name: {file_name}"
+)
+
+CONVERSATION_HISTORY_INJECT_PREFIX = (
+ "\n\nBellow is you and user previous conversation history:\n"
+)
+
</code_context>
<issue_to_address>
**nitpick (typo):** Fix typo in conversation history prefix prompt.
This string currently reads `"Bellow is you and user previous conversation history"`; please correct the typo and grammar (e.g., `"Below is your and the user's previous conversation history:"`) so prompts using `CONVERSATION_HISTORY_INJECT_PREFIX` don’t propagate the error.
```suggestion
CONVERSATION_HISTORY_INJECT_PREFIX = (
"\n\nBelow is your and the user's previous conversation history:\n"
)
```
</issue_to_address>帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈来改进后续的 Review。
Original comment in English
Hey - I've found 3 issues, and left some high level feedback:
- The
sourcevalues ('plugin', 'internal', 'mcp') are now used across the backend and frontend; consider centralizing them as shared constants/enums (and typing them) to avoid drift or typos between components. - In
ComputerToolProvider._sandbox_tools, when Shipyard config is incomplete the function just logs an error and returns an empty tool list; you may want to surface this as a clearer runtime error or fall back behavior so the missing computer-use tools are easier to diagnose. - For internal tools in the WebUI, the backend defaults
sourceviagetattr(tool, 'source', 'plugin')but the frontend treatssourceas optional; consider normalizing this to always return a non-emptysourcevalue so the UI logic (including the internal lock styling) behaves consistently for all tools.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `source` values ('plugin', 'internal', 'mcp') are now used across the backend and frontend; consider centralizing them as shared constants/enums (and typing them) to avoid drift or typos between components.
- In `ComputerToolProvider._sandbox_tools`, when Shipyard config is incomplete the function just logs an error and returns an empty tool list; you may want to surface this as a clearer runtime error or fall back behavior so the missing computer-use tools are easier to diagnose.
- For internal tools in the WebUI, the backend defaults `source` via `getattr(tool, 'source', 'plugin')` but the frontend treats `source` as optional; consider normalizing this to always return a non-empty `source` value so the UI logic (including the internal lock styling) behaves consistently for all tools.
## Individual Comments
### Comment 1
<location path="astrbot/core/tools/send_message.py" line_range="92" />
<code_context>
- context.context.event.unified_msg_origin,
- )
- # Use shell to check if the file exists in sandbox
- result = await sb.shell.exec(f"test -f {path} && echo '_&exists_'")
- if "_&exists_" in json.dumps(result):
- # Download the file from sandbox
</code_context>
<issue_to_address>
**🚨 issue (security):** Quote sandbox paths in shell command to avoid breakage and injection risk.
Passing `path` directly into `test -f {path}` will fail for paths with spaces or shell metacharacters and also introduces command injection risk. Please quote/escape the path (e.g. with `shlex.quote(path)`) or, preferably, avoid shell string construction altogether by using a non-shell API to test file existence. If you must use `sb.shell.exec`, ensure the path is safely quoted before execution.
</issue_to_address>
### Comment 2
<location path="astrbot/core/astr_main_agent.py" line_range="1037-1038" />
<code_context>
+ session_id=req.session_id or "",
+ )
+ # Respect WebUI tool enable/disable settings
+ _inactivated: set[str] = set(
+ sp.get("inactivated_llm_tools", [], scope="global", scope_id="global")
+ )
+ for _tp in config.tool_providers:
</code_context>
<issue_to_address>
**question (bug_risk):** Clarify how global inactivated tools interact with non-togglable internal tools.
Because internal tools are no longer user-togglable and `toggle_tool` rejects `source == "internal"`, any legacy entries for internal tools in `inactivated_llm_tools` will still disable them at runtime with no way for users to fix this via the UI. Please either (1) ignore `inactivated_llm_tools` for internal tools, or (2) migrate/clean internal tools out of that list so runtime behavior aligns with the new UX.
</issue_to_address>
### Comment 3
<location path="astrbot/core/tools/prompts.py" line_range="134-136" />
<code_context>
+ "{file_content}\nFile Name: {file_name}"
+)
+
+CONVERSATION_HISTORY_INJECT_PREFIX = (
+ "\n\nBellow is you and user previous conversation history:\n"
+)
+
</code_context>
<issue_to_address>
**nitpick (typo):** Fix typo in conversation history prefix prompt.
This string currently reads `"Bellow is you and user previous conversation history"`; please correct the typo and grammar (e.g., `"Below is your and the user's previous conversation history:"`) so prompts using `CONVERSATION_HISTORY_INJECT_PREFIX` don’t propagate the error.
```suggestion
CONVERSATION_HISTORY_INJECT_PREFIX = (
"\n\nBelow is your and the user's previous conversation history:\n"
)
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
astrbot/core/tools/send_message.py
Outdated
| context.context.event.unified_msg_origin, | ||
| ) | ||
| # Use shell to check if the file exists in sandbox | ||
| result = await sb.shell.exec(f"test -f {path} && echo '_&exists_'") |
There was a problem hiding this comment.
🚨 issue (security): 在 shell 命令中为 sandbox 路径加上引号,以避免出错和命令注入风险。
将 path 直接拼入 test -f {path} 会在路径包含空格或 shell 元字符时失败,同时也带来命令注入风险。请为该路径添加引号/转义(例如使用 shlex.quote(path)),或者更好的做法是完全避免构造 shell 字符串,改用非 shell 的 API 来检测文件是否存在。如果必须使用 sb.shell.exec,请确保在执行前对路径进行安全引用。
Original comment in English
🚨 issue (security): Quote sandbox paths in shell command to avoid breakage and injection risk.
Passing path directly into test -f {path} will fail for paths with spaces or shell metacharacters and also introduces command injection risk. Please quote/escape the path (e.g. with shlex.quote(path)) or, preferably, avoid shell string construction altogether by using a non-shell API to test file existence. If you must use sb.shell.exec, ensure the path is safely quoted before execution.
| _inactivated: set[str] = set( | ||
| sp.get("inactivated_llm_tools", [], scope="global", scope_id="global") |
There was a problem hiding this comment.
question (bug_risk): 需要澄清全局禁用工具与不可切换的 internal 工具之间是如何交互的。
由于 internal 工具已经不再允许用户切换,且 toggle_tool 会拒绝 source == "internal",任何遗留在 inactivated_llm_tools 中的 internal 工具条目仍会在运行时被禁用,而用户无法通过 UI 来修复这一点。请考虑:(1)对 internal 工具忽略 inactivated_llm_tools;或者(2)在该列表中迁移/清理掉 internal 工具条目,使运行时行为与新的 UX 保持一致。
Original comment in English
question (bug_risk): Clarify how global inactivated tools interact with non-togglable internal tools.
Because internal tools are no longer user-togglable and toggle_tool rejects source == "internal", any legacy entries for internal tools in inactivated_llm_tools will still disable them at runtime with no way for users to fix this via the UI. Please either (1) ignore inactivated_llm_tools for internal tools, or (2) migrate/clean internal tools out of that list so runtime behavior aligns with the new UX.
| CONVERSATION_HISTORY_INJECT_PREFIX = ( | ||
| "\n\nBellow is you and user previous conversation history:\n" | ||
| ) |
There was a problem hiding this comment.
nitpick (typo): 修正会话历史前缀提示文案中的拼写错误。
该字符串当前为 "Bellow is you and user previous conversation history";请修正其中的拼写和语法错误(例如改为 "Below is your and the user's previous conversation history:"),以避免使用 CONVERSATION_HISTORY_INJECT_PREFIX 的提示继续传播这一问题。
| CONVERSATION_HISTORY_INJECT_PREFIX = ( | |
| "\n\nBellow is you and user previous conversation history:\n" | |
| ) | |
| CONVERSATION_HISTORY_INJECT_PREFIX = ( | |
| "\n\nBelow is your and the user's previous conversation history:\n" | |
| ) |
Original comment in English
nitpick (typo): Fix typo in conversation history prefix prompt.
This string currently reads "Bellow is you and user previous conversation history"; please correct the typo and grammar (e.g., "Below is your and the user's previous conversation history:") so prompts using CONVERSATION_HISTORY_INJECT_PREFIX don’t propagate the error.
| CONVERSATION_HISTORY_INJECT_PREFIX = ( | |
| "\n\nBellow is you and user previous conversation history:\n" | |
| ) | |
| CONVERSATION_HISTORY_INJECT_PREFIX = ( | |
| "\n\nBelow is your and the user's previous conversation history:\n" | |
| ) |
|
Related Documentation 1 document(s) may need updating based on files changed in this PR: AstrBotTeam's Space pr4697的改动View Suggested Changes@@ -91,7 +91,7 @@
**运行时工具解析**
-新增 `_get_runtime_computer_tools()` 方法,根据 `provider_settings.computer_use_runtime` 配置动态解析运行时工具。
+Computer Use 工具现在通过 `ComputerToolProvider` 统一提供(`astrbot/core/computer/computer_tool_provider.py`)。主代理在构建时调用 `provider.get_tools(ctx)` 方法,根据 `provider_settings.computer_use_runtime` 配置动态获取运行时工具。
**Booter 选择优化(PR #6064)**
@@ -200,8 +200,8 @@
**技术实现**
-- 实现读取 `provider_settings.computer_use_runtime` 配置,确定需要挂载的运行时工具类型
-- `HandoffExecutor.build_handoff_toolset()` 方法替代了原有 `_build_handoff_toolset()` 方法,在 `handoff_executor.py` 模块中实现
+- `ComputerToolProvider` 负责根据 `provider_settings.computer_use_runtime` 配置决定需要提供的运行时工具
+- `HandoffExecutor.build_handoff_toolset()` 方法在子代理 handoff 时通过 `ComputerToolProvider.get_tools(ctx)` 获取运行时工具
- 工具解析优先级:已注册工具 → 运行时工具,确保灵活性和正确性
**权限控制(PR #5402)**
@@ -369,7 +369,93 @@
### 4. 工具注册与配置加载
+#### 架构重构(PR #6071)
+
+[PR #6071](https://github.com/AstrBotDevs/AstrBot/pull/6071) 对内部工具注册机制进行了全面重构,引入 `ToolProvider` 协议实现工具注入与主代理的解耦,提升了系统的可扩展性和模块化程度。
+
+**新增 ToolProvider 协议**(`astrbot/core/tool_provider.py`)
+
+引入 `ToolProvider` 协议和 `ToolProviderContext` 上下文类,提供标准化的工具注入接口:
+
+- **`ToolProvider` 协议**:定义两个核心方法
+ - `get_tools(ctx: ToolProviderContext) -> list[FunctionTool]`:返回当前会话可用的工具列表
+ - `get_system_prompt_addon(ctx: ToolProviderContext) -> str`:返回需要附加到系统提示词的文本(可选)
+
+- **`ToolProviderContext` 类**:封装会话级上下文信息
+ - `computer_use_runtime`:Computer Use 运行时类型(none、local、sandbox)
+ - `sandbox_cfg`:沙箱配置字典
+ - `session_id`:会话 ID
+
+该协议使工具提供者与主代理构建逻辑完全解耦,主代理无需知道具体的工具实现细节。
+
+**核心 ToolProvider 实现**
+
+系统现提供以下内置 ToolProvider:
+
+1. **`ComputerToolProvider`**(`astrbot/core/computer/computer_tool_provider.py`)
+ - 提供所有 Computer Use 工具(Shell、Python、文件传输、浏览器自动化、Neo 技能生命周期工具)
+ - `get_all_tools()` 方法:返回所有 Computer Use 工具的非活跃实例,用于 WebUI 显示和工具分配
+ - `get_tools(ctx)` 方法:根据 `computer_use_runtime` 配置过滤并返回当前运行时可用的工具
+ - `get_system_prompt_addon(ctx)` 方法:根据运行时类型返回对应的系统提示词(sandbox/local 模式提示)
+ - 沙箱能力感知:通过 `session_booter.get(session_id)` 查询沙箱能力,条件性地包含浏览器工具
+
+2. **`CronToolProvider`**(`astrbot/core/cron/cron_tool_provider.py`)
+ - 提供定时任务管理工具(CREATE_CRON_JOB_TOOL、DELETE_CRON_JOB_TOOL、LIST_CRON_JOBS_TOOL)
+ - 遵循相同的 ToolProvider 协议
+
+**系统提示词集中管理**(`astrbot/core/tools/prompts.py`)
+
+所有系统提示词已从 `astr_main_agent_resources.py` 迁移到专用模块,便于其他模块导入而无需引入工具类或重量级依赖:
+
+- `FILE_EXTRACT_CONTEXT_TEMPLATE`:文件提取上下文模板
+- `IMAGE_CAPTION_DEFAULT_PROMPT`:图片描述默认提示词
+- `COMPUTER_USE_DISABLED_PROMPT`:Computer Use 禁用提示
+- `WEBCHAT_TITLE_GENERATOR_SYSTEM_PROMPT` / `WEBCHAT_TITLE_GENERATOR_USER_PROMPT`:WebChat 标题生成提示词
+- `LIVE_MODE_SYSTEM_PROMPT`、`LLM_SAFETY_MODE_SYSTEM_PROMPT`、`TOOL_CALL_PROMPT` 等
+
+**内部工具注册(FunctionToolManager)**
+
+`FunctionToolManager` 新增 `register_internal_tools()` 方法,使用模块列表动态加载工具:
+
+```python
+_INTERNAL_TOOL_PROVIDERS = [
+ "astrbot.core.tools.cron_tools",
+ "astrbot.core.tools.kb_query",
+ "astrbot.core.tools.send_message",
+ "astrbot.core.computer.computer_tool_provider",
+]
+```
+
+每个模块暴露 `get_all_tools()` 函数,返回该模块的所有工具。工具自动标记 `source='internal'`,便于 WebUI 区分内部工具、插件工具和 MCP 工具。
+
+**主代理构建逻辑调整(build_main_agent)**
+
+`build_main_agent` 函数的工具注入逻辑已重构:
+
+1. **移除旧的工具注入函数**:`_apply_sandbox_tools()`、`_apply_local_env_tools()`、`_proactive_cron_job_tools()` 等函数已删除
+2. **新增 `tool_providers` 参数**:`MainAgentBuildConfig` 新增 `tool_providers: list[ToolProvider]` 参数
+3. **统一工具注入流程**:
+ - 构建 `ToolProviderContext`,包含 `computer_use_runtime`、`sandbox_cfg`、`session_id`
+ - 遍历所有 `tool_providers`,调用 `provider.get_tools(ctx)` 获取工具并添加到 `req.func_tool`
+ - 调用 `provider.get_system_prompt_addon(ctx)` 获取系统提示词附加内容
+ - 自动尊重 WebUI 工具启用/禁用设置(`inactivated_llm_tools`)
+
+**工具模块拆分**
+
+原 `astr_main_agent_resources.py` 文件已删除,工具类迁移到以下模块:
+
+- `astrbot/core/computer/computer_tool_provider.py`:Computer Use 工具(Shell、Python、文件传输等),带懒加载缓存
+- `astrbot/core/tools/cron_tools.py`:定时任务管理工具
+- `astrbot/core/tools/kb_query.py`:知识库查询工具
+- `astrbot/core/tools/send_message.py`:主动消息发送工具
+- `astrbot/core/tools/prompts.py`:系统提示词常量
+
+**WebUI 工具管理集成**
+
+WebUI 工具管理现在调用 `ComputerToolProvider.get_all_tools()` 获取所有可用的 Computer Use 工具(返回非活跃、仅用于注册的实例),用于在界面中显示所有工具选项。运行时工具注入通过 `get_tools(ctx)` 在实际 LLM 请求时独立处理。
+
#### 逻辑改进
+
工具注册和配置加载逻辑已优化,确保子代理配置的正确性和工具的动态注册。FunctionTool 新增 `is_background_task` 属性,支持异步后台任务。
#### MCP 客户端初始化(PR #5993)
@@ -526,7 +612,7 @@
- 供应商类型选择:现在支持在“选择供应商”下拉菜单中选择 `chat_completion` 和 `agent_runner` 类型,便于根据不同需求配置子代理的执行方式。
- 页面副标题已更新为:“主 LLM 可直接使用自身工具,也可通过 handoff 分派给各个 SubAgent。”
-主代理和子代理的工具分配逻辑在 UI 中有清晰展示,支持灵活配置。
+主代理和子代理的工具分配逻辑在 UI 中有清晰展示,支持灵活配置。WebUI 工具管理通过调用 `ComputerToolProvider.get_all_tools()` 获取所有可用的 Computer Use 工具(返回非活跃、仅用于注册的实例),用于在界面中显示所有可配置的工具选项。运行时工具注入通过 `provider.get_tools(ctx)` 在实际 LLM 请求时独立处理。
#### 定时任务管理
新增 Cron Job 管理页面(CronJobPage.vue),支持:
@@ -679,12 +765,13 @@
#### 主要改动
##### 调整 build_main_agent 逻辑顺序
-修复的核心是调整内置工具的加载时机,确保在去重装饰逻辑(`_decorate_llm_request`)执行前已加载所有内置工具。调整后的执行顺序为:
-
-1. **内置工具加载**(提前至去重前):
- - Computer Use 工具(sandbox/local 运行时)
- - Cron 工具(定时任务工具)
- - send_message_to_user 工具(主动消息工具)
+
+修复的核心是调整内置工具的加载时机,通过 ToolProvider 机制实现工具注入与去重逻辑的正确配合。调整后的执行顺序为:
+
+1. **ToolProvider 工具注入**:
+ - 通过 `tool_providers` 列表注入所有内部工具(Computer Use、Cron、send_message_to_user 等)
+ - 工具通过 `ComputerToolProvider.get_tools(ctx)` 和 `CronToolProvider.get_tools(ctx)` 动态加载
+ - 自动尊重 WebUI 工具启用/禁用设置
2. **知识库应用**(`_apply_kb`)
@@ -692,7 +779,7 @@
- 此时所有内置工具已完整加载
- 子智能体去重逻辑可以正确识别并移除主智能体中与子智能体重复的内置工具
-修复前,内置工具在去重逻辑之后添加,导致去重功能无法识别这些工具,造成重复加载。
+修复前,内置工具在去重逻辑之后添加,导致去重功能无法识别这些工具,造成重复加载。PR #6071 的 ToolProvider 重构将工具注入逻辑前置,彻底解决了该问题。
##### 修复人格工具查找逻辑
改进了 `_ensure_persona_and_skills()` 方法中的人格工具查找逻辑:
@@ -717,24 +804,32 @@
#### 技术细节
-**内置工具加载时机调整:**
-- Computer Use 工具(`_apply_sandbox_tools` / `_apply_local_env_tools`)
-- Cron 工具(`_proactive_cron_job_tools`)
-- send_message_to_user 工具(`SEND_MESSAGE_TO_USER_TOOL`)
-
-这些工具现在在 `await _decorate_llm_request()` 之前添加,确保子智能体的去重逻辑能够正确处理这些工具。
+**工具注入时机调整:**
+- Computer Use 工具、Cron 工具、send_message_to_user 工具现在通过 `ToolProvider` 机制统一注入
+- `ComputerToolProvider` 和 `CronToolProvider` 在 `build_main_agent` 中通过 `tool_providers` 参数传递
+- 工具注入在 `await _decorate_llm_request()` 之前完成,确保子智能体的去重逻辑能够正确处理这些工具
**知识库应用调用位置:**
知识库应用(`_apply_kb`)的调用位置保持在去重逻辑之前,确保知识库工具也能被正确去重。
#### 影响范围
-此修复确保子智能体去重功能(`remove_main_duplicate_tools`)正常工作,解决了以下问题:
+
+PR #5459 和 PR #6071 的 ToolProvider 重构共同确保子智能体去重功能(`remove_main_duplicate_tools`)正常工作,解决了以下问题:
- 内置工具重复加载问题
- 人格配置兼容性问题(`tools=None` 语义)
- 插件过滤时误删内置工具和 MCP 工具的问题
修复后,当启用 `remove_main_duplicate_tools: true` 时,主智能体会正确移除与子智能体重复的所有工具(包括内置工具),仅保留独有工具和 handoff 工具。
+
+**可扩展性提升**
+
+ToolProvider 机制显著简化了新工具类别的添加流程:
+- 新增内部工具类别(如未来的网络工具、多媒体处理工具等)只需:
+ 1. 实现 `ToolProvider` 协议
+ 2. 在 `FunctionToolManager._INTERNAL_TOOL_PROVIDERS` 列表中注册模块路径
+- 主代理无需导入具体的工具类,仅依赖 `ToolProvider` 接口
+- 工具的懒加载和缓存模式在各 Provider 内部封装,不影响主代理逻辑
---
Note: You must be authenticated to accept/decline updates. |
|
Warning Gemini is experiencing higher than usual traffic and was unable to create the review. Please try again in a few hours by commenting |
- Add get_default_tools/get_tools/get_system_prompt_parts to ComputerBooter base - Each booter subclass (ShipyardNeo, Shipyard, Boxlite) declares its own tools - ComputerToolProvider now delegates to booter API via computer_client helpers - Add unified query API: get_sandbox_tools, get_default_sandbox_tools, etc. - Extract Neo prompts to dedicated computer/prompts.py module - Add booter type constants (booters/constants.py) - Fix subagent tool path to pass sandbox_cfg and session_id - Fix Sourcery issues: shell injection in send_message, typo in prompts, internal tools bypass inactivated_llm_tools check
Two changes to make the tool schema sent to the LLM deterministic: 1. ToolSet.normalize() — sort tools by name before serialization. Called at the end of build_main_agent() after all injection passes. Eliminates ordering drift from plugin load order, MCP reconnection, and persona tool list differences. 2. Always inject full sandbox tool set — ComputerToolProvider now returns get_default_sandbox_tools() unconditionally, regardless of sandbox boot state. Browser tools are always in the schema even if the sandbox profile lacks browser capability. The executor rejects calls to unavailable browser tools with a descriptive error instead of silently omitting them from the schema. This eliminates the pre-boot/post-boot tool set jump that caused prefix cache misses on the second request of a conversation.
- Rewrite TestApplySandboxToolsRefactored to test ComputerToolProvider directly (_apply_sandbox_tools was removed; old tests permanently skipped) - Add TestExecutorCapabilityGuard with 5 strict tests for browser capability rejection at executor level - Fix typo: "on hehalf you" -> "on behalf of you" in subagent result - Remove extra blank lines (ruff E303) after _apply_sandbox_tools comment - Remove dead _build_sync_and_scan_command (no callers after refactor)
- shipyard_neo: browser property now returns None when not initialized instead of raising RuntimeError, matching ComputerBooter base contract - computer_tool_provider: remove dead os.environ writes for shipyard (SHIPYARD_ENDPOINT / SHIPYARD_ACCESS_TOKEN are never read anywhere) and remove unused os import
|
f16edd4 — Booter 自描述 API:各 booter 子类声明自身工具集,ComputerToolProvider |
- .gitignore: keep both .serena and .worktrees/ entries - astr_main_agent_resources.py: keep deletion (refactored to tools/) - send_message.py: port video message type support from master
当前Astrbot的内部工具注册十分混乱,且和agent模块强耦合,估计重构
Modifications / 改动点
Screenshots or Test Results / 运行截图或测试结果
Checklist / 检查清单
requirements.txt和pyproject.toml文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations inrequirements.txtandpyproject.toml.由 Sourcery 生成的总结
通过引入可复用的工具提供者和集中管理的提示词常量,将 AstrBot 的内部工具注册与主代理解耦,同时在 WebUI 中明确区分内置工具并禁止对其进行开关操作。
新功能:
ToolProvider协议和上下文,在构建时向主代理注入工具和系统提示词附加内容。缺陷修复:
改进:
prompts模块中,避免在各组件间重复。FunctionTool增加source属性,并通过 MCP 工具和仪表盘 API 进行传递,以便区分工具来源。Original summary in English
Summary by Sourcery
Decouple AstrBot’s internal tool registration from the main agent by introducing reusable tool providers and centralized prompt constants, while surfacing built-in tools distinctly in the WebUI and preventing them from being toggled.
New Features:
Bug Fixes:
Enhancements: