diff --git a/src/agents/common/toolagent.py b/src/agents/common/toolagent.py deleted file mode 100644 index de3ec218..00000000 --- a/src/agents/common/toolagent.py +++ /dev/null @@ -1,86 +0,0 @@ -from abc import abstractmethod -from typing import Any, cast - -from langchain.messages import AIMessage, ToolMessage -from langgraph.prebuilt import ToolNode -from langgraph.runtime import Runtime - -from src.agents.common.base import BaseAgent -from src.agents.common.mcp import get_mcp_tools -from src.agents.common.models import load_chat_model -from src.utils import logger - -from .context import BaseContext -from .state import BaseState - - -class ToolAgent(BaseAgent): - name = "ToolAgent" - description = "具有工具调用能力的Agent" - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.graph = None - self.checkpointer = None - self.context_schema = BaseContext - self.agent_tools = None - - # TODO:[修改建议] _get_invoke_tools,llm_call,dynamic_tools_node这类针对工具调用的功能大多数Agent都能用得到 - # 可以通过一个ToolAgent类继承BaseAgent,通过重写抽象方法获取tools,通过继承BaseState和BaseContext获取配置 - # 必要时可通过重写以下方法实现其他逻辑 - @abstractmethod - def get_tools(self): - logger.error(f"get_tools() is not implemented in {self.__class__.__name__}") - return [] - - async def _get_invoke_tools(self, selected_tools: list[str], selected_mcps: list[str]): - """根据配置获取工具。 - 默认不使用任何工具。 - 如果配置为列表,则使用列表中的工具。 - """ - enabled_tools = [] - self.agent_tools = self.agent_tools or self.get_tools() - if selected_tools and isinstance(selected_tools, list) and len(selected_tools) > 0: - # 使用配置中指定的工具 - enabled_tools = [tool for tool in self.agent_tools if tool.name in selected_tools] - - if selected_mcps and isinstance(selected_mcps, list) and len(selected_mcps) > 0: - for mcp in selected_mcps: - enabled_tools.extend(await get_mcp_tools(mcp)) - - return enabled_tools - - async def llm_call(self, state: BaseState, runtime: Runtime[BaseContext] = None) -> dict[str, Any]: - """调用 llm 模型 - 异步版本以支持异步工具""" - model = load_chat_model(runtime.context.model) - - # 这里要根据配置动态获取工具 - available_tools = await self._get_invoke_tools(runtime.context.tools, runtime.context.mcps) - logger.info(f"LLM binded ({len(available_tools)}) available_tools: {[tool.name for tool in available_tools]}") - - if available_tools: - model = model.bind_tools(available_tools) - - # 使用异步调用 - response = cast( - AIMessage, - await model.ainvoke([{"role": "system", "content": runtime.context.system_prompt}, *state.messages]), - ) - return {"messages": [response]} - - async def dynamic_tools_node(self, state: BaseState, runtime: Runtime[BaseContext]) -> dict[str, list[ToolMessage]]: - """Execute tools dynamically based on configuration. - - This function gets the available tools based on the current configuration - and executes the requested tool calls from the last message. - """ - # Get available tools based on configuration - available_tools = await self._get_invoke_tools(runtime.context.tools, runtime.context.mcps) - - # Create a ToolNode with the available tools - tool_node = ToolNode(available_tools) - - # Execute the tool node - result = await tool_node.ainvoke(state) - - return cast(dict[str, list[ToolMessage]], result) diff --git a/src/agents/multi_agent/__init__.py b/src/agents/multi_agent/__init__.py deleted file mode 100644 index 740c89a0..00000000 --- a/src/agents/multi_agent/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .graph import SampleMultiAgent - -__all__ = ["SampleMultiAgent"] diff --git a/src/agents/multi_agent/context.py b/src/agents/multi_agent/context.py deleted file mode 100644 index 7bb1adad..00000000 --- a/src/agents/multi_agent/context.py +++ /dev/null @@ -1,25 +0,0 @@ -from dataclasses import dataclass, field -from typing import Annotated - -from src.agents.common.context import BaseContext -from src.agents.common.mcp import MCP_SERVERS -from src.agents.common.tools import gen_tool_info - -from .tools import get_tools - - -@dataclass(kw_only=True) -class Context(BaseContext): - tools: Annotated[list[dict], {"__template_metadata__": {"kind": "tools"}}] = field( - default_factory=list, - metadata={ - "name": "工具", - "options": gen_tool_info(get_tools()), # 这里的选择是所有的工具 - "description": "工具列表", - }, - ) - - mcps: list[str] = field( - default_factory=list, - metadata={"name": "MCP服务器", "options": list(MCP_SERVERS.keys()), "description": "MCP服务器列表"}, - ) diff --git a/src/agents/multi_agent/graph.py b/src/agents/multi_agent/graph.py deleted file mode 100644 index 8db3db0c..00000000 --- a/src/agents/multi_agent/graph.py +++ /dev/null @@ -1,61 +0,0 @@ -from langgraph.graph import END, START, StateGraph -from langgraph.prebuilt import tools_condition - -from src.agents.common.toolagent import ToolAgent - -from .context import Context -from .state import State -from .tools import get_tools - - -class SampleMultiAgent(ToolAgent): - name = "MultiAgent智能体" - description = "Supervisor智能体,具有调用其他子智能体的能力(在工具中添加)" - - # TODO[已完成]: 通过将其他agent封装为工具的方式添加了多智能体调度 - """ - 你是一个多智能体核心,通过多智能体调用的方式帮助用户完成一系列任务: - - 1.当你需要知识库问答功能时,请调用对话聊天智能体实现 - 2.当你需要加密计算的时候,请调用加密计算智能体实现 - """ - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.graph = None - self.checkpointer = None - self.context_schema = Context - self.agent_tools = None - - def get_tools(self): - return get_tools() - - async def get_graph(self, **kwargs): - """构建图""" - if self.graph: - return self.graph - - builder = StateGraph(State, context_schema=self.context_schema) - builder.add_node("chatbot", self.llm_call) - builder.add_node("tools", self.dynamic_tools_node) - builder.add_edge(START, "chatbot") - builder.add_conditional_edges( - "chatbot", - tools_condition, - ) - builder.add_edge("tools", "chatbot") - builder.add_edge("chatbot", END) - - self.checkpointer = await self._get_checkpointer() - graph = builder.compile(checkpointer=self.checkpointer, name=self.name) - self.graph = graph - return graph - - -def main(): - pass - - -if __name__ == "__main__": - main() - # asyncio.run(main()) diff --git a/src/agents/multi_agent/state.py b/src/agents/multi_agent/state.py deleted file mode 100644 index 927bfa95..00000000 --- a/src/agents/multi_agent/state.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Define the state structures for the agent.""" - -from __future__ import annotations - -from collections.abc import Sequence -from dataclasses import dataclass, field -from typing import Annotated - -from langchain.messages import AnyMessage -from langgraph.graph import add_messages - -from src.agents.common.state import BaseState - - -@dataclass -class State(BaseState): - """Defines the input state for the agent, representing a narrower interface to the outside world. - - This class is used to define the initial state and structure of incoming data. - """ - - messages: Annotated[Sequence[AnyMessage], add_messages] = field(default_factory=list) diff --git a/src/agents/multi_agent/tools.py b/src/agents/multi_agent/tools.py deleted file mode 100644 index fc3fe951..00000000 --- a/src/agents/multi_agent/tools.py +++ /dev/null @@ -1,76 +0,0 @@ -from typing import Any - -from langchain.tools import tool -from langchain_core.runnables import RunnableConfig - -from src.agents import agent_manager -from src.agents.common.tools import get_buildin_tools -from src.utils import logger - - -# TODO[修改建议]:能不能通过前端直接指定子智能体? -# 调用子智能体后的日志是输出到tool_calls的 -@tool(name_or_callable="对话聊天智能体", description="调用指定智能体进行对话聊天的功能") -async def call_chatbot(query: str, config: RunnableConfig) -> str: - """ - 调用指定chatbot智能体进行对话聊天的功能 - - Args: - query: 根据需要构造的提问 - config: LangGraph运行时配置(自动注入) - Returns: - str: 最终的回答结果 - """ - try: - input = [{"role": "user", "content": query}] - chatbot = agent_manager.get_agent("ChatbotAgent") - configurable = config.get("configurable", {}) - input_context = { - "thread_id": configurable.get("thread_id"), - "user_id": configurable.get("user_id"), - } - message = await chatbot.invoke_messages(input, input_context=input_context) - # 直接获取最后一个消息的内容 - final_answer = message.get("messages", [])[-1].content - logger.info(f"ChatbotAgent: {final_answer}") - return final_answer - except Exception as e: - logger.error(f"CallAgent error: {e}") - raise - - -@tool(name_or_callable="加密计算智能体", description="调用指定智能体进行加密计算的功能") -async def call_react_agent(query: str, config: RunnableConfig) -> str: - """ - 调用指定智能体进行加密计算的功能 - - Args: - query: 根据需要构造的提问 - config: LangGraph运行时配置(自动注入) - Returns: - str: 最终的回答结果 - """ - try: - input = [{"role": "user", "content": query}] - chatbot = agent_manager.get_agent("ReActAgent") - configurable = config.get("configurable", {}) - input_context = { - "thread_id": configurable.get("thread_id"), - "user_id": configurable.get("user_id"), - } - message = await chatbot.invoke_messages(input, input_context=input_context) - # 直接获取最后一个消息的内容 - final_answer = message.get("messages", [])[-1].content - logger.info(f"ReActAgent: {final_answer}") - return final_answer - except Exception as e: - logger.error(f"CallAgent error: {e}") - raise - - -def get_tools() -> list[Any]: - """获取所有可运行的工具(给大模型使用)""" - tools = get_buildin_tools() - tools.append(call_chatbot) - tools.append(call_react_agent) - return tools diff --git a/src/agents/react/__init__.py b/src/agents/react/__init__.py deleted file mode 100644 index eefad6b3..00000000 --- a/src/agents/react/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .graph import ReActAgent - -__all__ = ["ReActAgent"] diff --git a/src/agents/react/context.py b/src/agents/react/context.py deleted file mode 100644 index 7bb1adad..00000000 --- a/src/agents/react/context.py +++ /dev/null @@ -1,25 +0,0 @@ -from dataclasses import dataclass, field -from typing import Annotated - -from src.agents.common.context import BaseContext -from src.agents.common.mcp import MCP_SERVERS -from src.agents.common.tools import gen_tool_info - -from .tools import get_tools - - -@dataclass(kw_only=True) -class Context(BaseContext): - tools: Annotated[list[dict], {"__template_metadata__": {"kind": "tools"}}] = field( - default_factory=list, - metadata={ - "name": "工具", - "options": gen_tool_info(get_tools()), # 这里的选择是所有的工具 - "description": "工具列表", - }, - ) - - mcps: list[str] = field( - default_factory=list, - metadata={"name": "MCP服务器", "options": list(MCP_SERVERS.keys()), "description": "MCP服务器列表"}, - ) diff --git a/src/agents/react/graph.py b/src/agents/react/graph.py deleted file mode 100644 index 50fad7a9..00000000 --- a/src/agents/react/graph.py +++ /dev/null @@ -1,78 +0,0 @@ -from langgraph.constants import END -from langgraph.graph import StateGraph - -from src.agents.common.toolagent import ToolAgent - -from .context import Context -from .state import State -from .tools import get_tools - - -def tools_branch_continue(state: State): - messages = state.messages - last_message = messages[-1] - # 如果无工具调用则结束 - if not last_message.tool_calls: - return "end" - else: - return "continue" - - -class ReActAgent(ToolAgent): - name = "ReActAgent" - description = "符合 ReAct 范式的智能体,可以通过调用工具来完成复杂任务。" - - """ - 提示词示例: - 你是一个智能体助手 - - 你的所有需要计算的工作不能自己计算,只能通过调用加密计算机工具进行,你只能通过调用该工具的方式进行计算才能得到正确答案。计算顺序是正常的。 - - 例如 2+3+2,你必须调用两次加密计算机工具进行运算,最终结果为9。 - """ - - # TODO:[已完成] React智能体 - ''' - 提示词示例: - 你是一个智能体助手 - - 你的所有需要计算的工作不能自己计算,只能通过调用加密计算机工具进行,你只能通过调用该工具的方式进行计算才能得到正确答案。计算顺序是正常的。 - - 例如 2+3+2,你必须调用两次加密计算机工具进行运算,最终结果为9。 - ''' - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.graph = None - self.checkpointer = None - self.context_schema = Context - self.agent_tools = None - - def get_tools(self): - return get_tools() - - async def get_graph(self, **kwargs): - # 创建 ReActAgent - """构建图""" - if self.graph: - return self.graph - - builder = StateGraph(State, context_schema=self.context_schema) - builder.add_node("agent", self.llm_call) - builder.add_node("tools", self.dynamic_tools_node) - builder.set_entry_point("agent") - # 添加条件边:agent 决定是否调用工具继续还是结束对话 - builder.add_conditional_edges( - "agent", - tools_branch_continue, - { - "continue": "tools", # 调用工具 - "end": END, # 结束对话 - }, - ) - builder.add_edge("tools", "agent") - self.checkpointer = await self._get_checkpointer() - graph = builder.compile(checkpointer=self.checkpointer, name=self.name) - self.graph = graph - return graph - diff --git a/src/agents/react/state.py b/src/agents/react/state.py deleted file mode 100644 index 927bfa95..00000000 --- a/src/agents/react/state.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Define the state structures for the agent.""" - -from __future__ import annotations - -from collections.abc import Sequence -from dataclasses import dataclass, field -from typing import Annotated - -from langchain.messages import AnyMessage -from langgraph.graph import add_messages - -from src.agents.common.state import BaseState - - -@dataclass -class State(BaseState): - """Defines the input state for the agent, representing a narrower interface to the outside world. - - This class is used to define the initial state and structure of incoming data. - """ - - messages: Annotated[Sequence[AnyMessage], add_messages] = field(default_factory=list) diff --git a/src/agents/react/tools.py b/src/agents/react/tools.py deleted file mode 100644 index 42529bdf..00000000 --- a/src/agents/react/tools.py +++ /dev/null @@ -1,46 +0,0 @@ -from typing import Any - -from langchain.tools import tool - -from src.agents.common.toolkits.mysql import get_mysql_tools -from src.agents.common.tools import get_buildin_tools -from src.utils import logger - - -@tool(name_or_callable="加密计算器", description="可以对给定的2个数字选择进行加减乘除四种加密计算") -def calculator(a: float, b: float, operation: str) -> float: - """ - 可以对给定的2个数字选择进行加减乘除四种加密计算 - - Args: - a: 第一个数字 - b: 第二个数字 - operation: 计算操作符号,可以是add,subtract,multiply,divide - - Returns: - float: 最终的计算结果 - """ - try: - if operation == "add": - return a + b + 1 - elif operation == "subtract": - return a - b - 1 - elif operation == "multiply": - return a * b * 2 - elif operation == "divide": - if b == 0: - raise ZeroDivisionError("除数不能为零") - return a / b - 1 - else: - raise ValueError(f"不支持的运算类型: {operation},仅支持 add, subtract, multiply, divide") - except Exception as e: - logger.error(f"Calculator error: {e}") - raise - - -def get_tools() -> list[Any]: - """获取所有可运行的工具(给大模型使用)""" - tools = get_buildin_tools() - tools.append(calculator) - tools.extend(get_mysql_tools()) - return tools