feat(chatbot): 添加子智能体支持和摘要中间件
This commit is contained in:
parent
403608fb7d
commit
3babd9be54
@ -11,6 +11,7 @@ from yuxi.agents.common import BaseAgent, load_chat_model
|
||||
from yuxi.agents.common.backends import create_agent_composite_backend
|
||||
from yuxi.agents.common.middlewares import (
|
||||
RuntimeConfigMiddleware,
|
||||
SummaryOffloadMiddleware,
|
||||
save_attachments_to_fs,
|
||||
)
|
||||
from yuxi.agents.common.middlewares.knowledge_base_middleware import KnowledgeBaseMiddleware
|
||||
@ -38,10 +39,10 @@ class ChatbotAgent(BaseAgent):
|
||||
await get_tools_from_all_servers()
|
||||
) # 因为异步加载,无法放在 RuntimeConfigMiddleware 的 __init__ 中
|
||||
|
||||
# subagents
|
||||
subagents = await get_subagents_from_names(context.subagents)
|
||||
subagents_model = load_chat_model(context.subagents_model)
|
||||
subagents_middleware = SubAgentMiddleware(
|
||||
default_model=subagents_model,
|
||||
default_model=load_chat_model(context.subagents_model),
|
||||
subagents=subagents,
|
||||
general_purpose_agent=True,
|
||||
default_middleware=[
|
||||
@ -50,6 +51,17 @@ class ChatbotAgent(BaseAgent):
|
||||
],
|
||||
)
|
||||
|
||||
# summary middleware
|
||||
# 主 Agent 上下文优化:90k tokens 触发压缩(128k context window 的 70%)
|
||||
summary_middleware = SummaryOffloadMiddleware(
|
||||
model=load_chat_model(fully_specified_name=context.model),
|
||||
trigger=("tokens", getattr(context, "summary_threshold", 100) * 1024),
|
||||
trim_tokens_to_summarize=4000,
|
||||
summary_offload_threshold=500,
|
||||
max_retention_ratio=0.5,
|
||||
)
|
||||
|
||||
# all middlewares
|
||||
middlewares = [
|
||||
save_attachments_to_fs, # 附件注入提示词
|
||||
FilesystemMiddleware(backend=_create_fs_backend), # 文件系统后端
|
||||
@ -57,6 +69,7 @@ class ChatbotAgent(BaseAgent):
|
||||
RuntimeConfigMiddleware(extra_tools=all_mcp_tools), # 运行时配置应用(模型/工具/MCP/提示词)
|
||||
SkillsMiddleware(), # Skills 中间件(提示词注入、依赖展开、动态激活)
|
||||
subagents_middleware,
|
||||
summary_middleware,
|
||||
ModelRetryMiddleware(), # 模型重试中间件
|
||||
TodoListMiddleware(),
|
||||
PatchToolCallsMiddleware(),
|
||||
|
||||
@ -34,12 +34,6 @@ class BaseContext:
|
||||
metadata={"name": "用户ID", "configurable": False, "description": "用来唯一标识一个用户"},
|
||||
)
|
||||
|
||||
# 不需要了,使用 user_id 判断
|
||||
# department_id: int | None = field(
|
||||
# default=None,
|
||||
# metadata={"name": "部门ID", "configurable": False, "description": "用来唯一标识一个部门"},
|
||||
# )
|
||||
|
||||
system_prompt: Annotated[str, {"__template_metadata__": {"kind": "prompt"}}] = field(
|
||||
default="You are a helpful assistant.",
|
||||
metadata={"name": "系统提示词", "description": "用来描述智能体的角色和行为"},
|
||||
@ -94,6 +88,33 @@ class BaseContext:
|
||||
},
|
||||
)
|
||||
|
||||
subagents_model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
|
||||
default=sys_config.default_model,
|
||||
metadata={
|
||||
"name": "子智能体的默认模型",
|
||||
"description": "子智能体的默认模型,会被子智能体的配置覆盖。",
|
||||
},
|
||||
)
|
||||
|
||||
subagents: Annotated[list[str], {"__template_metadata__": {"kind": "subagents"}}] = field(
|
||||
default_factory=list,
|
||||
metadata={
|
||||
"name": "子智能体",
|
||||
"options": [],
|
||||
"description": "可选子智能体列表。为空表示不启用任何 SubAgent。但依然会启用一个 general-purpose 的子智能体",
|
||||
"type": "list",
|
||||
},
|
||||
)
|
||||
|
||||
summary_threshold: int = field(
|
||||
default=100,
|
||||
metadata={
|
||||
"name": "上下文摘要触发阈值 (KB)",
|
||||
"description": "当上下文大小超过该值时,启用摘要功能以优化上下文使用。单位为 KB,默认值为 100KB。",
|
||||
"type": "number",
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_configurable_items(cls):
|
||||
"""实现一个可配置的参数列表,在 UI 上配置时使用"""
|
||||
|
||||
@ -431,6 +431,7 @@ import { useAgentStore } from '@/stores/agent'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useDatabaseStore } from '@/stores/database'
|
||||
import { skillApi } from '@/apis/skill_api'
|
||||
import { subagentApi } from '@/apis/subagent_api'
|
||||
import { toolApi } from '@/apis/tool_api'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
@ -461,6 +462,7 @@ watch(
|
||||
if (val) {
|
||||
databaseStore.loadDatabases().catch(() => {})
|
||||
loadLiveSkillOptions().catch(() => {})
|
||||
loadSubagentOptions().catch(() => {})
|
||||
loadToolOptions().catch(() => {})
|
||||
if (selectedAgentId.value) {
|
||||
try {
|
||||
@ -496,6 +498,7 @@ const currentSystemPromptKey = ref(null)
|
||||
const systemPromptDraft = ref('')
|
||||
const activeTab = ref('basic')
|
||||
const liveSkillOptions = ref([])
|
||||
const liveSubagentOptions = ref([])
|
||||
const toolOptionsFromApi = ref([])
|
||||
|
||||
const isEmptyConfig = computed(() => {
|
||||
@ -518,7 +521,8 @@ const hasOtherConfigs = computed(() => {
|
||||
value.template_metadata?.kind === 'mcps' ||
|
||||
value.template_metadata?.kind === 'knowledges' ||
|
||||
value.template_metadata?.kind === 'tools' ||
|
||||
value.template_metadata?.kind === 'skills'
|
||||
value.template_metadata?.kind === 'skills' ||
|
||||
value.template_metadata?.kind === 'subagents'
|
||||
|
||||
return !isBasic && !isTools
|
||||
})
|
||||
@ -580,9 +584,30 @@ const loadToolOptions = async (force = false) => {
|
||||
}
|
||||
}
|
||||
|
||||
const loadSubagentOptions = async (force = false) => {
|
||||
if (!userStore.isAdmin) {
|
||||
liveSubagentOptions.value = []
|
||||
return
|
||||
}
|
||||
if (!force && liveSubagentOptions.value.length > 0) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const result = await subagentApi.getSubAgents()
|
||||
const rows = result?.data || []
|
||||
liveSubagentOptions.value = rows.map((item) => ({
|
||||
id: item.name,
|
||||
name: item.name,
|
||||
description: item.description || ''
|
||||
}))
|
||||
} catch (error) {
|
||||
console.warn('加载 Subagents 列表失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否为需要跳转的配置类型
|
||||
const isToolsKind = (kind) => {
|
||||
return ['knowledges', 'tools', 'mcps', 'skills'].includes(kind)
|
||||
return ['knowledges', 'tools', 'mcps', 'skills', 'subagents'].includes(kind)
|
||||
}
|
||||
|
||||
// 强制刷新对应配置项的选项列表
|
||||
@ -601,6 +626,10 @@ const refreshConfigOptions = async (key, kind) => {
|
||||
await loadLiveSkillOptions(true)
|
||||
message.success('Skills 列表已刷新')
|
||||
break
|
||||
case 'subagents':
|
||||
await loadSubagentOptions(true)
|
||||
message.success('Subagents 列表已刷新')
|
||||
break
|
||||
case 'mcps':
|
||||
// MCP 没有前端 store,提示用户刷新页面
|
||||
message.info('请在 MCP 管理页面刷新')
|
||||
@ -631,6 +660,9 @@ const navigateToConfigPage = (kind) => {
|
||||
case 'skills':
|
||||
router.push({ path: '/extensions', query: { tab: 'skills' } })
|
||||
break
|
||||
case 'subagents':
|
||||
router.push({ path: '/extensions', query: { tab: 'subagents' } })
|
||||
break
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
@ -651,13 +683,16 @@ const getConfigOptions = (value) => {
|
||||
if (value?.template_metadata?.kind === 'skills') {
|
||||
return liveSkillOptions.value.length > 0 ? liveSkillOptions.value : value?.options || []
|
||||
}
|
||||
if (value?.template_metadata?.kind === 'subagents') {
|
||||
return liveSubagentOptions.value.length > 0 ? liveSubagentOptions.value : value?.options || []
|
||||
}
|
||||
return value?.options || []
|
||||
}
|
||||
|
||||
const isListConfig = (key, value) => {
|
||||
const isTools = value?.template_metadata?.kind === 'tools'
|
||||
const isList = value?.type === 'list'
|
||||
return isTools || isList || key === 'skills'
|
||||
return isTools || isList || key === 'skills' || key === 'subagents'
|
||||
}
|
||||
|
||||
const getOptionValue = (option) => {
|
||||
@ -723,7 +758,9 @@ const shouldShowConfig = (key, value) => {
|
||||
value.template_metadata?.kind === 'knowledges' ||
|
||||
value.template_metadata?.kind === 'tools' ||
|
||||
value.template_metadata?.kind === 'skills' ||
|
||||
key === 'skills'
|
||||
value.template_metadata?.kind === 'subagents' ||
|
||||
key === 'skills' ||
|
||||
key === 'subagents'
|
||||
|
||||
if (activeTab.value === 'basic') {
|
||||
// 基础:System Prompt, LLM Model
|
||||
@ -825,6 +862,9 @@ const openSelectionModal = async (key) => {
|
||||
if (configurableItems.value[key]?.template_metadata?.kind === 'skills') {
|
||||
await loadLiveSkillOptions()
|
||||
}
|
||||
if (configurableItems.value[key]?.template_metadata?.kind === 'subagents') {
|
||||
await loadSubagentOptions()
|
||||
}
|
||||
const currentValues = agentConfig.value[key] || []
|
||||
tempSelectedValues.value = [...currentValues]
|
||||
selectionModalOpen.value = true
|
||||
|
||||
Loading…
Reference in New Issue
Block a user