Skip to content

feat(persona): 为 Persona 增加 Subagent 选择功能,允许指定人格可调用的 Subagent 范围,从而避免无限制调用所有已配置并启用 Subagent,降低权限风险#6906

Open
Astral-Yang wants to merge 6 commits intoAstrBotDevs:masterfrom
Astral-Yang:master

Conversation

@Astral-Yang
Copy link

@Astral-Yang Astral-Yang commented Mar 24, 2026

增加 Subagent 选择功能,类似Skill选择,允许指定人格可调用的 Subagent 范围,从而避免无限制调用所有已配置并启用 Subagent,降低权限风险

Modifications / 改动点

  • 在 Persona 数据库模型中新增 subagents 字段,支持选择特定 Subagents

  • 在仪表板人格表单中添加 Subagents 选择面板,支持搜索和批量选择

  • 在人格卡片和详情页中显示 Subagents 配置状态和数量

  • 在核心代理逻辑中根据人格配置过滤可用的 Subagents

  • 更新所有相关 API 接口以支持 subagents 字段的增删改查

  • 添加多语言支持(中文、英文、俄文)的 Subagents 相关文本

  • 确保数据库迁移兼容性,自动添加 subagents 列到现有表

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

image

观察persona_toolset

  • 测试人格1,只能调用testagent1
image
  • 测试人格2,只能调用testagent2
image
  • 测试人格3,能调用所有subagents
