多智能体调试完成

This commit is contained in:
Wenjie Zhang 2025-03-29 19:13:56 +08:00
parent 3f79018373
commit 27e44f26c8
5 changed files with 143 additions and 2 deletions

View File

@ -1,4 +1,5 @@
from src.agents.chatbot import ChatbotAgent, ChatbotConfiguration from src.agents.chatbot import ChatbotAgent
from src.agents.react import ReActAgent
class AgentManager: class AgentManager:
def __init__(self): def __init__(self):
@ -17,6 +18,7 @@ class AgentManager:
agent_manager = AgentManager() agent_manager = AgentManager()
agent_manager.add_agent("chatbot", ChatbotAgent) agent_manager.add_agent("chatbot", ChatbotAgent)
agent_manager.add_agent("react", ReActAgent)
__all__ = ["agent_manager"] __all__ = ["agent_manager"]

View File

@ -0,0 +1,4 @@
from .graph import ReActAgent
from .configuration import ReActConfiguration
__all__ = ["ReActAgent", "ReActConfiguration"]

View File

@ -0,0 +1,36 @@
from dataclasses import dataclass, field
from datetime import datetime, timezone
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from src.agents.registry import Configuration
def get_default_requirements():
return ["TAVILY_API_KEY"]
@tool
def multiply(first_int: int, second_int: int) -> int:
"""Multiply two integers together."""
return first_int * second_int
@dataclass(kw_only=True)
class ReActConfiguration(Configuration):
"""ReAct 的配置"""
system_prompt: str = field(
default=f"You are a helpful assistant. Now is {datetime.now(tz=timezone.utc).isoformat()}",
metadata={
"description": "The system prompt to use for the agent's interactions. "
"This prompt sets the context and behavior for the agent."
},
)
model: str = field(
default="zhipu/glm-4-plus",
metadata={
"description": "The name of the language model to use for the agent's main interactions. "
"Should be in the form: provider/model-name."
},
)

86
src/agents/react/graph.py Normal file
View File

@ -0,0 +1,86 @@
import asyncio
import uuid
from typing import Any
from datetime import datetime
from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
from langchain_community.tools.tavily_search import TavilySearchResults
from src.agents.registry import State, BaseAgent
from src.agents.utils import load_chat_model
from src.agents.react.configuration import ReActConfiguration, multiply
class ReActAgent(BaseAgent):
name = "react"
description = "A react agent that can answer questions and help with tasks."
_graph_cache = None
config_schema = ReActConfiguration.to_dict()
def __init__(self, **kwargs):
super().__init__(**kwargs)
def _get_tools(self, config_schema: RunnableConfig):
"""根据配置获取工具"""
tools = [multiply, TavilySearchResults(max_results=10)]
return tools
def llm_call(self, state: State, config: RunnableConfig = None) -> dict[str, Any]:
"""调用 llm 模型"""
config_schema = config or {}
conf = ReActConfiguration.from_runnable_config(config_schema)
model = load_chat_model(conf.model)
model_with_tools = model.bind_tools(self._get_tools(config_schema))
res = model_with_tools.invoke(
[{"role": "system", "content": conf.system_prompt}, *state["messages"]]
)
return {"messages": [res]}
def get_graph(self, config_schema: RunnableConfig = None):
"""构建图"""
workflow = StateGraph(State, config_schema=ReActConfiguration)
workflow.add_node("react", self.llm_call)
workflow.add_node("tools", ToolNode(tools=self._get_tools(config_schema)))
workflow.add_edge(START, "react")
workflow.add_conditional_edges(
"react",
tools_condition,
)
workflow.add_edge("tools", "react")
workflow.add_edge("react", END)
graph = workflow.compile(checkpointer=MemorySaver())
return graph
def stream_values(self, messages: list[str], config_schema: RunnableConfig = None):
graph = self.get_graph(config_schema)
for event in graph.stream({"messages": messages}, stream_mode="values", config=config_schema):
yield event["messages"]
def stream_messages(self, messages: list[str], config_schema: RunnableConfig = None):
graph = self.get_graph(config_schema)
conf = ReActConfiguration.from_runnable_config(config_schema)
for msg, metadata in graph.stream({"messages": messages}, stream_mode="messages", config=config_schema):
msg_type = msg.type
return_keys =conf.return_keys
if not return_keys or msg_type in return_keys:
yield msg, metadata
def main():
agent = ReActAgent(ReActConfiguration())
thread_id = str(uuid.uuid4())
config = {"configurable": {"thread_id": thread_id}}
from src.agents.utils import agent_cli
agent_cli(agent, config)
if __name__ == "__main__":
main()
# asyncio.run(main())

View File

@ -3,7 +3,7 @@ from fastapi import APIRouter, Body
from pydantic import BaseModel from pydantic import BaseModel
from typing import List, Dict, Any, Optional from typing import List, Dict, Any, Optional
from src.utils import logger from src.agents import agent_manager
tool = APIRouter(prefix="/tool") tool = APIRouter(prefix="/tool")
@ -15,6 +15,7 @@ class Tool(BaseModel):
url: str url: str
method: Optional[str] = "POST" method: Optional[str] = "POST"
params: Optional[Dict[str, Any]] = None params: Optional[Dict[str, Any]] = None
metadata: Optional[Dict[str, Any]] = None
@tool.get("/", response_model=List[Tool]) @tool.get("/", response_model=List[Tool])
async def route_index(): async def route_index():
@ -41,6 +42,18 @@ async def route_index():
) )
] ]
for agent in agent_manager.agents.values():
tools.append(
Tool(
name=agent.name,
title=agent.name,
description=agent.description,
url=f"/agent/{agent.name}",
method="POST",
metadata=agent.config_schema,
)
)
return tools return tools
@tool.post("/text-chunking") @tool.post("/text-chunking")