修复bug,优化前端页面

This commit is contained in:
advent259141
2026-01-26 22:14:56 +08:00
parent 6d47663842
commit 3cf0880f98
5 changed files with 137 additions and 28 deletions
+4
View File
@@ -31,6 +31,10 @@ class HandoffTool(FunctionTool, Generic[TContext]):
**kwargs,
)
# Optional provider override for this subagent. When set, the handoff
# execution will use this chat provider id instead of the global/default.
self.provider_id: str | None = None
def default_parameters(self) -> dict:
return {
"type": "object",
+4 -1
View File
@@ -74,7 +74,10 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
ctx = run_context.context.context
event = run_context.context.event
umo = event.unified_msg_origin
prov_id = await ctx.get_current_chat_provider_id(umo)
# Use per-subagent provider override if configured; otherwise fall back
# to the current/default provider resolution.
prov_id = getattr(tool, "provider_id", None) or await ctx.get_current_chat_provider_id(umo)
llm_resp = await ctx.tool_loop_agent(
event=event,
chat_provider_id=prov_id,
+6
View File
@@ -68,6 +68,9 @@ class SubAgentOrchestrator:
instructions = str(item.get("system_prompt", "")).strip()
public_description = str(item.get("public_description", "")).strip()
provider_id = item.get("provider_id")
if provider_id is not None:
provider_id = str(provider_id).strip() or None
tools = item.get("tools", [])
if not isinstance(tools, list):
tools = []
@@ -82,6 +85,9 @@ class SubAgentOrchestrator:
# while the subagent system prompt can be longer/more specific.
handoff = HandoffTool(agent=agent, description=public_description or None)
# Optional per-subagent chat provider override.
handoff.provider_id = provider_id
# Mark as dynamic so we can replace/remove later.
handoff.handler_module_path = "core.subagent_orchestrator"
+7
View File
@@ -48,6 +48,13 @@ class SubAgentRoute(Route):
data.setdefault("main_enable", False)
data.setdefault("main_tools_policy", "handoff_only")
data.setdefault("agents", [])
# Backward/forward compatibility: ensure each agent contains provider_id.
# None means follow global/default provider settings.
if isinstance(data.get("agents"), list):
for a in data["agents"]:
if isinstance(a, dict):
a.setdefault("provider_id", None)
return jsonify(Response().ok(data=data).__dict__)
except Exception as e:
logger.error(traceback.format_exc())
+116 -27
View File
@@ -29,18 +29,14 @@
</v-row>
<div class="text-caption text-medium-emphasis mt-1">
启用后 LLM 只会看到 transfer_to_*不会直接注入/调用其他工具所有工具调用交给 SubAgent 完成
关闭后恢复原有行为 persona 选择并直接注入工具
<div>
启用 LLM 仅负责对话与转交只会看到 transfer_to_* 这类委派工具需要调用工具时会把任务交给对应 SubAgent 执行SubAgent 负责真正的工具调用与结果整理并把结论回传给主 LLM
</div>
<div>
关闭恢复原有行为 persona 选择并直接注入工具
</div>
</div>
<v-alert
type="info"
variant="tonal"
class="mt-3"
>
Router Prompt 当前使用系统内置默认值暂不支持在 WebUI 中自定义
</v-alert>
<div class="d-flex align-center justify-space-between mt-6 mb-2">
<div class="text-subtitle-1 font-weight-bold">SubAgents</div>
<v-btn
@@ -59,8 +55,8 @@
:key="agent.__key"
>
<v-expansion-panel-title>
<div class="d-flex align-center justify-space-between" style="width: 100%;">
<div class="d-flex align-center" style="gap: 10px; min-width: 0;">
<div class="subagent-panel-title">
<div class="subagent-title-left">
<v-chip
:color="agent.enabled ? 'success' : 'grey'"
size="small"
@@ -68,12 +64,25 @@
>
{{ agent.enabled ? '启用' : '停用' }}
</v-chip>
<div class="text-body-1 font-weight-medium text-truncate" style="max-width: 520px;">
{{ agent.name || '未命名 SubAgent' }}
<div class="subagent-title-text">
<div class="subagent-title-name">{{ agent.name || '未命名 SubAgent' }}</div>
<div class="subagent-title-sub">transfer_to_{{ agent.name || '...' }}</div>
</div>
</div>
<div class="d-flex align-center" style="gap: 8px;">
<div class="subagent-title-right">
<v-switch
v-model="agent.enabled"
inset
color="primary"
hide-details
class="subagent-enabled-inline"
@click.stop
>
<template #label>启用</template>
</v-switch>
<v-btn
size="small"
variant="text"
@@ -87,8 +96,8 @@
</v-expansion-panel-title>
<v-expansion-panel-text>
<v-row>
<v-col cols="12" md="4">
<v-row class="subagent-grid">
<v-col cols="12" md="5">
<v-text-field
v-model="agent.name"
label="Agent 名称(用于 transfer_to_{name}"
@@ -98,16 +107,18 @@
persistent-hint
/>
</v-col>
<v-col cols="12" md="3">
<v-switch
v-model="agent.enabled"
inset
color="primary"
label="启用"
hide-details
<v-col cols="12" md="7" class="subagent-actions">
<ProviderSelector
v-model="agent.provider_id"
provider-type="chat_completion"
label="Chat Provider(可选)"
hint="留空表示跟随全局默认 provider。"
persistent-hint
clearable
class="subagent-provider"
/>
</v-col>
<v-col cols="12" md="5">
<v-col cols="12">
<v-autocomplete
v-model="agent.tools"
:items="toolOptions"
@@ -183,6 +194,7 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import axios from 'axios'
import ProviderSelector from '@/components/shared/ProviderSelector.vue'
type ToolOption = { title: string; value: string }
@@ -193,6 +205,7 @@ type SubAgentItem = {
system_prompt: string
tools: string[]
enabled: boolean
provider_id?: string
}
type SubAgentConfig = {
@@ -240,6 +253,7 @@ function normalizeConfig(raw: any): SubAgentConfig {
const system_prompt = (a?.system_prompt ?? '').toString()
const tools = Array.isArray(a?.tools) ? a.tools.map((x: any) => String(x)) : []
const enabled = a?.enabled !== false
const provider_id = (a?.provider_id ?? undefined) as (string | undefined)
return {
__key: `${Date.now()}_${i}_${Math.random().toString(16).slice(2)}`,
@@ -248,6 +262,8 @@ function normalizeConfig(raw: any): SubAgentConfig {
system_prompt,
tools,
enabled
,
provider_id
}
})
@@ -316,7 +332,8 @@ function addAgent() {
public_description: '',
system_prompt: '',
tools: [],
enabled: true
enabled: true,
provider_id: undefined
})
}
@@ -337,7 +354,8 @@ async function save() {
public_description: a.public_description,
system_prompt: a.system_prompt,
tools: a.tools,
enabled: a.enabled
enabled: a.enabled,
provider_id: a.provider_id
}))
}
@@ -369,6 +387,77 @@ onMounted(() => {
padding-top: 8px;
padding-bottom: 40px;
}
.subagent-panel-title {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.subagent-title-left {
min-width: 0;
display: flex;
align-items: center;
gap: 10px;
}
.subagent-title-text {
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.subagent-title-name {
font-weight: 600;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 520px;
}
.subagent-title-sub {
font-size: 12px;
opacity: 0.72;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 520px;
}
.subagent-title-right {
display: flex;
align-items: center;
gap: 8px;
}
.subagent-actions {
display: flex;
align-items: flex-start;
gap: 14px;
}
.subagent-provider {
flex: 1;
min-width: 260px;
}
.subagent-enabled-inline {
margin-right: 2px;
}
/* Keep the switch compact inside the expansion-panel title row. */
.subagent-enabled-inline :deep(.v-input__details) {
display: none;
}
.subagent-enabled-inline :deep(.v-selection-control) {
min-height: 32px;
}
</style>
<style>