image

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • [x 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Add persona-level configuration for allowed subagents and integrate it through the backend, database, and dashboard UI.

New Features:

  • Allow personas to specify which subagents they can call, with support for using all or none.

Enhancements:

  • Filter available agents and handoff tools at runtime based on persona subagent configuration to limit cross-agent calls.
  • Extend persona APIs, database models, and migrations to persist subagent allowlists and expose them via v3 persona data.
  • Update dashboard persona forms, cards, and details views to manage and display subagent configuration, including search and multi-select UI and i18n labels.

- 在 Persona 数据库模型中新增 subagents 字段,支持选择特定 Subagents
- 在仪表板人格表单中添加 Subagents 选择面板,支持搜索和批量选择
- 在人格卡片和详情页中显示 Subagents 配置状态和数量
- 在核心代理逻辑中根据人格配置过滤可用的 Subagents
- 更新所有相关 API 接口以支持 subagents 字段的增删改查
- 添加多语言支持(中文、英文、俄文)的 Subagents 相关文本
- 确保数据库迁移兼容性,自动添加 subagents 列到现有表
@auto-assign auto-assign bot requested review from Fridemn and Raven95676 March 24, 2026 15:19
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. area:webui The bug / feature is about webui(dashboard) of astrbot. feature:persona The bug / feature is about astrbot AI persona system (system prompt) labels Mar 24, 2026
@dosubot
Copy link

dosubot bot commented Mar 24, 2026

Related Documentation

1 document(s) may need updating based on files changed in this PR:

AstrBotTeam's Space

pr4697的改动
View Suggested Changes
@@ -34,6 +34,59 @@
 
 #### 配置说明
 SubAgent 的定义与 Persona 配置一致,需在配置文件中指定 tools、skills、name、description 等。
+
+**Subagent 选择功能(PR #6906)**
+
+[PR #6906](https://github.com/AstrBotDevs/AstrBot/pull/6906) 为 Persona 新增了 Subagent 选择功能,允许管理员为每个人格指定可调用的 Subagent 范围,从而避免无限制调用所有已配置并启用的 Subagent,降低权限风险。该功能与现有的 Skill 选择功能类似,提供了精细的权限控制能力。
+
+**数据库支持:**
+- **新增字段**:Persona 数据库模型(`Persona` 表)新增 `subagents` 字段
+  - **字段类型**:`list | None`(JSON 格式存储)
+  - **默认值**:`None`(表示使用所有可用 Subagents)
+  - **语义说明**:
+    - `None`:使用所有可用 Subagents(默认行为)
+    - 空列表 `[]`:不使用任何 Subagent
+    - 非空列表:仅使用列表中指定的 Subagents
+
+**核心逻辑:**
+- **Subagent 过滤**(`astr_main_agent.py`):在主代理构建过程中,根据 Persona 的 `subagents` 配置过滤可用的 Subagents
+  - 如果 `subagents` 为 `None`,保留所有已启用的 Subagents
+  - 如果 `subagents` 为空列表,不加载任何 Subagent
+  - 如果 `subagents` 包含具体名称,仅加载匹配的 Subagents
+- **Handoff 工具过滤**:系统会根据 Persona 配置自动过滤 `transfer_to_*` 工具
+  - 如果 `subagents` 配置为空列表,不挂载任何 handoff 工具
+  - 如果 `subagents` 配置为非空列表,仅挂载匹配的 handoff 工具(通过移除 `transfer_to_` 前缀进行名称匹配)
+  - 如果 `subagents` 为 `None` 或未配置,挂载所有 handoff 工具
+
+**API 支持:**
+- 所有 Persona 相关 API 端点(`/api/persona`)现已支持 `subagents` 字段
+  - **创建人格**(POST `/api/persona/create`):可在请求中指定 `subagents` 字段
+  - **更新人格**(POST `/api/persona/update`):可更新 `subagents` 字段
+  - **查询人格**(GET `/api/persona/list` 和 `/api/persona/detail`):返回结果包含 `subagents` 字段
+
+**UI 集成:**
+- **Persona 表单**(`PersonaForm.vue`)新增 Subagents 选择面板
+  - **选择模式**:
+    - "默认使用全部 Subagents":设置 `subagents` 为 `None`,允许使用所有已启用的 Subagents
+    - "选择指定 Subagents":手动选择特定 Subagents,设置 `subagents` 为指定名称列表
+  - **搜索功能**:支持按名称或描述搜索 Subagents
+  - **虚拟滚动**:采用虚拟滚动列表优化大量 Subagents 场景下的性能
+  - **批量选择**:支持通过复选框批量选择/取消选择 Subagents
+  - **Chip 展示**:已选择的 Subagents 以 Chip 形式展示,支持单独移除
+- **Persona 卡片**(`PersonaCard.vue`)显示 Subagents 配置状态
+  - 显示已选择的 Subagents 数量或"全部可用"状态
+  - 使用绿色 Chip 表示"使用所有 Subagents",蓝色 Chip 表示具体数量
+- **国际化支持**:新增中文、英文、俄文三语言支持,包含所有 Subagents 相关 UI 文本
+
+**使用场景:**
+- **权限隔离**:为不同权限等级的 Persona 配置不同的 Subagent 访问权限
+- **功能分组**:根据任务类型为 Persona 分配特定的 Subagent 组合
+- **安全控制**:限制敏感 Subagent(如数据库操作、文件管理等)的访问范围,仅授权给特定 Persona
+- **场景定制**:为特定应用场景(如客服、技术支持等)配置专用的 Subagent 集合
+
+**行为变更:**
+- **修复前**:Persona 可无限制调用所有已配置并启用的 Subagents,存在权限泄露风险
+- **修复后**:管理员可为每个 Persona 精确指定可调用的 Subagent 范围,实现细粒度权限控制
 
 **架构重构(PR #5722)**
 

[Accept] [Decline]

Note: You must be authenticated to accept/decline updates.

How did I do? Any feedback?  Join Discord

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, 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!

This pull request introduces a new feature that allows users to specify which subagents a persona can access. This enhances security by preventing personas from indiscriminately calling all available subagents, and improves the control over persona behavior. The changes include modifications to the database model, dashboard interface, core agent logic, and API endpoints to fully support the new subagent selection functionality.

Highlights

  • Subagent Selection Feature: Introduces a new feature to allow specifying the range of Subagents that a Persona can call, mitigating the risk of unrestricted access to all configured and enabled Subagents.
  • Database Model Update: Adds a subagents field to the Persona database model, enabling the selection of specific Subagents for each persona.
  • Dashboard Integration: Integrates a Subagents selection panel into the dashboard persona form, supporting search and bulk selection of Subagents.
  • Core Agent Logic Filtering: Updates the core agent logic to filter available Subagents based on the persona configuration.
  • API Updates: Updates all relevant API interfaces to support CRUD operations for the subagents field.
  • UI Enhancements: Displays Subagents configuration status and count in the persona card and details page.
  • Database Migration Compatibility: Ensures database migration compatibility by automatically adding the subagents column to existing tables.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • In astr_main_agent._ensure_persona_and_skills, the subagent filtering semantics differ between agents and handoffs: an empty subagents list blocks all agents but not sub_agents_cfg in the handoff section allows all tools; consider aligning this so that None means all subagents and [] means no subagents for both agents and handoffs.
  • In PersonaForm.vue::loadSubagents, payload is treated as both an object (payload.main_enable, payload.agents) and potentially an array (Array.isArray(payload)), which makes the response handling brittle; it would be clearer to branch explicitly based on the actual expected API shape and avoid checking main_enable on a value that might be an array.
  • The subagentMainEnable flag loaded in PersonaForm.vue is not used to control the UI or behavior (e.g., disabling the subagent selector when subagents are globally disabled); consider wiring this flag into the template/logic so the form reflects the global subagent enablement state.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `astr_main_agent._ensure_persona_and_skills`, the subagent filtering semantics differ between `agents` and `handoffs`: an empty `subagents` list blocks all agents but `not sub_agents_cfg` in the handoff section allows all tools; consider aligning this so that `None` means all subagents and `[]` means no subagents for both agents and handoffs.
- In `PersonaForm.vue::loadSubagents`, `payload` is treated as both an object (`payload.main_enable`, `payload.agents`) and potentially an array (`Array.isArray(payload)`), which makes the response handling brittle; it would be clearer to branch explicitly based on the actual expected API shape and avoid checking `main_enable` on a value that might be an array.
- The `subagentMainEnable` flag loaded in `PersonaForm.vue` is not used to control the UI or behavior (e.g., disabling the subagent selector when subagents are globally disabled); consider wiring this flag into the template/logic so the form reflects the global subagent enablement state.

## Individual Comments

### Comment 1
<location path="astrbot/core/astr_main_agent.py" line_range="390-399" />
<code_context>
         assigned_tools: set[str] = set()
         agents = orch_cfg.get("agents", [])
+
+        # 1. 提取白名单
+        sub_agents_cfg = (persona or {}).get("subagents")
+        persona_subagents = (
</code_context>
<issue_to_address>
**issue (bug_risk):** Empty `subagents` list semantics are inconsistent between agent filtering and handoff tools, which can lead to handoffs to agents that were filtered out.

`sub_agents_cfg` and `persona_subagents` are interpreted inconsistently:

- In agent filtering, any non-`None` `sub_agents_cfg` (including `[]`) yields a non-`None` `persona_subagents`, so `[]` means "no agents".
- In handoff tools, `if not sub_agents_cfg:` makes both `None` and `[]` mean "all handoffs".

So `subagents: []` clears the agent list but still enables all handoff tools, allowing transfers to agents that were filtered out.

To avoid this, use a single convention in both places: `None` = no filtering / all agents & all handoffs; `[]` = filter out all agents & no handoffs.
</issue_to_address>

### Comment 2
<location path="astrbot/core/astr_main_agent.py" line_range="398-404" />
<code_context>
+            else None
+        )
+
+        # 2. 过滤 agents
+        if persona_subagents is not None:
+            agents = [
+                agent
+                for agent in agents
+                if isinstance(agent, dict)
+                and str(agent.get("name", "")).strip() in persona_subagents
+            ]
         if isinstance(agents, list):
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Subagent name normalization is applied to agent filtering but not to handoff tool matching, which can lead to subtle mismatches.

For filtering you normalize agent names with `str(...).strip()`, but handoff tools use `short_name = replace("transfer_to_", "")` and check `if short_name in sub_agents_cfg` against the unnormalized list. This inconsistency means agents may be filtered correctly but fail handoff (or vice versa) if casing/whitespace or types differ.

Consider normalizing once, e.g. `normalized_subagents = {str(name).strip() for name in sub_agents_cfg or []}`, and use that both for filtering and for handoff checks (`short_name.strip() in normalized_subagents`).

Suggested implementation:

```python
        agents = orch_cfg.get("agents", [])

        # 1. 提取白名单(归一化 subagents 名称)
        sub_agents_cfg = (persona or {}).get("subagents")
        normalized_subagents = (
            {str(name).strip() for name in sub_agents_cfg if str(name).strip()}
            if sub_agents_cfg is not None
            else None
        )

        # 2. 过滤 agents(使用归一化后的名称)
        if normalized_subagents is not None:
            agents = [
                agent
                for agent in agents
                if isinstance(agent, dict)
                and str(agent.get("name", "")).strip() in normalized_subagents
            ]

```

```python
        # add subagent handoff tools
        # 如果 sub_agents_cfg 为 None 或空列表,则默认放行所有 handoffs
        # 注意:handoff 也使用归一化后的 subagent 名称,以避免大小写/空白差异
        if not sub_agents_cfg or normalized_subagents is None:
            # 不配置 subagents 时,默认放行所有 handoffs
            for tool in so.handoffs:
                req.func_tool.register(tool)
        else:
            # 只允许指向归一化白名单中的 subagents 的 handoff
            for tool in so.handoffs:
                short_name = (getattr(tool, "name", None) or "").replace("transfer_to_", "").strip()
                if short_name in normalized_subagents:
                    req.func_tool.register(tool)

```

I assumed the handoff registration was a simple `for tool in so.handoffs: req.func_tool.register(tool)` plus a membership check against `sub_agents_cfg`. If the actual body under `for tool in so.handoffs:` is more complex (e.g., additional conditions or different registration method), you should:
1. Move that existing registration logic into both branches (the “allow all” branch and the filtered branch) instead of the simple `req.func_tool.register(tool)` used above.
2. In the filtered branch, keep everything you already do, but add the `short_name` normalization and `if short_name in normalized_subagents:` guard around the registration logic.
3. Ensure `normalized_subagents` is in scope where handoffs are configured (same function or enclosing scope).
</issue_to_address>

### Comment 3
<location path="dashboard/src/components/shared/PersonaForm.vue" line_range="709-718" />
<code_context>
+        async loadSubagents(){
</code_context>
<issue_to_address>
**issue (bug_risk):** The `loadSubagents` response handling mixes object and array shapes in a way that can silently ignore valid data.

Because `payload` is treated as both an object and an array, a plain array response with valid subagents but no `main_enable` will set `subagentMainEnable` to `undefined` and skip population entirely. Similarly, small API shape changes could bypass the intended array branch.

Consider first determining whether the response is an object-with-config or a bare array, then:
- Derive `subagentMainEnable` explicitly from the config shape only, and
- Derive `subagents` from either the array or the object’s `agents` field,
without ever reading `payload.main_enable` when `payload` is an array.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

- 将 PersonaCard 中的子代理图标从闪电改为链接图标以提升语义一致性
- 移除 PersonaForm 中冗余的子代理主开关检查,直接使用配置中的代理列表
- 修复主代理中子代理白名单处理逻辑,确保名称归一化并正确处理空集合情况
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces subagent management for personas, allowing users to define which subagents a persona can utilize. This includes database schema updates, API modifications for persona creation and updates, and a new UI component for subagent selection within the persona form. The review identified several issues: a logical error in astr_main_agent.py where an empty subagent list was incorrectly treated as "all subagents" and a performance concern with in checks on lists; a similar logical error in persona.py where an empty subagent list was converted to None; and a potential bug in PersonaForm.vue where subagent loading logic might fail if the API response format is an array instead of an object. Additionally, a code duplication issue was noted in PersonaForm.vue regarding removeSubagent and toggleSubagent methods.

I am having trouble creating individual review comments. Click here to see my feedback.

astrbot/core/astr_main_agent.py (441-450)

high

这部分逻辑存在两个问题:

  1. 正确性问题if not sub_agents_cfg: 会在 sub_agents_cfg[] (空列表) 时返回 True,从而错误地允许所有 handoffs。根据设计,空列表应该表示“不允许任何 subagent”,而不是“允许所有”。这可能会导致权限控制失效。
  2. 性能和一致性问题:在 else 分支中,代码使用了 sub_agents_cfg (一个列表) 进行 in 操作,这是 O(n) 的线性搜索。而在前面已经创建了一个经过清理的、用于 O(1) 查找的 set,即 persona_subagents。这里应该复用 persona_subagents 以提高性能并确保逻辑一致性(例如,处理名称前后的空格)。

建议重构此部分,使用 persona_subagents 来进行判断和过滤。

        if persona_subagents is None:
            for tool in so.handoffs:
                req.func_tool.add_tool(tool)
        else:
            for tool in so.handoffs:
                # 去掉 "transfer_to_" 前缀再匹配
                short_name = tool.name.replace("transfer_to_", "")
                if short_name in persona_subagents:
                    req.func_tool.add_tool(tool)

astrbot/dashboard/routes/persona.py (160)

high

这里的 subagents if subagents else None 逻辑会将一个空列表 [] 转换为 None。根据设计,[] 表示不允许任何 subagents,而 None 表示允许所有 subagents。这个转换会导致无法创建一个不允许任何 subagents 的 persona。

为了正确处理空列表,应该直接传递 subagents 变量,因为 data.get("subagents") 在键不存在时会返回 None,这已经能正确处理 subagents 未提供的情况。

同样的问题也可能存在于 toolsskills 的处理中。

                subagents=subagents,

dashboard/src/components/shared/PersonaForm.vue (713-726)

high

加载 subagents 的逻辑似乎过于复杂,并且存在一个潜在的错误。如果 /api/subagent/config 返回一个数组,payload.main_enable 将是 undefined,导致 if (this.subagentMainEnable) 为 false,因此不会加载任何 subagents。if (Array.isArray(payload)) 代码块实际上是无法访问的死代码。

为了简化和修复这个问题,可以使此逻辑与 loadSkills 保持一致,即假设 API 返回一个包含 main_enableagents 属性的对象。这将使代码更具可读性和健壮性。

                if (response.data.status === 'ok') {
                    const payload = response.data.data || {};
                    this.subagentMainEnable = payload.main_enable;
                    if (this.subagentMainEnable) {
                        const subagents = payload.agents || [];
                        this.availableSubagents = subagents.filter(subagent => subagent.enabled !== false);
                    }
                } else {
                    this.$emit('error', response.data.message || 'Failed to load subagents');
                }

dashboard/src/components/shared/PersonaForm.vue (951-962)

medium

removeSubagent 方法中的逻辑与 toggleSubagent 方法有大量重复。为了减少重复代码并提高可维护性,你可以让 removeSubagent 复用 toggleSubagent 的逻辑。

由于 removeSubagent 的作用是取消选择一个 subagent,你可以先检查它是否已被选择,如果是,则调用 toggleSubagent 来取消它。

        removeSubagent(subagentName) {
            if (this.isSubagentSelected(subagentName)) {
                this.toggleSubagent(subagentName);
            }
        },

@Astral-Yang
Copy link
Author

@gemini-code-assist review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the concept of 'subagents' into the persona management system. It updates the database schema to include a subagents field for personas, modifies core agent logic to filter agents and handoff tools based on persona-defined subagent whitelists, and extends the API and manager functions to support subagent configuration. The frontend UI has also been updated with a new 'Subagents Selection' panel in the persona form, including search, selection, and display functionalities, along with internationalization. The review highlights several critical logical errors in the persona creation and retrieval process related to the handling of None versus empty lists for tools, skills, and subagents, which can lead to incorrect semantic interpretation. Additionally, it suggests improving the robustness of handoff tool name parsing in the core agent logic and refactoring duplicated code in the frontend for subagent selection to enhance maintainability and state consistency.

- 之前的实现错误地尝试从工具名称中提取代理名,现在直接使用工具的agent.name属性进行白名单检查,提升健壮性。
移除_ensure_persona_and_skills方法中冗余的调试日志输出,该日志仅用于打印代理名称,不影响功能逻辑。
@Astral-Yang
Copy link
Author

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In dashboard/src/components/shared/PersonaForm.vue the subagentMainEnable data property is defined but never used; consider removing it or wiring it into the UI logic if it was intended to control subagent-related behavior.
  • In astrbot/dashboard/routes/persona.py#create_persona, subagents (like tools/skills) is passed as subagents if subagents else None, which collapses an explicit empty list ("no subagents allowed") into None ("all subagents allowed"); if you want to preserve the allowlist semantics you defined, pass subagents through unchanged and let None vs [] be distinguished.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `dashboard/src/components/shared/PersonaForm.vue` the `subagentMainEnable` data property is defined but never used; consider removing it or wiring it into the UI logic if it was intended to control subagent-related behavior.
- In `astrbot/dashboard/routes/persona.py#create_persona`, `subagents` (like `tools`/`skills`) is passed as `subagents if subagents else None`, which collapses an explicit empty list ("no subagents allowed") into `None` ("all subagents allowed"); if you want to preserve the allowlist semantics you defined, pass `subagents` through unchanged and let `None` vs `[]` be distinguished.

## Individual Comments

### Comment 1
<location path="astrbot/core/astr_main_agent.py" line_range="438-447" />
<code_context>
             req.func_tool = ToolSet()

         # add subagent handoff tools
-        for tool in so.handoffs:
-            req.func_tool.add_tool(tool)
+        # 如果 normalized_subagents 为 None 则默认放行所有 handoffs,空集合禁用所有handoffs
+        if normalized_subagents is None:
+            # 不配置 subagents 时,默认放行所有 handoffs
+            for tool in so.handoffs:
+                req.func_tool.add_tool(tool)
+        else:
+            # 只允许指向归一化白名单中的 subagents 的 handoff
+            for tool in so.handoffs:
+                agent = getattr(tool, "agent", None)
+                agent_name = getattr(agent, "name", None) if agent else None
+                if agent_name and agent_name.strip() in normalized_subagents:
+                    req.func_tool.add_tool(tool)
</code_context>
<issue_to_address>
**issue (bug_risk):** Calling `.strip()` on `agent_name` assumes it is always a string, which may not hold and could raise at runtime.

Here `agent_name` may not be a string, so `agent_name.strip()` can raise if `agent.name` is another type or doesn’t implement `.strip()`. Since `normalized_subagents` is already built via `str(name).strip()`, you can mirror that here to be safe:

```python
if agent_name is not None:
    name_norm = str(agent_name).strip()
    if name_norm and name_norm in normalized_subagents:
        req.func_tool.add_tool(tool)
```

This keeps the normalization consistent and avoids type-related runtime errors.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

- 修复子代理工具匹配逻辑中可能因agent_name类型是导致的匹配问题
- 移除前端未使用的subagentMainEnable状态变量以简化代码。
@Astral-Yang
Copy link
Author

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The PersonaForm.vue methods toggleSubagent and removeSubagent contain very similar branches (especially for the subagents === null case); consider refactoring the shared logic into a small helper to simplify future changes and keep the tri‑state behavior (null/[]/list) easier to reason about.
  • In PersonaForm.vue you define .subagents-selection twice with different max-height values (300px and 38vh); aligning these into a single rule or clarifying which one is intended will avoid confusing cascade behavior across different viewport sizes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `PersonaForm.vue` methods `toggleSubagent` and `removeSubagent` contain very similar branches (especially for the `subagents === null` case); consider refactoring the shared logic into a small helper to simplify future changes and keep the tri‑state behavior (null/[]/list) easier to reason about.
- In `PersonaForm.vue` you define `.subagents-selection` twice with different `max-height` values (300px and 38vh); aligning these into a single rule or clarifying which one is intended will avoid confusing cascade behavior across different viewport sizes.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Astral-Yang
Copy link
Author

Hey - I've left some high level feedback:嘿——我留下了一些高层次的反馈意见:

  • The PersonaForm.vue methods toggleSubagent and removeSubagent contain very similar branches (especially for the subagents === null case); consider refactoring the shared logic into a small helper to simplify future changes and keep the tri‑state behavior (null/[]/list) easier to reason about.PersonaForm.vue 方法 toggleSubagentremoveSubagent 包含非常相似的分支(特别是 subagents === null 情况);考虑将共享逻辑重构为一个小的辅助函数,以简化未来的更改,并使三态行为(null/[]/list)更容易理解。
  • In PersonaForm.vue you define .subagents-selection twice with different max-height values (300px and 38vh); aligning these into a single rule or clarifying which one is intended will avoid confusing cascade behavior across different viewport sizes.在 PersonaForm.vue 中,您定义了两次 .subagents-selection 分别具有不同的 max-height 值(300px 和 38vh);将它们合并为一个规则或明确说明哪个是预期的,将避免在不同的视口大小上出现混淆的级联行为。

Prompt for AI Agents
提示人工智能代理

Please address the comments from this code review:

## Overall Comments
- The `PersonaForm.vue` methods `toggleSubagent` and `removeSubagent` contain very similar branches (especially for the `subagents === null` case); consider refactoring the shared logic into a small helper to simplify future changes and keep the tri‑state behavior (null/[]/list) easier to reason about.
- In `PersonaForm.vue` you define `.subagents-selection` twice with different `max-height` values (300px and 38vh); aligning these into a single rule or clarifying which one is intended will avoid confusing cascade behavior across different viewport sizes.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Sourcery 是免费的开源软件 - 如果您喜欢我们的评论,请考虑分享 ✨

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
请帮我做得更好!请在每条评论上点击👍或👎,我会利用这些反馈来改进您的评论。

toggleSubagentremoveSubagent保持与skills的相关方法一致,PersonaForm.vue中CSS使用了媒体查询区分,无上述问题

@Astral-Yang
Copy link
Author

问题处理完毕,如果仍然有什么问题请告诉我进行更改

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:webui The bug / feature is about webui(dashboard) of astrbot. feature:persona The bug / feature is about astrbot AI persona system (system prompt) size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant