feat(agent): 添加智能体配置保存时的graph重载功能
在保存智能体配置时新增reload_graph选项,用于强制清空graph缓存并重新构建 修改前后端接口以支持该功能,并在UI中添加相应提示 为DeepAgent添加subagents_model配置项,优化子智能体模型选择
This commit is contained in:
parent
0d87c1755e
commit
da351df193
@ -3,7 +3,7 @@ import json
|
||||
import traceback
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, UploadFile, File
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Query, UploadFile, File
|
||||
from fastapi.responses import StreamingResponse
|
||||
from langchain.messages import AIMessageChunk, HumanMessage
|
||||
from langgraph.types import Command
|
||||
@ -849,7 +849,12 @@ async def resume_agent_chat(
|
||||
|
||||
|
||||
@chat.post("/agent/{agent_id}/config")
|
||||
async def save_agent_config(agent_id: str, config: dict = Body(...), current_user: User = Depends(get_required_user)):
|
||||
async def save_agent_config(
|
||||
agent_id: str,
|
||||
config: dict = Body(...),
|
||||
reload_graph: bool = Query(False),
|
||||
current_user: User = Depends(get_required_user),
|
||||
):
|
||||
"""保存智能体配置到YAML文件(需要登录)"""
|
||||
try:
|
||||
# 获取Agent实例和配置类
|
||||
@ -860,6 +865,8 @@ async def save_agent_config(agent_id: str, config: dict = Body(...), current_use
|
||||
result = agent.context_schema.save_to_file(config, agent.module_name)
|
||||
|
||||
if result:
|
||||
if reload_graph:
|
||||
agent_manager.get_agent(agent_id, reload_graph=True)
|
||||
return {"success": True, "message": f"智能体 {agent.name} 配置已保存"}
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail="保存智能体配置失败")
|
||||
|
||||
@ -20,12 +20,16 @@ class AgentManager(metaclass=SingletonMeta):
|
||||
for agent_id in self._classes.keys():
|
||||
self.get_agent(agent_id)
|
||||
|
||||
def get_agent(self, agent_id, reload=False, **kwargs):
|
||||
def get_agent(self, agent_id, reload=False, reload_graph=False, **kwargs):
|
||||
# 检查是否已经创建了该 agent 的实例
|
||||
if reload or agent_id not in self._instances:
|
||||
agent_class = self._classes[agent_id]
|
||||
self._instances[agent_id] = agent_class()
|
||||
|
||||
# 如果仅需要重新加载 graph,则清空 graph 缓存
|
||||
if reload_graph and agent_id in self._instances:
|
||||
self._instances[agent_id].reload_graph()
|
||||
|
||||
return self._instances[agent_id]
|
||||
|
||||
def get_agents(self):
|
||||
|
||||
@ -132,6 +132,11 @@ class BaseAgent:
|
||||
logger.error(f"获取智能体 {self.name} 历史消息出错: {e}")
|
||||
return []
|
||||
|
||||
def reload_graph(self):
|
||||
"""重置 graph 缓存,强制下次调用 get_graph 时重新构建"""
|
||||
self.graph = None
|
||||
logger.info(f"{self.name} graph 缓存已清空,将在下次调用时重新构建")
|
||||
|
||||
@abstractmethod
|
||||
async def get_graph(self, **kwargs) -> CompiledStateGraph:
|
||||
"""
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
"""Deep Agent Context - 基于BaseContext的深度分析上下文配置"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Annotated
|
||||
|
||||
from src.agents.common.context import BaseContext
|
||||
|
||||
@ -102,3 +103,10 @@ class DeepContext(BaseContext):
|
||||
default=DEEP_PROMPT,
|
||||
metadata={"name": "系统提示词", "description": "Deep智能体的角色和行为指导"},
|
||||
)
|
||||
subagents_model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
|
||||
default="siliconflow/deepseek-ai/DeepSeek-V3.2",
|
||||
metadata={
|
||||
"name": "Sub-agent Model",
|
||||
"description": "The model used by sub-agents (e.g., critique-agent, research-agent).",
|
||||
},
|
||||
)
|
||||
|
||||
@ -10,6 +10,7 @@ from src.agents.common import BaseAgent, load_chat_model
|
||||
from src.agents.common.middlewares import context_based_model, inject_attachment_context
|
||||
from src.agents.common.tools import search
|
||||
|
||||
from .context import DeepContext
|
||||
from .prompts import DEEP_PROMPT
|
||||
|
||||
search_tools = [search]
|
||||
@ -57,10 +58,12 @@ def context_aware_prompt(request: ModelRequest) -> str:
|
||||
class DeepAgent(BaseAgent):
|
||||
name = "深度分析智能体"
|
||||
description = "具备规划、深度分析和子智能体协作能力的智能体,可以处理复杂的多步骤任务"
|
||||
context_schema = DeepContext
|
||||
capabilities = [
|
||||
"file_upload",
|
||||
"todo",
|
||||
"files",
|
||||
"reload_graph",
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -82,6 +85,7 @@ class DeepAgent(BaseAgent):
|
||||
context = self.context_schema.from_file(module_name=self.module_name)
|
||||
|
||||
model = load_chat_model(context.model)
|
||||
sub_model = load_chat_model(context.subagents_model)
|
||||
tools = await self.get_tools()
|
||||
|
||||
# 使用 create_deep_agent 创建深度智能体
|
||||
@ -95,15 +99,14 @@ class DeepAgent(BaseAgent):
|
||||
TodoListMiddleware(),
|
||||
FilesystemMiddleware(),
|
||||
SubAgentMiddleware(
|
||||
default_model=load_chat_model(context.model),
|
||||
default_model=sub_model,
|
||||
default_tools=tools,
|
||||
subagents=[critique_sub_agent, research_sub_agent],
|
||||
default_middleware=[
|
||||
context_based_model, # 动态模型选择
|
||||
TodoListMiddleware(),
|
||||
FilesystemMiddleware(),
|
||||
SummarizationMiddleware(
|
||||
model=model,
|
||||
model=sub_model,
|
||||
trigger=("tokens", 110000),
|
||||
keep=("messages", 10),
|
||||
trim_tokens_to_summarize=None,
|
||||
|
||||
@ -126,10 +126,13 @@ export const agentApi = {
|
||||
* 保存智能体配置
|
||||
* @param {string} agentName - 智能体名称
|
||||
* @param {Object} config - 配置对象
|
||||
* @param {Object} options - 额外参数 (e.g., { reload_graph: true })
|
||||
* @returns {Promise} - 保存结果
|
||||
*/
|
||||
saveAgentConfig: async (agentName, config) => {
|
||||
return apiAdminPost(`/api/chat/agent/${agentName}/config`, config)
|
||||
saveAgentConfig: async (agentName, config, options = {}) => {
|
||||
const queryParams = new URLSearchParams(options).toString();
|
||||
const url = `/api/chat/agent/${agentName}/config` + (queryParams ? `?${queryParams}` : '');
|
||||
return apiAdminPost(url, config)
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
<!-- 模型选择 -->
|
||||
<div v-if="value.template_metadata.kind === 'llm'" class="model-selector">
|
||||
<ModelSelectorComponent
|
||||
@select-model="handleModelChange"
|
||||
@select-model="(spec) => handleModelChange(key, spec)"
|
||||
:model_spec="agentConfig[key] || ''"
|
||||
/>
|
||||
</div>
|
||||
@ -219,7 +219,7 @@
|
||||
<div class="sidebar-footer" v-if="!isEmptyConfig">
|
||||
<div class="form-actions">
|
||||
<a-button @click="saveConfig" class="save-btn" :class="{'changed': agentStore.hasConfigChanges}">
|
||||
保存配置
|
||||
{{ needsReload ? '保存配置并重新加载' : '保存配置' }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -333,6 +333,12 @@ const isEmptyConfig = computed(() => {
|
||||
return !selectedAgentId.value || Object.keys(configurableItems.value).length === 0;
|
||||
});
|
||||
|
||||
const needsReload = computed(() => {
|
||||
return selectedAgent.value &&
|
||||
selectedAgent.value.capabilities &&
|
||||
selectedAgent.value.capabilities.includes('reload_graph');
|
||||
});
|
||||
|
||||
const filteredTools = computed(() => {
|
||||
const toolsList = availableTools.value ? Object.values(availableTools.value) : [];
|
||||
if (!toolsSearchText.value) {
|
||||
@ -363,10 +369,10 @@ const getPlaceholder = (key, value) => {
|
||||
return `(默认: ${value.default})`;
|
||||
};
|
||||
|
||||
const handleModelChange = (spec) => {
|
||||
const handleModelChange = (key, spec) => {
|
||||
if (typeof spec !== 'string' || !spec) return;
|
||||
agentStore.updateAgentConfig({
|
||||
model: spec
|
||||
[key]: spec
|
||||
});
|
||||
};
|
||||
|
||||
@ -529,7 +535,12 @@ const saveConfig = async () => {
|
||||
message.info('检测到无效配置项,已自动过滤');
|
||||
}
|
||||
|
||||
await agentStore.saveAgentConfig();
|
||||
const options = {};
|
||||
if (selectedAgent.value && selectedAgent.value.capabilities && selectedAgent.value.capabilities.includes('reload_graph')) {
|
||||
options.reload_graph = true;
|
||||
}
|
||||
|
||||
await agentStore.saveAgentConfig(options);
|
||||
message.success('配置已保存到服务器');
|
||||
} catch (error) {
|
||||
console.error('保存配置到服务器出错:', error);
|
||||
|
||||
@ -88,6 +88,7 @@ const isTaskResult = computed(() => {
|
||||
|
||||
const isKnowledgeBaseResult = computed(() => {
|
||||
const currentTool = tool.value;
|
||||
|
||||
if (currentTool && currentTool.metadata) {
|
||||
const metadata = currentTool.metadata;
|
||||
const hasKnowledgebaseTag = metadata.tag && metadata.tag.includes('knowledgebase');
|
||||
|
||||
@ -249,13 +249,14 @@ export const useAgentStore = defineStore('agent', () => {
|
||||
|
||||
/**
|
||||
* 保存智能体配置
|
||||
* @param {Object} options - 额外参数 (e.g., { reload_graph: true })
|
||||
*/
|
||||
async function saveAgentConfig(agentId = null) {
|
||||
const targetAgentId = agentId || selectedAgentId.value
|
||||
async function saveAgentConfig(options = {}) {
|
||||
const targetAgentId = selectedAgentId.value
|
||||
if (!targetAgentId) return
|
||||
|
||||
try {
|
||||
await agentApi.saveAgentConfig(targetAgentId, agentConfig.value)
|
||||
await agentApi.saveAgentConfig(targetAgentId, agentConfig.value, options)
|
||||
originalAgentConfig.value = { ...agentConfig.value }
|
||||
} catch (err) {
|
||||
console.error('Failed to save agent config:', err)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user