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