diff --git a/server/routers/chat_router.py b/server/routers/chat_router.py index 5ac3b2de..dfaa9cc6 100644 --- a/server/routers/chat_router.py +++ b/server/routers/chat_router.py @@ -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="保存智能体配置失败") diff --git a/src/agents/__init__.py b/src/agents/__init__.py index 210ed251..89bbe0ab 100644 --- a/src/agents/__init__.py +++ b/src/agents/__init__.py @@ -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): diff --git a/src/agents/common/base.py b/src/agents/common/base.py index d67fe8c2..25a61b9a 100644 --- a/src/agents/common/base.py +++ b/src/agents/common/base.py @@ -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: """ diff --git a/src/agents/deep_agent/context.py b/src/agents/deep_agent/context.py index 8fa18959..34e957cf 100644 --- a/src/agents/deep_agent/context.py +++ b/src/agents/deep_agent/context.py @@ -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).", + }, + ) diff --git a/src/agents/deep_agent/graph.py b/src/agents/deep_agent/graph.py index f9c5847d..d59f1482 100644 --- a/src/agents/deep_agent/graph.py +++ b/src/agents/deep_agent/graph.py @@ -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, diff --git a/web/src/apis/agent_api.js b/web/src/apis/agent_api.js index 089ad9e1..ef89ca6e 100644 --- a/web/src/apis/agent_api.js +++ b/web/src/apis/agent_api.js @@ -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) }, /** diff --git a/web/src/components/AgentConfigSidebar.vue b/web/src/components/AgentConfigSidebar.vue index d742ff8e..4e8bdd16 100644 --- a/web/src/components/AgentConfigSidebar.vue +++ b/web/src/components/AgentConfigSidebar.vue @@ -55,7 +55,7 @@