@@ -83,6 +91,7 @@ interface Persona {
begin_dialogs?: string[] | null;
tools?: string[] | null;
skills?: string[] | null;
+ subagents?: string[] | null;
created_at?: string;
updated_at?: string;
folder_id?: string | null;
diff --git a/dashboard/src/views/persona/PersonaManager.vue b/dashboard/src/views/persona/PersonaManager.vue
index 8ad581779f..621cef7ac7 100644
--- a/dashboard/src/views/persona/PersonaManager.vue
+++ b/dashboard/src/views/persona/PersonaManager.vue
@@ -191,6 +191,25 @@
+
{{ tm('labels.createdAt') }}: {{ formatDate(viewingPersona.created_at) }}
{{ tm('labels.updatedAt') }}:
@@ -290,6 +309,7 @@ interface Persona {
begin_dialogs?: string[] | null;
tools?: string[] | null;
skills?: string[] | null;
+ subagents?: string[] | null;
created_at?: string;
updated_at?: string;
folder_id?: string | null;
From e7e7ba8bf374cd1730a67607de28ef4a154834c1 Mon Sep 17 00:00:00 2001
From: AstralYang <1723250309@qq.com>
Date: Tue, 24 Mar 2026 22:59:27 +0800
Subject: [PATCH 2/6] =?UTF-8?q?feat(persona):=20=E4=B8=BA=E9=BB=98?=
=?UTF-8?q?=E8=AE=A4=E4=BA=BA=E6=A0=BC=E9=85=8D=E7=BD=AE=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=20subagents=20=E5=AD=97=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
astrbot/core/persona_mgr.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/astrbot/core/persona_mgr.py b/astrbot/core/persona_mgr.py
index 06367144cd..118a77b7de 100644
--- a/astrbot/core/persona_mgr.py
+++ b/astrbot/core/persona_mgr.py
@@ -13,6 +13,7 @@
mood_imitation_dialogs=[],
tools=None,
skills=None,
+ subagents=None,
custom_error_message=None,
_begin_dialogs_processed=[],
_mood_imitation_dialogs_processed="",
From 4c24fd3b8f5ad25a724c16b475ae772b6314d9ad Mon Sep 17 00:00:00 2001
From: AstralYang <1723250309@qq.com>
Date: Tue, 24 Mar 2026 23:59:49 +0800
Subject: [PATCH 3/6] =?UTF-8?q?fix:=20=E7=BB=9F=E4=B8=80=E5=AD=90=E4=BB=A3?=
=?UTF-8?q?=E7=90=86=E5=9B=BE=E6=A0=87=E5=B9=B6=E4=BF=AE=E5=A4=8D=E5=AD=90?=
=?UTF-8?q?=E4=BB=A3=E7=90=86=E9=85=8D=E7=BD=AE=E5=A4=84=E7=90=86=E9=80=BB?=
=?UTF-8?q?=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将 PersonaCard 中的子代理图标从闪电改为链接图标以提升语义一致性
- 移除 PersonaForm 中冗余的子代理主开关检查,直接使用配置中的代理列表
- 修复主代理中子代理白名单处理逻辑,确保名称归一化并正确处理空集合情况
---
astrbot/core/astr_main_agent.py | 27 ++++++++++++-------
.../src/components/shared/PersonaForm.vue | 11 ++------
dashboard/src/views/persona/PersonaCard.vue | 4 +--
3 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/astrbot/core/astr_main_agent.py b/astrbot/core/astr_main_agent.py
index 6671ec628a..b7b8154bc3 100644
--- a/astrbot/core/astr_main_agent.py
+++ b/astrbot/core/astr_main_agent.py
@@ -387,21 +387,21 @@ async def _ensure_persona_and_skills(
assigned_tools: set[str] = set()
agents = orch_cfg.get("agents", [])
- # 1. 提取白名单
+ # 1. 提取白名单(归一化 subagents 名称)
sub_agents_cfg = (persona or {}).get("subagents")
- persona_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 persona_subagents is not 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 persona_subagents
+ and str(agent.get("name", "")).strip() in normalized_subagents
]
if isinstance(agents, list):
for a in agents:
@@ -438,15 +438,22 @@ async def _ensure_persona_and_skills(
req.func_tool = ToolSet()
# add subagent handoff tools
- # 如果 sub_agents_cfg 为 None 或空列表,则默认放行所有 handoffs
- if not sub_agents_cfg:
+ # 如果 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:
- # 去掉 "transfer_to_" 前缀再匹配
- short_name = tool.name.replace("transfer_to_", "")
- if short_name in sub_agents_cfg:
+ short_name = (
+ (getattr(tool, "name", None) or "")
+ .replace("transfer_to_", "")
+ .strip()
+ )
+ if (
+ short_name in normalized_subagents
+ ): # normalized_subagents 可能是空集合则禁用所有 handoffs
req.func_tool.add_tool(tool)
# check duplicates
diff --git a/dashboard/src/components/shared/PersonaForm.vue b/dashboard/src/components/shared/PersonaForm.vue
index 8531a125f0..1984539191 100644
--- a/dashboard/src/components/shared/PersonaForm.vue
+++ b/dashboard/src/components/shared/PersonaForm.vue
@@ -712,15 +712,8 @@ export default {
const response = await axios.get('/api/subagent/config');
if (response.data.status === 'ok') {
const payload = response.data.data || [];
- this.subagentMainEnable = payload.main_enable;
- if (this.subagentMainEnable){
- if (Array.isArray(payload)) {
- this.availableSubagents = payload.filter(subagent => subagent.enabled !== false);
- } else {
- const subagents = payload.agents || [];
- this.availableSubagents = subagents.filter(subagent => subagent.enabled !== false);
- }
- }
+ const subagents = payload.agents || [];
+ this.availableSubagents = subagents.filter(subagent => subagent.enabled !== false);
} else {
this.$emit('error', response.data.message || 'Failed to load subagents');
}
diff --git a/dashboard/src/views/persona/PersonaCard.vue b/dashboard/src/views/persona/PersonaCard.vue
index 68451e322f..66ea15d876 100644
--- a/dashboard/src/views/persona/PersonaCard.vue
+++ b/dashboard/src/views/persona/PersonaCard.vue
@@ -58,11 +58,11 @@
{{ persona.skills.length }} {{ tm('persona.skillsCount') }}
+ prepend-icon="mdi-vector-link">
{{ tm('form.allSubagentsAvailable') }}
+ variant="tonal" prepend-icon="mdi-vector-link">
{{ persona.subagents.length }} {{ tm('persona.subagentsCount') }}
From 87e832903462579db1512464803bedee233f0bec Mon Sep 17 00:00:00 2001
From: AstralYang <1723250309@qq.com>
Date: Wed, 25 Mar 2026 00:50:14 +0800
Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dhandoff=E5=B7=A5?=
=?UTF-8?q?=E5=85=B7=E6=A3=80=E6=9F=A5=E9=80=BB=E8=BE=91=E4=BB=A5=E4=BD=BF?=
=?UTF-8?q?=E7=94=A8agent.name=E5=B1=9E=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 之前的实现错误地尝试从工具名称中提取代理名,现在直接使用工具的agent.name属性进行白名单检查,提升健壮性。
---
astrbot/core/astr_main_agent.py | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/astrbot/core/astr_main_agent.py b/astrbot/core/astr_main_agent.py
index b7b8154bc3..dc2579348c 100644
--- a/astrbot/core/astr_main_agent.py
+++ b/astrbot/core/astr_main_agent.py
@@ -4,6 +4,7 @@
import copy
import datetime
import json
+import logging
import os
import platform
import zoneinfo
@@ -446,14 +447,10 @@ async def _ensure_persona_and_skills(
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
- ): # normalized_subagents 可能是空集合则禁用所有 handoffs
+ agent = getattr(tool, "agent", None)
+ agent_name = getattr(agent, "name", None) if agent else None
+ logging.info(f"Agent {agent_name}")
+ if agent_name and agent_name.strip() in normalized_subagents:
req.func_tool.add_tool(tool)
# check duplicates
From 8b564a3b782b49560b8b99d9b459cd86fc05a958 Mon Sep 17 00:00:00 2001
From: AstralYang <1723250309@qq.com>
Date: Wed, 25 Mar 2026 01:05:26 +0800
Subject: [PATCH 5/6] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E8=B0=83?=
=?UTF-8?q?=E8=AF=95=E6=97=A5=E5=BF=97=E4=BB=A5=E6=B8=85=E7=90=86=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
移除_ensure_persona_and_skills方法中冗余的调试日志输出,该日志仅用于打印代理名称,不影响功能逻辑。
---
astrbot/core/astr_main_agent.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/astrbot/core/astr_main_agent.py b/astrbot/core/astr_main_agent.py
index dc2579348c..99edc409f1 100644
--- a/astrbot/core/astr_main_agent.py
+++ b/astrbot/core/astr_main_agent.py
@@ -4,7 +4,6 @@
import copy
import datetime
import json
-import logging
import os
import platform
import zoneinfo
@@ -449,7 +448,6 @@ async def _ensure_persona_and_skills(
for tool in so.handoffs:
agent = getattr(tool, "agent", None)
agent_name = getattr(agent, "name", None) if agent else None
- logging.info(f"Agent {agent_name}")
if agent_name and agent_name.strip() in normalized_subagents:
req.func_tool.add_tool(tool)
From c106f986f0f4e2356106c03a413392165fe3151f Mon Sep 17 00:00:00 2001
From: AstralYang <1723250309@qq.com>
Date: Wed, 25 Mar 2026 10:02:18 +0800
Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=AD=90=E4=BB=A3?=
=?UTF-8?q?=E7=90=86=E5=B7=A5=E5=85=B7=E5=8C=B9=E9=85=8D=E9=80=BB=E8=BE=91?=
=?UTF-8?q?=E5=B9=B6=E7=A7=BB=E9=99=A4WebUI=E6=9C=AA=E4=BD=BF=E7=94=A8?=
=?UTF-8?q?=E7=8A=B6=E6=80=81=E5=8F=98=E9=87=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 修复子代理工具匹配逻辑中可能因agent_name类型是导致的匹配问题
- 移除前端未使用的subagentMainEnable状态变量以简化代码。
---
astrbot/core/astr_main_agent.py | 6 ++++--
dashboard/src/components/shared/PersonaForm.vue | 1 -
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/astrbot/core/astr_main_agent.py b/astrbot/core/astr_main_agent.py
index 99edc409f1..b699455516 100644
--- a/astrbot/core/astr_main_agent.py
+++ b/astrbot/core/astr_main_agent.py
@@ -448,8 +448,10 @@ async def _ensure_persona_and_skills(
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)
+ 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)
# check duplicates
if remove_dup:
diff --git a/dashboard/src/components/shared/PersonaForm.vue b/dashboard/src/components/shared/PersonaForm.vue
index 1984539191..0a81e80972 100644
--- a/dashboard/src/components/shared/PersonaForm.vue
+++ b/dashboard/src/components/shared/PersonaForm.vue
@@ -462,7 +462,6 @@ export default {
loadingTools: false,
availableSkills: [],
loadingSkills: false,
- subagentMainEnable:false,
availableSubagents: [],
loadingSubagents: false,
existingPersonaIds: [], // 已存在的人格ID列表