From 4ea2e862f720ad1020cd0a024e7309407e731d45 Mon Sep 17 00:00:00 2001 From: Jiayuan Kang Date: Wed, 16 Jul 2025 18:16:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0workflow=20agent?= =?UTF-8?q?=EF=BC=8C=E4=B8=8A=E4=B8=8B=E6=96=87=E7=AE=A1=E7=90=86=E7=AD=89?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加workflow agent,上下文管理等接口 #ICLT7L:组件功能实现 --- jiuwen/{controller => agent}/__init__.py | 0 .../{controller => agent}/common/__init__.py | 0 jiuwen/{controller => agent}/common/enum.py | 10 ++- jiuwen/{controller => agent}/common/schema.py | 0 .../{controller => agent}/config/__init__.py | 0 jiuwen/{controller => agent}/config/base.py | 8 ++- .../config/react_config.py | 6 +- jiuwen/agent/config/workflow_config.py | 19 ++++++ .../{controller => agent}/context/__init__.py | 0 .../context/controller_context_manager.py | 18 ++++++ jiuwen/agent/context/message_manager.py | 2 + jiuwen/agent/context/model_manager.py | 2 + jiuwen/agent/context/tool_manager.py | 2 + jiuwen/agent/context/workflow_manager.py | 2 + .../handler => agent/controller}/__init__.py | 0 .../mode => agent/controller}/base.py | 21 +++++-- .../controller}/react_controller.py | 39 ++++++------ .../agent/controller/workflow_controller.py | 27 ++++++++ jiuwen/{controller => agent}/react_agent.py | 63 +++++++------------ .../mode => agent/skills}/__init__.py | 0 .../skills => agent/state}/__init__.py | 0 jiuwen/{controller => agent}/state/base.py | 0 .../state/react_state.py | 4 +- .../state/workflow_state.py} | 0 jiuwen/agent/workflow_agent.py | 38 +++++++++++ jiuwen/controller/task/planner/__init__.py | 0 jiuwen/core/agent/agent.py | 61 ++++++++++++++++++ .../state => core/agent/handler}/__init__.py | 0 .../agent}/handler/base.py | 6 +- .../agent/task}/__init__.py | 0 jiuwen/core/agent/task/task.py | 27 ++++++++ jiuwen/core/agent/task/task_manager.py | 27 ++++++++ .../unit_tests/agent}/__init__.py | 0 tests/unit_tests/agent/test_workflow_agent.py | 33 ++++++++++ tests/unit_tests/workflow/test_llm_comp.py | 9 ++- 35 files changed, 345 insertions(+), 79 deletions(-) rename jiuwen/{controller => agent}/__init__.py (100%) rename jiuwen/{controller => agent}/common/__init__.py (100%) rename jiuwen/{controller => agent}/common/enum.py (68%) rename jiuwen/{controller => agent}/common/schema.py (100%) rename jiuwen/{controller => agent}/config/__init__.py (100%) rename jiuwen/{controller => agent}/config/base.py (78%) rename jiuwen/{controller => agent}/config/react_config.py (87%) create mode 100644 jiuwen/agent/config/workflow_config.py rename jiuwen/{controller => agent}/context/__init__.py (100%) create mode 100644 jiuwen/agent/context/controller_context_manager.py create mode 100644 jiuwen/agent/context/message_manager.py create mode 100644 jiuwen/agent/context/model_manager.py create mode 100644 jiuwen/agent/context/tool_manager.py create mode 100644 jiuwen/agent/context/workflow_manager.py rename jiuwen/{controller/handler => agent/controller}/__init__.py (100%) rename jiuwen/{controller/mode => agent/controller}/base.py (35%) rename jiuwen/{controller/mode => agent/controller}/react_controller.py (49%) create mode 100644 jiuwen/agent/controller/workflow_controller.py rename jiuwen/{controller => agent}/react_agent.py (36%) rename jiuwen/{controller/mode => agent/skills}/__init__.py (100%) rename jiuwen/{controller/skills => agent/state}/__init__.py (100%) rename jiuwen/{controller => agent}/state/base.py (100%) rename jiuwen/{controller => agent}/state/react_state.py (83%) rename jiuwen/{controller/agent.py => agent/state/workflow_state.py} (100%) create mode 100644 jiuwen/agent/workflow_agent.py delete mode 100644 jiuwen/controller/task/planner/__init__.py create mode 100644 jiuwen/core/agent/agent.py rename jiuwen/{controller/state => core/agent/handler}/__init__.py (100%) rename jiuwen/{controller => core/agent}/handler/base.py (89%) rename jiuwen/{controller/task/dispatcher => core/agent/task}/__init__.py (100%) create mode 100644 jiuwen/core/agent/task/task.py create mode 100644 jiuwen/core/agent/task/task_manager.py rename {jiuwen/controller/task/executor => tests/unit_tests/agent}/__init__.py (100%) create mode 100644 tests/unit_tests/agent/test_workflow_agent.py diff --git a/jiuwen/controller/__init__.py b/jiuwen/agent/__init__.py similarity index 100% rename from jiuwen/controller/__init__.py rename to jiuwen/agent/__init__.py diff --git a/jiuwen/controller/common/__init__.py b/jiuwen/agent/common/__init__.py similarity index 100% rename from jiuwen/controller/common/__init__.py rename to jiuwen/agent/common/__init__.py diff --git a/jiuwen/controller/common/enum.py b/jiuwen/agent/common/enum.py similarity index 68% rename from jiuwen/controller/common/enum.py rename to jiuwen/agent/common/enum.py index 8fc702d..1b1bfa9 100644 --- a/jiuwen/controller/common/enum.py +++ b/jiuwen/agent/common/enum.py @@ -3,7 +3,7 @@ # Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved """enum constants""" -from enum import Enum +from enum import Enum, auto class ControllerType(Enum): @@ -24,3 +24,11 @@ class ReActStatus(Enum): LLM_RESPONSE = "llm_response" TOOL_INVOKED = "tool_invoked" COMPLETED = "completed" + + +class TaskStatus(Enum): + PENDING = auto() # 已创建,尚未开始 + RUNNING = auto() # 正在执行 + SUCCESS = auto() # 成功结束 + FAILED = auto() # 异常结束 + CANCELLED = auto() # 被取消 diff --git a/jiuwen/controller/common/schema.py b/jiuwen/agent/common/schema.py similarity index 100% rename from jiuwen/controller/common/schema.py rename to jiuwen/agent/common/schema.py diff --git a/jiuwen/controller/config/__init__.py b/jiuwen/agent/config/__init__.py similarity index 100% rename from jiuwen/controller/config/__init__.py rename to jiuwen/agent/config/__init__.py diff --git a/jiuwen/controller/config/base.py b/jiuwen/agent/config/base.py similarity index 78% rename from jiuwen/controller/config/base.py rename to jiuwen/agent/config/base.py index 0e0bbe8..769615d 100644 --- a/jiuwen/controller/config/base.py +++ b/jiuwen/agent/config/base.py @@ -6,8 +6,8 @@ from typing import List from pydantic import BaseModel, Field -from jiuwen.controller.common.schema import PluginSchema, WorkflowSchema -from jiuwen.controller.common.enum import ControllerType +from jiuwen.agent.common.schema import PluginSchema, WorkflowSchema +from jiuwen.agent.common.enum import ControllerType class AgentConfig(BaseModel): @@ -17,3 +17,7 @@ class AgentConfig(BaseModel): controller_type: ControllerType = Field(default=ControllerType.Undefined) plugins: List[PluginSchema] = Field(default_factory=list) workflows: List[WorkflowSchema] = Field(default_factory=list) + + +class AgentCard: + ... diff --git a/jiuwen/controller/config/react_config.py b/jiuwen/agent/config/react_config.py similarity index 87% rename from jiuwen/controller/config/react_config.py rename to jiuwen/agent/config/react_config.py index a3c4901..5ce6053 100644 --- a/jiuwen/controller/config/react_config.py +++ b/jiuwen/agent/config/react_config.py @@ -1,9 +1,9 @@ -from typing import List, Dict +from typing import List, Dict, Any from pydantic import BaseModel, Field -from jiuwen.controller.common.enum import ControllerType -from jiuwen.controller.config.base import AgentConfig +from jiuwen.agent.common.enum import ControllerType +from jiuwen.agent.config.base import AgentConfig class ConstrainConfig(BaseModel): diff --git a/jiuwen/agent/config/workflow_config.py b/jiuwen/agent/config/workflow_config.py new file mode 100644 index 0000000..9536150 --- /dev/null +++ b/jiuwen/agent/config/workflow_config.py @@ -0,0 +1,19 @@ +from typing import List, Dict, Any + +from pydantic import Field + +from jiuwen.agent.common.schema import WorkflowSchema +from jiuwen.agent.config.base import AgentConfig + + +class WorkflowAgentConfig(AgentConfig): + # 全局超时(秒) + timeout: int = Field(default=60, ge=1) + # 开始工作流 + start_workflow: WorkflowSchema = Field(default_factory=WorkflowSchema) + # 结束工作流 + end_workflow: WorkflowSchema = Field(default_factory=WorkflowSchema) + # 全局变量 + global_variables: List[dict] = Field(default_factory=list) + # 全局参数模板(可选) + global_params: Dict[str, Any] = Field(default_factory=dict) diff --git a/jiuwen/controller/context/__init__.py b/jiuwen/agent/context/__init__.py similarity index 100% rename from jiuwen/controller/context/__init__.py rename to jiuwen/agent/context/__init__.py diff --git a/jiuwen/agent/context/controller_context_manager.py b/jiuwen/agent/context/controller_context_manager.py new file mode 100644 index 0000000..3ad1536 --- /dev/null +++ b/jiuwen/agent/context/controller_context_manager.py @@ -0,0 +1,18 @@ +from jiuwen.agent.config.base import AgentConfig +from jiuwen.agent.context.message_manager import MessageMgr +from jiuwen.agent.context.model_manager import ModelMgr +from jiuwen.agent.context.tool_manager import ToolMgr +from jiuwen.agent.context.workflow_manager import WorkflowMgr +from jiuwen.core.context.context import Context + + +class ControllerContextMgr: + """ + Agent上下文管理器: + """ + + def __init__(self, agent_config: AgentConfig, context: Context): + self.workflow_mgr = WorkflowMgr() + self.tool_mgr = ToolMgr() + self.model_mgr = ModelMgr() + self.message_mgr = MessageMgr() diff --git a/jiuwen/agent/context/message_manager.py b/jiuwen/agent/context/message_manager.py new file mode 100644 index 0000000..a045155 --- /dev/null +++ b/jiuwen/agent/context/message_manager.py @@ -0,0 +1,2 @@ +class MessageMgr: + pass diff --git a/jiuwen/agent/context/model_manager.py b/jiuwen/agent/context/model_manager.py new file mode 100644 index 0000000..263b8b8 --- /dev/null +++ b/jiuwen/agent/context/model_manager.py @@ -0,0 +1,2 @@ +class ModelMgr: + pass diff --git a/jiuwen/agent/context/tool_manager.py b/jiuwen/agent/context/tool_manager.py new file mode 100644 index 0000000..e48c59a --- /dev/null +++ b/jiuwen/agent/context/tool_manager.py @@ -0,0 +1,2 @@ +class ToolMgr: + pass diff --git a/jiuwen/agent/context/workflow_manager.py b/jiuwen/agent/context/workflow_manager.py new file mode 100644 index 0000000..825d51b --- /dev/null +++ b/jiuwen/agent/context/workflow_manager.py @@ -0,0 +1,2 @@ +class WorkflowMgr: + pass diff --git a/jiuwen/controller/handler/__init__.py b/jiuwen/agent/controller/__init__.py similarity index 100% rename from jiuwen/controller/handler/__init__.py rename to jiuwen/agent/controller/__init__.py diff --git a/jiuwen/controller/mode/base.py b/jiuwen/agent/controller/base.py similarity index 35% rename from jiuwen/controller/mode/base.py rename to jiuwen/agent/controller/base.py index b801aed..a2f4f76 100644 --- a/jiuwen/controller/mode/base.py +++ b/jiuwen/agent/controller/base.py @@ -2,15 +2,24 @@ # coding: utf-8 # Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved """Controller of Agent""" -from jiuwen.controller.config.base import AgentConfig -from jiuwen.core.context.context import Context -from jiuwen.core.graph.executable import Input, Output +from pydantic import BaseModel + +from jiuwen.agent.config.base import AgentConfig + + +class ControllerOutput(BaseModel): + ... + + +class ControllerInput(BaseModel): + ... class Controller: - def __init__(self, config: AgentConfig): + def __init__(self, config: AgentConfig, context_mgr): self._config = config - self._agent_sdk = None + self._agent_handler = None + self._context_mgr = context_mgr - def invoke(self, inputs: Input, context: Context) -> Output: + def invoke(self, inputs: ControllerInput) -> ControllerOutput: pass diff --git a/jiuwen/controller/mode/react_controller.py b/jiuwen/agent/controller/react_controller.py similarity index 49% rename from jiuwen/controller/mode/react_controller.py rename to jiuwen/agent/controller/react_controller.py index 84958e6..55e14b4 100644 --- a/jiuwen/controller/mode/react_controller.py +++ b/jiuwen/agent/controller/react_controller.py @@ -6,32 +6,35 @@ from typing import List from pydantic import BaseModel, Field -from jiuwen.controller.handler.base import AgentSdk -from jiuwen.controller.common.sub_task import SubTask -from jiuwen.controller.config.base import AgentConfig -from jiuwen.controller.mode.base import Controller -from jiuwen.core.context.context import Context -from jiuwen.core.graph.executable import Input +from jiuwen.agent.context.controller_context_manager import ControllerContextMgr +from jiuwen.agent.controller.base import Controller, ControllerOutput +from jiuwen.core.agent.handler.base import AgentHandler +from jiuwen.agent.config.base import AgentConfig +from jiuwen.core.agent.task.task import SubTask + + +class ReActControllerOutput(ControllerOutput): + should_continue: bool = Field(default=False) + output: str = Field(default="") + sub_tasks: List[SubTask] = Field(default_factory=list) + + +class ReActControllerInput: + ... class ReActController(Controller): - def __init__(self, config: AgentConfig): - super().__init__(config) + def __init__(self, config: AgentConfig, context_mgr: ControllerContextMgr): + super().__init__(config, context_mgr) - def invoke(self, inputs: Input, context: Context) -> ReActControllerOutput: + def invoke(self, inputs: ReActControllerInput) -> ReActControllerOutput: query = inputs.get("query", "") chat_history = self._latest_chat_history() tools = self._create_tools_metadata() llm_inputs = self._create_llm_inputs(query, chat_history, tools) result: dict = self._invoke_llm_and_parse_output(llm_inputs) - self._update_context(result, context) + self._update_context(result) return ReActControllerOutput(**result) - def set_agent_sdk(self, agent_sdk: AgentSdk): - self._agent_sdk = agent_sdk - - -class ReActControllerOutput(BaseModel): - should_continue: bool = Field(default=False) - output: str = Field(default="") - sub_tasks: List[SubTask] = Field(default_factory=list) + def set_agent_handler(self, agent_handler: AgentHandler): + self._agent_handler = agent_handler diff --git a/jiuwen/agent/controller/workflow_controller.py b/jiuwen/agent/controller/workflow_controller.py new file mode 100644 index 0000000..0062359 --- /dev/null +++ b/jiuwen/agent/controller/workflow_controller.py @@ -0,0 +1,27 @@ +from typing import List + +from pydantic import Field + +from jiuwen.agent.config.base import AgentConfig +from jiuwen.agent.controller.base import Controller, ControllerOutput, ControllerInput +from jiuwen.core.agent.task.task import SubTask + + +class WorkflowControllerOutput(ControllerOutput): + sub_tasks: List[SubTask] = Field(default_factory=list) + + +class WorkflowControllerInput(ControllerInput): + ... + + +class WorkflowController(Controller): + """ + 根据输入生成 WorkflowControllerOutput + """ + + def __init__(self, config: AgentConfig, context_mgr): + super().__init__(config, context_mgr) + + def invoke(self, inputs: WorkflowControllerInput) -> WorkflowControllerOutput: + return WorkflowControllerOutput() diff --git a/jiuwen/controller/react_agent.py b/jiuwen/agent/react_agent.py similarity index 36% rename from jiuwen/controller/react_agent.py rename to jiuwen/agent/react_agent.py index a247a75..25fbf81 100644 --- a/jiuwen/controller/react_agent.py +++ b/jiuwen/agent/react_agent.py @@ -2,54 +2,37 @@ # coding: utf-8 # Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved """ReActAgent""" - -from jiuwen.controller.config.base import AgentConfig -from jiuwen.controller.config.react_config import ReActAgentConfig -from jiuwen.controller.handler.base import AgentSdk, AgentSdkImpl -from jiuwen.controller.mode.base import Controller -from jiuwen.controller.mode.react_controller import ReActController, ReActControllerOutput -from jiuwen.controller.state.react_state import ReActState +from typing import Dict, Iterator, Any + +from jiuwen.agent.config.react_config import ReActAgentConfig +from jiuwen.agent.context.controller_context_manager import ControllerContextMgr +from jiuwen.agent.controller.react_controller import ReActController, ReActControllerOutput +from jiuwen.core.agent.agent import Agent +from jiuwen.core.agent.handler.base import AgentHandlerImpl +from jiuwen.agent.state.react_state import ReActState +from jiuwen.core.context.config import Config from jiuwen.core.context.context import Context -from jiuwen.core.graph.executable import Input, Output - - -class TaskManager: - pass - - -class MockAgent: - def __init__(self, agent_config: AgentConfig): - self._config = agent_config - self._controller = self._init_controller() - self._agent_sdk = self._init_agent_sdk() - self._task_manager = self._init_task_manager() - - def _init_controller(self): - return Controller(self._config) - - def _init_agent_sdk(self): - return AgentSdk() - - def _init_task_manager(self): - return TaskManager() +from jiuwen.core.context.memory.base import InMemoryState -class ReActAgent(MockAgent): +class ReActAgent(Agent): def __init__(self, agent_config: ReActAgentConfig): super().__init__(agent_config) self._state = ReActState() def _init_controller(self): - return ReActController(self._config) + return ReActController(self._config, self._controller_context_manager) - def _init_agent_sdk(self): - return AgentSdkImpl() + def _init_agent_handler(self): + return AgentHandlerImpl() - def invoke(self, inputs: Input, context: Context) -> Output: - self._init_state(context) + def _init_controller_context_manager(self) -> ControllerContextMgr: + context = Context(config=Config(), state=InMemoryState(), store=None, tracer=None) + return ControllerContextMgr(self._config, context) + def invoke(self, inputs: Dict) -> Dict: while self._state.current_iteration < self._config.constrain.max_iteration: - controller_output: ReActControllerOutput = self._controller.invoke(inputs, self._context) + controller_output: ReActControllerOutput = self._controller.invoke(inputs) if not controller_output.should_continue: break @@ -58,12 +41,8 @@ class ReActAgent(MockAgent): return dict(output=self._state.final_result) - async def ainvoke(self, inputs: Input, context: Context) -> Output: - return dict() + def stream(self, inputs: Dict) -> Iterator[Any]: + pass def _execute_sub_tasks(self, sub_tasks): pass - - def _init_state(self, context): - self._state = ReActState() - self._context = context diff --git a/jiuwen/controller/mode/__init__.py b/jiuwen/agent/skills/__init__.py similarity index 100% rename from jiuwen/controller/mode/__init__.py rename to jiuwen/agent/skills/__init__.py diff --git a/jiuwen/controller/skills/__init__.py b/jiuwen/agent/state/__init__.py similarity index 100% rename from jiuwen/controller/skills/__init__.py rename to jiuwen/agent/state/__init__.py diff --git a/jiuwen/controller/state/base.py b/jiuwen/agent/state/base.py similarity index 100% rename from jiuwen/controller/state/base.py rename to jiuwen/agent/state/base.py diff --git a/jiuwen/controller/state/react_state.py b/jiuwen/agent/state/react_state.py similarity index 83% rename from jiuwen/controller/state/react_state.py rename to jiuwen/agent/state/react_state.py index 2202043..7737892 100644 --- a/jiuwen/controller/state/react_state.py +++ b/jiuwen/agent/state/react_state.py @@ -6,8 +6,8 @@ from typing import Optional, List from pydantic import BaseModel, Field -from jiuwen.controller.common.sub_task import SubTask -from jiuwen.controller.common.enum import ReActStatus +from jiuwen.agent.common.enum import ReActStatus +from jiuwen.core.agent.task.task import SubTask from jiuwen.core.utils.llm.messages import BaseMessage diff --git a/jiuwen/controller/agent.py b/jiuwen/agent/state/workflow_state.py similarity index 100% rename from jiuwen/controller/agent.py rename to jiuwen/agent/state/workflow_state.py diff --git a/jiuwen/agent/workflow_agent.py b/jiuwen/agent/workflow_agent.py new file mode 100644 index 0000000..8305572 --- /dev/null +++ b/jiuwen/agent/workflow_agent.py @@ -0,0 +1,38 @@ +from typing import Dict + +from jiuwen.agent.config.workflow_config import WorkflowAgentConfig +from jiuwen.agent.context.controller_context_manager import ControllerContextMgr +from jiuwen.agent.controller.workflow_controller import WorkflowController, WorkflowControllerOutput +from jiuwen.core.agent.agent import Agent +from jiuwen.core.agent.handler.base import AgentHandlerImpl +from jiuwen.core.context.config import Config +from jiuwen.core.context.context import Context +from jiuwen.core.context.memory.base import InMemoryState + + +class WorkflowAgent(Agent): + def __init__(self, agent_config: WorkflowAgentConfig): + super().__init__(agent_config) + + def _init_controller(self): + return WorkflowController(self._config, self._controller_context_manager) + + def _init_agent_handler(self): + return AgentHandlerImpl() + + def _init_controller_context_manager(self) -> ControllerContextMgr: + context = Context(config=Config(), state=InMemoryState(), store=None, tracer=None) + return ControllerContextMgr(self._config, context) + + def _init_task_manager(self): + return None + + def invoke(self, inputs: Dict) -> Dict: + output: WorkflowControllerOutput = self._controller.invoke(inputs) + + outputs = [self._agent_handler.invoke(st) for st in output.sub_tasks] + + return {"outputs": outputs} + + def stream(self, inputs: Dict): + pass diff --git a/jiuwen/controller/task/planner/__init__.py b/jiuwen/controller/task/planner/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/jiuwen/core/agent/agent.py b/jiuwen/core/agent/agent.py new file mode 100644 index 0000000..f86301d --- /dev/null +++ b/jiuwen/core/agent/agent.py @@ -0,0 +1,61 @@ +from abc import ABC, abstractmethod +from typing import Any, Iterator, Optional, Dict + +from jiuwen.agent.config.base import AgentConfig +from jiuwen.agent.context.controller_context_manager import ControllerContextMgr + + +class Agent(ABC): + """ + 最顶层抽象,所有 Agent 的公共基类。 + 子类必须实现: + - invoke : 同步一次性调用 + - stream : 流式调用 + """ + + def __init__(self, agent_config: "AgentConfig"): + self._config = agent_config + self._controller_context_manager: Optional["ControllerContextMgr"] = \ + self._init_controller_context_manager() + self._controller: "Controller | None" = self._init_controller() + self._agent_handler: "AgentHandler | None" = self._init_agent_handler() + self._task_manager: "TaskManager | None" = self._init_task_manager() + + @abstractmethod + def invoke(self, inputs: Dict) -> Dict: + """ + 同步调用,一次性返回最终结果 + """ + pass + + @abstractmethod + def stream(self, inputs: Dict) -> Iterator[Any]: + """ + 流式调用,逐个 yield 中间结果 + """ + pass + + def _init_controller(self) -> "Controller | None": + """ + 留给子类按需实例化 Controller;默认返回 None + """ + return None + + def _init_agent_handler(self) -> "AgentHandler | None": + """ + 留给子类按需实例化 AgentHandler;默认返回 None + """ + return None + + def _init_task_manager(self) -> "TaskManager | None": + """ + 留给子类按需实例化 TaskManager;默认返回 None + """ + return None + + def _init_controller_context_manager(self) -> Optional["ControllerContextMgr"]: + """ + 子类返回具体的 ControllerContextMgr 实例即可。 + 默认返回 None,表示无需上下文管理。 + """ + return None diff --git a/jiuwen/controller/state/__init__.py b/jiuwen/core/agent/handler/__init__.py similarity index 100% rename from jiuwen/controller/state/__init__.py rename to jiuwen/core/agent/handler/__init__.py diff --git a/jiuwen/controller/handler/base.py b/jiuwen/core/agent/handler/base.py similarity index 89% rename from jiuwen/controller/handler/base.py rename to jiuwen/core/agent/handler/base.py index 89f9c8b..df0e0c0 100644 --- a/jiuwen/controller/handler/base.py +++ b/jiuwen/core/agent/handler/base.py @@ -4,11 +4,11 @@ """Handler of Agent""" from typing import Dict, Callable -from jiuwen.controller.common.enum import SubTaskType +from jiuwen.agent.common.enum import SubTaskType from jiuwen.core.common.exception.exception import JiuWenBaseException -class AgentSdk: +class AgentHandler: def __init__(self): self._function_map: Dict[SubTaskType, Callable[[dict], dict]] = { SubTaskType.WORKFLOW: self.invoke_workflow, @@ -34,5 +34,5 @@ class AgentSdk: return dict() -class AgentSdkImpl(AgentSdk): +class AgentHandlerImpl(AgentHandler): pass diff --git a/jiuwen/controller/task/dispatcher/__init__.py b/jiuwen/core/agent/task/__init__.py similarity index 100% rename from jiuwen/controller/task/dispatcher/__init__.py rename to jiuwen/core/agent/task/__init__.py diff --git a/jiuwen/core/agent/task/task.py b/jiuwen/core/agent/task/task.py new file mode 100644 index 0000000..ece351d --- /dev/null +++ b/jiuwen/core/agent/task/task.py @@ -0,0 +1,27 @@ +import uuid +from typing import Any, Dict, Optional, List + +from pydantic import BaseModel, Field + +from jiuwen.agent.common.enum import SubTaskType, TaskStatus + + +class SubTask(BaseModel): + id: str = Field(default="") + sub_task_type: SubTaskType = Field(default=SubTaskType.UNDEFINED) + func_name: str = Field(default="") + func_args: dict = Field(default_factory=dict) + + +class Task: + def __init__(self, payload: Dict[str, Any], task_id: Optional[str] = None): + self.id: str = task_id or str(uuid.uuid4()) + self.payload: Dict[str, Any] = payload + self.sub_tasks: List[SubTask] = [] + self.status: TaskStatus = TaskStatus.PENDING + + def add_sub_task(self, sub_task: SubTask) -> str: + if not sub_task.id: # 若调用方没给 id,自动生成 + sub_task.id = f"{self.id}_{len(self.sub_tasks)}" + self.sub_tasks.append(sub_task) + return sub_task.id diff --git a/jiuwen/core/agent/task/task_manager.py b/jiuwen/core/agent/task/task_manager.py new file mode 100644 index 0000000..e7123d7 --- /dev/null +++ b/jiuwen/core/agent/task/task_manager.py @@ -0,0 +1,27 @@ +import threading +from typing import Dict, Any, Optional + +from jiuwen.agent.common.enum import TaskStatus +from jiuwen.core.agent.task.task import Task + + +class TaskManager: + def __init__(self): + self._lock = threading.Lock() + self._tasks: Dict[str, Task] = {} + + def submit(self, payload: Dict[str, Any], task_id: Optional[str] = None) -> str: + with self._lock: + task = Task(payload, task_id) + self._tasks[task.id] = task + return task.id + + def get(self, task_id: str) -> Optional[Task]: + with self._lock: + return self._tasks.get(task_id) + + def update_status(self, task_id: str, status: TaskStatus) -> None: + with self._lock: + t = self._tasks.get(task_id) + if t: + t.status = status diff --git a/jiuwen/controller/task/executor/__init__.py b/tests/unit_tests/agent/__init__.py similarity index 100% rename from jiuwen/controller/task/executor/__init__.py rename to tests/unit_tests/agent/__init__.py diff --git a/tests/unit_tests/agent/test_workflow_agent.py b/tests/unit_tests/agent/test_workflow_agent.py new file mode 100644 index 0000000..16d3b68 --- /dev/null +++ b/tests/unit_tests/agent/test_workflow_agent.py @@ -0,0 +1,33 @@ +import pytest +from unittest.mock import Mock, patch + +from jiuwen.agent.config.workflow_config import WorkflowAgentConfig +from jiuwen.agent.workflow_agent import WorkflowAgent + + +class TestWorkflowAgent: + # 在所有测试方法前一次性打补丁 + @pytest.fixture(autouse=True, scope="class") + def _patch_deps(self): + # 把私有工厂方法替换掉 + with patch.object( + WorkflowAgent, "_init_agent_handler", autospec=True + ) as mock_handler, patch.object( + WorkflowAgent, "_init_controller_context_manager", autospec=True + ): + # 返回统一的 mock handler + handler = Mock() + handler.invoke = Mock(side_effect=lambda st: {"mock": st.func_name}) + mock_handler.return_value = handler + yield + + # 真正实例化 + @pytest.fixture(scope="class") + def agent(self): + return WorkflowAgent(WorkflowAgentConfig()) + + # ---------- 测试用例 ---------- + def test_invoke_single(self, agent): + inputs = {"workflows": [{"name": "echo", "params": {"text": "hi"}}]} + result = agent.invoke(inputs) + assert result == {"outputs": []} diff --git a/tests/unit_tests/workflow/test_llm_comp.py b/tests/unit_tests/workflow/test_llm_comp.py index 34e2121..e2b92c7 100644 --- a/tests/unit_tests/workflow/test_llm_comp.py +++ b/tests/unit_tests/workflow/test_llm_comp.py @@ -129,7 +129,11 @@ class TestLLMExecutableInvoke: # 2. 构造工作流 flow = create_flow() - flow.set_start_comp("start", MockStartNode("start")) + flow.set_start_comp("start", MockStartNode("start"), + inputs_schema={ + "a": "${user.inputs.a}", + "b": "${user.inputs.b}"}) + flow.set_end_comp( "end", MockEndNode("end"), @@ -143,7 +147,8 @@ class TestLLMExecutableInvoke: output_config={"result": {"type": "string", "required": True}}, ) llm_comp = LLMComponent(config) - flow.add_workflow_comp("llm", llm_comp) + flow.add_workflow_comp("llm", llm_comp, + inputs_schema={"a": "${start.a}"}) flow.add_connection("start", "llm") flow.add_connection("llm", "end") -- Gitee