diff --git a/src/agents/chatbot/configuration.py b/src/agents/chatbot/configuration.py index 161f3358..af94792e 100644 --- a/src/agents/chatbot/configuration.py +++ b/src/agents/chatbot/configuration.py @@ -1,8 +1,15 @@ from dataclasses import dataclass, field from datetime import datetime, timezone +from langchain_community.tools.tavily_search import TavilySearchResults from src.agents.registry import Configuration +from src.agents.tools_factory import multiply, add, subtract, divide + + + +def get_default_tools(): + return ["TavilySearchResults", "multiply", "add", "subtract", "divide"] @dataclass(kw_only=True) class ChatbotConfiguration(Configuration): @@ -24,3 +31,32 @@ class ChatbotConfiguration(Configuration): }, ) + tools: list = field( + default_factory=get_default_tools, + metadata={ + "description": "The tools to use for the agent's interactions. " + "Should be in the form: provider/model-name." + }, + ) + + temperature: float = field( + default=0.7, + metadata={ + "description": "控制模型生成结果的随机性,值越大随机性越高,建议范围 0.0-1.0" + }, + ) + + use_tools: bool = field( + default=True, + metadata={ + "description": "是否启用工具调用功能" + }, + ) + + max_iterations: int = field( + default=10, + metadata={ + "description": "智能体最大执行步数,防止无限循环" + }, + ) + diff --git a/src/agents/chatbot/graph.py b/src/agents/chatbot/graph.py index 0625cdcf..e2df88fe 100644 --- a/src/agents/chatbot/graph.py +++ b/src/agents/chatbot/graph.py @@ -6,14 +6,13 @@ 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 langgraph.checkpoint.memory import MemorySaver # 实际上没有起作用 from src.agents.registry import State, BaseAgent from src.agents.utils import load_chat_model -from src.agents.tools_factory import multiply, add, subtract, divide from src.agents.chatbot.configuration import ChatbotConfiguration +from src.agents.tools_factory import _TOOLS_REGISTRY class ChatbotAgent(BaseAgent): name = "chatbot" @@ -27,14 +26,15 @@ class ChatbotAgent(BaseAgent): def _get_tools(self, config_schema: RunnableConfig): """根据配置获取工具""" - tools = [multiply, add, subtract, divide, TavilySearchResults(max_results=10)] - return tools + default_tools_names = config_schema.get("tools", []) + default_tools = [_TOOLS_REGISTRY[tool] for tool in default_tools_names] + return default_tools def llm_call(self, state: State, config: RunnableConfig = None) -> dict[str, Any]: """调用 llm 模型""" config_schema = config or {} conf = self.config_schema.from_runnable_config(config_schema) - model = load_chat_model(conf.model) + model = load_chat_model(conf.model, temperature=conf.temperature) model_with_tools = model.bind_tools(self._get_tools(config_schema)) res = model_with_tools.invoke( diff --git a/src/agents/registry.py b/src/agents/registry.py index 0da6b8bb..7d3c89bf 100644 --- a/src/agents/registry.py +++ b/src/agents/registry.py @@ -42,7 +42,9 @@ class Configuration(SimpleConfig): @classmethod def to_dict(cls): - return {f.name: getattr(cls, f.name) for f in fields(cls) if f.init} + # 创建一个实例来处理 default_factory + instance = cls() + return {f.name: getattr(instance, f.name) for f in fields(cls) if f.init} @@ -84,12 +86,13 @@ class BaseAgent(): def stream_messages(self, messages: list[str], config_schema: RunnableConfig = None, **kwargs): graph = self.get_graph(config_schema=config_schema, **kwargs) conf = self.config_schema.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 + for msg, metadata in graph.stream({"messages": messages}, stream_mode="messages", config=config_schema): + # msg_type = msg.type + # return_keys = conf.get("return_keys", []) + # if not return_keys or msg_type in return_keys: + # yield msg, metadata + yield msg, metadata @abstractmethod def get_graph(self, **kwargs) -> CompiledStateGraph: diff --git a/src/agents/tools_factory.py b/src/agents/tools_factory.py index 3dd0eee8..61369af4 100644 --- a/src/agents/tools_factory.py +++ b/src/agents/tools_factory.py @@ -6,9 +6,7 @@ from typing import Any, Callable, Optional, Type, Union from pydantic import BaseModel, Field from langchain_core.tools import tool, BaseTool - - -_TOOLS_REGISTRY = {} +from langchain_community.tools.tavily_search import TavilySearchResults # refs https://github.com/chatchat-space/LangGraph-Chatchat chatchat-server/chatchat/server/agent/tools_factory/tools_registry.py def regist_tool( @@ -115,3 +113,12 @@ def subtract(first_int: int, second_int: int) -> int: def divide(first_int: int, second_int: int) -> int: """Divide two integers.""" return first_int / second_int + + +_TOOLS_REGISTRY = { + "multiply": multiply, + "add": add, + "subtract": subtract, + "divide": divide, + "TavilySearchResults": TavilySearchResults(max_results=10), +} diff --git a/src/agents/utils.py b/src/agents/utils.py index 9da3ee33..b9401860 100644 --- a/src/agents/utils.py +++ b/src/agents/utils.py @@ -7,14 +7,23 @@ from langchain_core.messages import AIMessageChunk, ToolMessage -def load_chat_model(fully_specified_name: str) -> BaseChatModel: +def load_chat_model(fully_specified_name: str, **kwargs) -> BaseChatModel: """Load a chat model from a fully specified name. Args: fully_specified_name (str): String in the format 'provider/model'. + **kwargs: Additional parameters to pass to the model. """ provider, model = fully_specified_name.split("/", maxsplit=1) - return select_model(model_name=model, model_provider=provider).chat_open_ai + model_instance = select_model(model_name=model, model_provider=provider) + + # 配置额外参数,如temperature + if kwargs and hasattr(model_instance, 'chat_open_ai'): + for key, value in kwargs.items(): + if value is not None: + setattr(model_instance.chat_open_ai, key, value) + + return model_instance.chat_open_ai def agent_cli(agent: BaseAgent, config: RunnableConfig = None): diff --git a/src/models/chat_model.py b/src/models/chat_model.py index f71289e5..065d9e36 100644 --- a/src/models/chat_model.py +++ b/src/models/chat_model.py @@ -11,7 +11,8 @@ class OpenAIBase(): self.model_name = model_name self.chat_open_ai = ChatOpenAI(model=model_name, api_key=api_key, - base_url=base_url) + base_url=base_url, + temperature=0.7) def predict(self, message, stream=False): if isinstance(message, str): diff --git a/src/routers/chat_router.py b/src/routers/chat_router.py index e02d49b8..3c7c6d25 100644 --- a/src/routers/chat_router.py +++ b/src/routers/chat_router.py @@ -130,17 +130,21 @@ async def get_agent(): @chat.post("/agent/{agent_name}") def chat_agent(agent_name: str, query: str = Body(...), - meta: dict = Body({}), history: list = Body(...), - thread_id: str | None = Body(None)): - - meta["server_model_name"] = agent_name + config: dict = Body({})): + # 将meta和thread_id整合到config中 def make_chunk(content=None, **kwargs): + chat_metadata = { + "agent_name": agent_name, + "thread_id": config.get("thread_id"), + } + if update_metadata := kwargs.get("chat_metadata"): + chat_metadata.update(update_metadata) + return json.dumps({ "response": content, - "model_name": agent_name, - "meta": meta, + "chat_metadata": chat_metadata, **kwargs }, ensure_ascii=False).encode('utf-8') + b"\n" @@ -150,22 +154,27 @@ def chat_agent(agent_name: str, logger.error(f"Error getting agent {agent_name}: {e}") return StreamingResponse(make_chunk(message=f"Error getting agent {agent_name}: {e}", status="error"), media_type='application/json') + # 从config中获取history_round + history_round = config.get("history_round") history_manager = HistoryManager(history) - messages = history_manager.get_history_with_msg(query, max_rounds=meta.get('history_round')) - history_manager.add_user(query) # 注意这里使用原始查询 + messages = history_manager.get_history_with_msg(query, max_rounds=history_round) + history_manager.add_user(query) + # 如果没有thread_id则生成一个 + if "thread_id" not in config or not config["thread_id"]: + config["thread_id"] = str(uuid.uuid4()) + + # 构造运行时配置 runnable_config = { "configurable": { - "thread_id": thread_id or str(uuid.uuid4()), - "return_keys": [] + **config } } def stream_messages(): content = "" - yield make_chunk(status="waiting") - for msg, metadata in agent.stream_messages(messages, runnable_config): - # logger.debug(f">>>>> msg: {msg.model_dump()}, >>>>>>> {metadata=}") + yield make_chunk(status="init") + for msg, metadata in agent.stream_messages(messages, config_schema=runnable_config): if isinstance(msg, AIMessageChunk) and msg.content != "": content += msg.content yield make_chunk(content=msg.content, diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index 9a5c5e1a..5db16ca7 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -8,28 +8,16 @@ 新对话 +
+ +
- -
@@ -38,7 +26,7 @@

{{ currentAgent ? currentAgent.description : '不同的智能体有不同的专长和能力' }}

-
+
-
{{ message }}
+
{{ message }}