From 57d6df79bfa36fe372d7e7e9f9b0a6d5fdef57e4 Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Thu, 29 Jan 2026 22:07:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(middleware):=20=E4=BF=AE=E5=A4=8D=20Runtime?= =?UTF-8?q?ConfigMiddleware=20=E7=9A=84=E5=B7=A5=E5=85=B7override=20?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=E5=85=B6=E4=BD=99=E4=B8=AD=E9=97=B4?= =?UTF-8?q?=E4=BB=B6=E5=A4=B1=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=88?= =?UTF-8?q?=E8=BF=98=E9=9C=80=E8=A6=81=E8=BF=9B=E4=B8=80=E6=AD=A5=E6=8E=92?= =?UTF-8?q?=E6=9F=A5=20subagents=20=E7=9A=84=20model=20=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E6=9C=89=E9=97=AE=E9=A2=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/latest/advanced/agents-config.md | 10 +++ .../middlewares/runtime_config_middleware.py | 61 ++++++------------- src/agents/deep_agent/graph.py | 10 --- 3 files changed, 29 insertions(+), 52 deletions(-) diff --git a/docs/latest/advanced/agents-config.md b/docs/latest/advanced/agents-config.md index 85156132..850ce6fa 100644 --- a/docs/latest/advanced/agents-config.md +++ b/docs/latest/advanced/agents-config.md @@ -114,6 +114,16 @@ class ReporterContext(BaseContext): 中间件位于 `src/agents/common/middlewares`,包含上下文感知提示词、模型选择、动态工具加载以及附件注入等实现。如果需要编写新的中间件,请遵循 LangChain 官方文档中对 `AgentMiddleware`、`ModelRequest`、`ModelResponse` 等接口的定义,完成后在该目录的 `__init__.py` 暴露入口,主智能体即可在 `middleware` 列表中引用。 +#### RuntimeConfigMiddleware + +`RuntimeConfigMiddleware`([runtime_config_middleware.py](https://github.com/xerrors/Yuxi-Know/blob/main/src/agents/common/middlewares/runtime_config_middleware.py))是系统默认的核心中间件之一,负责在每次模型调用前自动注入运行时配置: + +1. **自动注入当前时间**:在 system prompt 开头追加当前时间,格式为 `当前时间:YYYY-MM-DD HH:MM:SS`,确保 LLM 能获取准确的时间上下文。 +2. **动态加载工具**:根据 `context.tools`、`context.knowledges`、`context.mcps` 自动组装可用工具列表。 +3. **模型选择**:根据 `context.model` 加载对应模型配置。 + +如需自定义时间注入逻辑或禁用该行为,可继承该中间件并覆盖 `awrap_model_call` 方法。 + #### 文件上传中间件 文件上传功能通过 `inject_attachment_context` 中间件实现(位于 `src/agents/common/middlewares/attachment_middleware.py`)。该中间件基于 LangChain 1.0 的 `AgentMiddleware` 标准实现,具有以下特点: diff --git a/src/agents/common/middlewares/runtime_config_middleware.py b/src/agents/common/middlewares/runtime_config_middleware.py index ea28262a..cb1e784d 100644 --- a/src/agents/common/middlewares/runtime_config_middleware.py +++ b/src/agents/common/middlewares/runtime_config_middleware.py @@ -4,29 +4,15 @@ 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_kb_based_tools, get_buildin_tools +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 -def _is_system_message(msg: Any) -> bool: - if isinstance(msg, dict): - role = msg.get("role") or msg.get("type") - return role == "system" - msg_type = getattr(msg, "type", None) or getattr(msg, "role", None) - return msg_type == "system" - - -def _get_message_content(msg: Any) -> str | None: - if isinstance(msg, dict): - content = msg.get("content") - return str(content) if content is not None else None - content = getattr(msg, "content", None) - return str(content) if content is not None else None - - class RuntimeConfigMiddleware(AgentMiddleware): """运行时配置中间件 - 应用模型/工具/知识库/MCP/提示词配置 @@ -54,35 +40,26 @@ class RuntimeConfigMiddleware(AgentMiddleware): model = load_chat_model(getattr(runtime_context, "model", None)) enabled_tools = await self.get_tools_from_context(runtime_context) - system_prompt = getattr(runtime_context, "system_prompt", None) - logger.debug(f"RuntimeConfigMiddleware: model={model}, " - f"tools={[t.name for t in enabled_tools]}. ") - existing_systems: list[Any] = [] - remaining: list[Any] = [] - in_prefix = True - for msg in request.messages: - if in_prefix and _is_system_message(msg): - existing_systems.append(msg) - else: - in_prefix = False - remaining.append(msg) + # 合并之前中间件设置的 tools,避免覆盖 + existing_tools = list(request.tools or []) + merged_tools = existing_tools + enabled_tools - existing_contents = [_get_message_content(m) for m in existing_systems] + # 动态生成 system message,添加当前时间 + cur_datetime = f"当前时间:{shanghai_now().strftime('%Y-%m-%d %H:%M:%S')} UTC" + system_prompt = getattr(runtime_context, "system_prompt", "") 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) - new_systems: list[Any] = [] - if system_prompt: - try: - idx = existing_contents.index(system_prompt) - except ValueError: - new_systems.append({"role": "system", "content": system_prompt}) - else: - new_systems.append(existing_systems.pop(idx)) - existing_contents.pop(idx) + logger.debug(f"RuntimeConfigMiddleware: model={model}, tools={[t.name for t in merged_tools]}. ") - messages = [*new_systems, *existing_systems, *remaining] - - request = request.override(model=model, tools=enabled_tools, messages=messages) + request = request.override( + model=model, + tools=merged_tools, + system_message=new_system_message + ) return await handler(request) async def get_tools_from_context(self, context) -> list: diff --git a/src/agents/deep_agent/graph.py b/src/agents/deep_agent/graph.py index 460e7952..8ac69d86 100644 --- a/src/agents/deep_agent/graph.py +++ b/src/agents/deep_agent/graph.py @@ -5,10 +5,8 @@ from deepagents.middleware.patch_tool_calls import PatchToolCallsMiddleware from deepagents.middleware.subagents import SubAgentMiddleware from langchain.agents import create_agent from langchain.agents.middleware import ( - ModelRequest, SummarizationMiddleware, TodoListMiddleware, - dynamic_prompt, ) from src.agents.common import BaseAgent, load_chat_model @@ -17,7 +15,6 @@ from src.agents.common.tools import get_tavily_search from src.services.mcp_service import get_tools_from_all_servers from .context import DeepContext -from .prompts import DEEP_PROMPT def _get_research_sub_agent(search_tools: list) -> dict: @@ -57,12 +54,6 @@ critique_sub_agent = { } -@dynamic_prompt -def context_aware_prompt(request: ModelRequest) -> str: - """从 runtime context 动态生成系统提示词""" - return DEEP_PROMPT + "\n\n\n" + request.runtime.context.system_prompt - - class DeepAgent(BaseAgent): name = "深度分析智能体" description = "具备规划、深度分析和子智能体协作能力的智能体,可以处理复杂的多步骤任务" @@ -111,7 +102,6 @@ class DeepAgent(BaseAgent): model=model, system_prompt=context.system_prompt, middleware=[ - context_aware_prompt, # 动态系统提示词 inject_attachment_context, # 附件上下文注入 RuntimeConfigMiddleware(extra_tools=all_mcp_tools), TodoListMiddleware(),