ForcePilot/src/agents/common/middlewares/runtime_config_middleware.py
Wenjie Zhang 796c804514 feat(middleware): 新增 SummaryOffloadMiddleware 中间件
- 新增 SummaryOffloadMiddleware,支持 summary 历史管理和 ToolResult 卸载
  - 重构 attachment_middleware 增强 thread_id 获取逻辑
  - 更新 chatbot/deep_agent/reporter 智能体配置
  - 优化 AgentPanel UI 交互体验
2026-02-02 21:44:10 +08:00

136 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from collections.abc import Callable
from typing import Any
from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
from langchain_core.messages import SystemMessage
from src.agents.common import load_chat_model
from src.agents.common.tools import get_buildin_tools, get_kb_based_tools
from src.services.mcp_service import get_enabled_mcp_tools
from src.utils.datetime_utils import shanghai_now
from src.utils.logging_config import logger
class RuntimeConfigMiddleware(AgentMiddleware):
"""运行时配置中间件 - 应用模型/工具/知识库/MCP/提示词配置
注意:所有可能用到的知识库工具必须在初始化时预加载并注册到 self.tools
运行时根据配置从 self.tools 中筛选工具,不能动态添加新工具
支持自定义上下文字段名称,以便在不同场景(如主智能体/子智能体)使用不同的配置字段
"""
def __init__(
self,
*,
extra_tools: list[Any] | None = None,
model_context_name: str = "model",
system_prompt_context_name: str = "system_prompt",
tools_context_name: str = "tools",
knowledges_context_name: str = "knowledges",
mcps_context_name: str = "mcps",
enable_model_override: bool = True,
enable_system_prompt_override: bool = True,
enable_tools_override: bool = True,
):
"""初始化中间件
Args:
extra_tools: 额外工具列表(从 create_agent 的 tools 参数传入)
model_context_name: 上下文中的模型字段名称(默认 "model"
system_prompt_context_name: 上下文中的系统提示词字段名称(默认 "system_prompt"
tools_context_name: 上下文中的工具列表字段名称(默认 "tools"
knowledges_context_name: 上下文中的知识库列表字段名称(默认 "knowledges"
mcps_context_name: 上下文中的 MCP 服务器列表字段名称(默认 "mcps"
enable_model_override: 是否允许覆盖模型配置(默认 True
enable_system_prompt_override: 是否允许覆盖系统提示词(默认 True
enable_tools_override: 是否允许覆盖工具列表(默认 True
"""
super().__init__()
self.kb_tools = get_kb_based_tools()
self.buildin_tools = get_buildin_tools()
self.tools = self.kb_tools + self.buildin_tools + (extra_tools or [])
# 存储自定义字段名称
self.model_context_name = model_context_name
self.system_prompt_context_name = system_prompt_context_name
self.tools_context_name = tools_context_name
self.knowledges_context_name = knowledges_context_name
self.mcps_context_name = mcps_context_name
# 存储覆盖配置
self.enable_model_override = enable_model_override
self.enable_system_prompt_override = enable_system_prompt_override
self.enable_tools_override = enable_tools_override
logger.debug(
f"Initialized RuntimeConfigMiddleware with custom field names: model={model_context_name}, "
f"system_prompt={system_prompt_context_name}, tools={tools_context_name}, "
f"knowledges={knowledges_context_name}, mcps={mcps_context_name}"
)
async def awrap_model_call(
self, request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
runtime_context = request.runtime.context
overrides: dict[str, Any] = {}
# 1. 模型覆盖(可选)
if self.enable_model_override:
model = load_chat_model(getattr(runtime_context, self.model_context_name, None))
overrides["model"] = model
# 2. 工具覆盖(可选)
if self.enable_tools_override:
enabled_tools = await self.get_tools_from_context(runtime_context)
existing_tools = list(request.tools or [])
merged_tools = []
for t_bind in existing_tools:
# (1) 已启用的工具保留
# (2) 非本中间件管理的工具保留
if t_bind.name in [t.name for t in enabled_tools] or t_bind.name not in [t.name for t in self.tools]:
merged_tools.append(t_bind)
overrides["tools"] = merged_tools
logger.debug(f"RuntimeConfigMiddleware selected tools: {[t.name for t in merged_tools]}")
# 3. 系统提示词覆盖(可选)
if self.enable_system_prompt_override:
cur_datetime = f"当前时间:{shanghai_now().strftime('%Y-%m-%d %H:%M:%S')} UTC"
system_prompt = getattr(runtime_context, self.system_prompt_context_name, "") or ""
new_content = list(request.system_message.content_blocks) + [
{"type": "text", "text": f"{cur_datetime}\n\n{system_prompt}"}
]
new_system_message = SystemMessage(content=new_content)
overrides["system_message"] = new_system_message
if overrides:
request = request.override(**overrides)
return await handler(request)
async def get_tools_from_context(self, context) -> list:
"""从上下文配置中获取工具列表"""
selected_tools = []
# 1. 基础工具 (从 context.tools 中筛选)
tools = getattr(context, self.tools_context_name, None)
if tools:
tools_map = {t.name: t for t in self.tools}
for tool_name in tools:
if tool_name in tools_map:
selected_tools.append(tools_map[tool_name])
# 2. 知识库工具
knowledges = getattr(context, self.knowledges_context_name, None)
if knowledges:
kb_tools = get_kb_based_tools(db_names=knowledges)
selected_tools.extend(kb_tools)
# 3. MCP 工具(使用统一入口,自动过滤 disabled_tools
mcps = getattr(context, self.mcps_context_name, None)
if mcps:
for server_name in mcps:
mcp_tools = await get_enabled_mcp_tools(server_name)
selected_tools.extend(mcp_tools)
return selected_tools