From a8491c5beb42faa24532e5baec358b2e460a2210 Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Sat, 25 Oct 2025 20:01:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8=20create=5Fagent=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=9B=BF=E6=8D=A2=E8=80=81=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=20create=5Freact=5Fagent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/changelog/roadmap.md | 8 ++--- src/agents/chatbot/context.py | 5 --- src/agents/common/context.py | 11 ++++--- src/agents/react/graph.py | 40 +++++++++++++++-------- src/knowledge/manager.py | 30 ++++++++--------- web/src/components/AgentConfigSidebar.vue | 2 +- 6 files changed, 54 insertions(+), 42 deletions(-) diff --git a/docs/changelog/roadmap.md b/docs/changelog/roadmap.md index f3ab953f..1eca0dee 100644 --- a/docs/changelog/roadmap.md +++ b/docs/changelog/roadmap.md @@ -11,16 +11,13 @@ ## Next -- [ ] 修改现有的智能体Demo,并尽量将默认助手的特性兼容到 LangGraph 的 [`create_agent`](https://docs.langchain.com/oss/python/langchain/agents) 中 +- [x] 修改现有的智能体Demo,并尽量将默认助手的特性兼容到 LangGraph 的 [`create_agent`](https://docs.langchain.com/oss/python/langchain/agents) 中 - [ ] 基于 create_agent 创建 SQL Viewer 智能体 - [ ] 优化 MCP 逻辑,支持 common + special 创建方式 - [ ] 添加对于上传文件的支持 - [ ] 统一图谱数据结构,优化可视化方式 [#298](https://github.com/xerrors/Yuxi-Know/issues/298) - [ ] 集成智能体评估,首先使用命令行来实现,然后考虑放在 UI 里面展示 - [ ] 开发与生产环境隔离,构建生产镜像 -- [x] 支持 MinerU 2.5 的解析方法 -- [x] 文件管理:(1)文件选择的时候会跨数据库;(2)文件校验会算上失败的文件; -- [x] Tasker 中获取历史任务的时候,仅获取 top100 个 task。 ## Later @@ -37,3 +34,6 @@ - [x] 优化对文档信息的检索展示(检索结果页、详情页) - [x] 当前 ReAct 智能体有消息顺序错乱的 bug,且不会默认调用工具 - [x] 优化全局配置的管理模型,优化配置管理 +- [x] 支持 MinerU 2.5 的解析方法 +- [x] 文件管理:(1)文件选择的时候会跨数据库;(2)文件校验会算上失败的文件; +- [x] Tasker 中获取历史任务的时候,仅获取 top100 个 task。 diff --git a/src/agents/chatbot/context.py b/src/agents/chatbot/context.py index 816fcc82..7bb1adad 100644 --- a/src/agents/chatbot/context.py +++ b/src/agents/chatbot/context.py @@ -10,11 +10,6 @@ from .tools import get_tools @dataclass(kw_only=True) class Context(BaseContext): - model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field( - default="siliconflow/Qwen/Qwen3-235B-A22B-Instruct-2507", - metadata={"name": "智能体模型", "options": [], "description": "智能体的驱动模型"}, - ) - tools: Annotated[list[dict], {"__template_metadata__": {"kind": "tools"}}] = field( default_factory=list, metadata={ diff --git a/src/agents/common/context.py b/src/agents/common/context.py index eb00f79e..00b82db1 100644 --- a/src/agents/common/context.py +++ b/src/agents/common/context.py @@ -1,12 +1,10 @@ """Define the configurable parameters for the agent.""" -from __future__ import annotations - import os import uuid from dataclasses import MISSING, dataclass, field, fields from pathlib import Path -from typing import get_args, get_origin +from typing import Annotated, get_args, get_origin import yaml @@ -46,8 +44,13 @@ class BaseContext: metadata={"name": "系统提示词", "description": "用来描述智能体的角色和行为"}, ) + model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field( + default="siliconflow/Qwen/Qwen3-235B-A22B-Instruct-2507", + metadata={"name": "智能体模型", "options": [], "description": "智能体的驱动模型"}, + ) + @classmethod - def from_file(cls, module_name: str, input_context: dict = None) -> BaseContext: + def from_file(cls, module_name: str, input_context: dict = None) -> "BaseContext": """Load configuration from a YAML file. 用于持久化配置""" # 从文件加载配置 diff --git a/src/agents/react/graph.py b/src/agents/react/graph.py index d70a6180..831905fb 100644 --- a/src/agents/react/graph.py +++ b/src/agents/react/graph.py @@ -1,12 +1,10 @@ from pathlib import Path -from langchain.messages import AnyMessage, SystemMessage -from langgraph.prebuilt import create_react_agent -from langgraph.runtime import get_runtime +from langchain.agents import create_agent +from langchain.agents.middleware import ModelRequest, ModelResponse, dynamic_prompt, wrap_model_call from src import config as sys_config from src.agents.common.base import BaseAgent -from src.agents.common.context import BaseContext from src.agents.common.models import load_chat_model from src.agents.common.tools import get_buildin_tools from src.utils import logger @@ -14,14 +12,24 @@ from src.utils import logger model = load_chat_model("siliconflow/Qwen/Qwen3-235B-A22B-Instruct-2507") -def prompt(state) -> list[AnyMessage]: - runtime = get_runtime(BaseContext) - system_msg = SystemMessage(content=runtime.context.system_prompt) - return [system_msg] + state["messages"] +@dynamic_prompt +def context_aware_prompt(request: ModelRequest) -> str: + runtime = request.runtime + return runtime.context.system_prompt + + +@wrap_model_call +async def context_based_model(request: ModelRequest, handler) -> ModelResponse: + # 从 runtime context 读取配置 + model_spec = request.runtime.context.model + model = load_chat_model(model_spec) + + request = request.override(model=model) + return await handler(request) class ReActAgent(BaseAgent): - name = "ReAct (all tools)" + name = "智能体 Demo" description = "A react agent that can answer questions and help with tasks." def __init__(self, **kwargs): @@ -30,15 +38,21 @@ class ReActAgent(BaseAgent): self.workdir = Path(sys_config.save_dir) / "agents" / self.module_name self.workdir.mkdir(parents=True, exist_ok=True) + def get_tools(self): + return get_buildin_tools() + async def get_graph(self, **kwargs): if self.graph: return self.graph - available_tools = get_buildin_tools() - self.checkpointer = await self._get_checkpointer() - # 创建 ReActAgent - graph = create_react_agent(model, tools=available_tools, prompt=prompt, checkpointer=self.checkpointer) + graph = create_agent( + model=model, + tools=self.get_tools(), + middleware=[context_aware_prompt, context_based_model], + checkpointer=await self._get_checkpointer(), + ) + self.graph = graph logger.info("ReActAgent 使用内存 checkpointer 构建成功") return graph diff --git a/src/knowledge/manager.py b/src/knowledge/manager.py index 429da5e2..95c148ba 100644 --- a/src/knowledge/manager.py +++ b/src/knowledge/manager.py @@ -46,21 +46,21 @@ class KnowledgeBaseManager: logger.info("KnowledgeBaseManager initialized") # 在后台运行数据一致性检测(不阻塞初始化) - try: - # 尝试获取当前事件循环,如果没有则创建新的 - try: - loop = asyncio.get_event_loop() - if loop.is_running(): - # 如果已经在事件循环中,创建任务 - asyncio.create_task(self.detect_data_inconsistencies()) - else: - # 如果事件循环未运行,直接运行 - loop.run_until_complete(self.detect_data_inconsistencies()) - except RuntimeError: - # 没有事件循环,创建一个来运行检测 - asyncio.run(self.detect_data_inconsistencies()) - except Exception as e: - logger.warning(f"初始化时运行数据一致性检测失败: {e}") + # try: + # # 尝试获取当前事件循环,如果没有则创建新的 + # try: + # loop = asyncio.get_event_loop() + # if loop.is_running(): + # # 如果已经在事件循环中,创建任务 + # asyncio.create_task(self.detect_data_inconsistencies()) + # else: + # # 如果事件循环未运行,直接运行 + # loop.run_until_complete(self.detect_data_inconsistencies()) + # except RuntimeError: + # # 没有事件循环,创建一个来运行检测 + # asyncio.run(self.detect_data_inconsistencies()) + # except Exception as e: + # logger.warning(f"初始化时运行数据一致性检测失败: {e}") def _load_global_metadata(self): """加载全局元数据""" diff --git a/web/src/components/AgentConfigSidebar.vue b/web/src/components/AgentConfigSidebar.vue index d2f616d3..92bf16bb 100644 --- a/web/src/components/AgentConfigSidebar.vue +++ b/web/src/components/AgentConfigSidebar.vue @@ -18,7 +18,7 @@