diff --git a/configs/__init__.py b/configs/__init__.py index 2a38f203..a4bf7665 100644 --- a/configs/__init__.py +++ b/configs/__init__.py @@ -5,4 +5,4 @@ from .server_config import * from .prompt_config import * -VERSION = "v0.2.7-preview" +VERSION = "v0.2.8-preview" diff --git a/configs/model_config.py.example b/configs/model_config.py.example index 59a070fe..7a7a20f5 100644 --- a/configs/model_config.py.example +++ b/configs/model_config.py.example @@ -259,15 +259,12 @@ VLLM_MODEL_DICT = { } # 你认为支持Agent能力的模型,可以在这里添加,添加后不会出现可视化界面的警告 +# 经过我们测试,原生支持Agent的模型仅有以下几个 SUPPORT_AGENT_MODEL = [ "azure-api", "openai-api", - "claude-api", - "zhipu-api", "qwen-api", "Qwen", - "baichuan-api", - "agentlm", "chatglm3", "xinghuo-api", ] diff --git a/configs/prompt_config.py.example b/configs/prompt_config.py.example index d6457258..6a8a0868 100644 --- a/configs/prompt_config.py.example +++ b/configs/prompt_config.py.example @@ -100,59 +100,13 @@ PROMPT_TEMPLATES = { Question: {input} Thought: {agent_scratchpad} """, - - "AgentLM": - """ - >\n - You are a helpful, respectful and honest assistant. - >\n - Answer the following questions as best you can. If it is in order, you can use some tools appropriately.You have access to the following tools: - - {tools}. - - Use the following steps and think step by step!: - Question: the input question you must answer1 - Thought: you should always think about what to do and what tools to use. - Action: the action to take, should be one of [{tool_names}] - Action Input: the input to the action - Observation: the result of the action - ... (this Thought/Action/Action Input/Observation can be repeated zero or more times) - Thought: I now know the final answer - Final Answer: the final answer to the original input question - - Begin! let's think step by step! + # ChatGLM3必须用官方的提示词,没有修改空间,参数都不会传入进去 + "ChatGLM3": + """ history: {history} Question: {input} Thought: {agent_scratchpad} - - """, - - "中文版本": - """ - 你的知识不一定正确,所以你一定要用提供的工具来思考,并给出用户答案。 - 你有以下工具可以使用: - {tools} - - 请请严格按照提供的思维方式来思考,所有的关键词都要输出,例如Action,Action Input,Observation等 - ``` - Question: 用户的提问或者观察到的信息, - Thought: 你应该思考该做什么,是根据工具的结果来回答问题,还是决定使用什么工具。 - Action: 需要使用的工具,应该是在[{tool_names}]中的一个。 - Action Input: 传入工具的内容 - Observation: 工具给出的答案(不是你生成的) - ... (this Thought/Action/Action Input/Observation can be repeated zero or more times) - Thought: 通过工具给出的答案,你是否能回答Question。 - Final Answer是你的答案 - - 现在,我们开始! - 你和用户的历史记录: - History: - {history} - - 用户开始以提问: - Question: {input} - Thought: {agent_scratchpad} """, }, } diff --git a/requirements.txt b/requirements.txt index 787c7ea8..ce420135 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # API requirements -langchain>=0.0.329 # 推荐使用最新的Langchain +langchain>=0.0.334 # 推荐使用最新的Langchain langchain-experimental>=0.0.30 fschat[model_worker]==0.2.32 xformers>=0.0.22.post4 @@ -53,7 +53,7 @@ vllm>=0.2.0; sys_platform == "linux" # WebUI requirements -streamlit~=1.27.0 +streamlit~=1.28.1 streamlit-option-menu>=0.3.6 streamlit-antd-components>=0.1.11 streamlit-chatbox>=1.1.11 diff --git a/requirements_api.txt b/requirements_api.txt index 7df5fb3a..328a0e5e 100644 --- a/requirements_api.txt +++ b/requirements_api.txt @@ -1,6 +1,6 @@ # API requirements -langchain>=0.0.329 # 推荐使用最新的Langchain +langchain>=0.0.334 # 推荐使用最新的Langchain langchain-experimental>=0.0.30 fschat[model_worker]==0.2.32 xformers>=0.0.22.post4 diff --git a/requirements_lite.txt b/requirements_lite.txt index c3532f2e..8afe6272 100644 --- a/requirements_lite.txt +++ b/requirements_lite.txt @@ -1,4 +1,4 @@ -langchain>=0.0.329 # 推荐使用最新的Langchain +langchain>=0.0.334 # 推荐使用最新的Langchain fschat>=0.2.32 openai # sentence_transformers @@ -41,7 +41,7 @@ dashscope>=1.10.0 # qwen numpy~=1.24.4 pandas~=2.0.3 -streamlit~=1.27.0 +streamlit~=1.28.1 streamlit-option-menu>=0.3.6 streamlit-antd-components>=0.1.11 streamlit-chatbox==1.1.11 diff --git a/requirements_webui.txt b/requirements_webui.txt index 65ff7526..39605bba 100644 --- a/requirements_webui.txt +++ b/requirements_webui.txt @@ -1,6 +1,6 @@ # WebUI requirements -streamlit~=1.27.0 +streamlit~=1.28.1 streamlit-option-menu>=0.3.6 streamlit-antd-components>=0.1.11 streamlit-chatbox>=1.1.11 diff --git a/server/agent/callbacks.py b/server/agent/callbacks.py index 49ce9730..0935f9dc 100644 --- a/server/agent/callbacks.py +++ b/server/agent/callbacks.py @@ -73,21 +73,40 @@ class CustomAsyncIteratorCallbackHandler(AsyncIteratorCallbackHandler): ) self.queue.put_nowait(dumps(self.cur_tool)) + # async def on_llm_new_token(self, token: str, **kwargs: Any) -> None: + # if "Action" in token: ## 减少重复输出 + # before_action = token.split("Action")[0] + # self.cur_tool.update( + # status=Status.running, + # llm_token=before_action + "\n", + # ) + # self.queue.put_nowait(dumps(self.cur_tool)) + # + # self.out = False + # + # if token and self.out: + # self.cur_tool.update( + # status=Status.running, + # llm_token=token, + # ) + # self.queue.put_nowait(dumps(self.cur_tool)) async def on_llm_new_token(self, token: str, **kwargs: Any) -> None: - if "Action" in token: ## 减少重复输出 - before_action = token.split("Action")[0] - self.cur_tool.update( - status=Status.running, - llm_token=before_action + "\n", - ) - self.queue.put_nowait(dumps(self.cur_tool)) - - self.out = False + special_tokens = ["Action", "<|observation|>"] + for stoken in special_tokens: + if stoken in token: + before_action = token.split(stoken)[0] + self.cur_tool.update( + status=Status.running, + llm_token=before_action + "\n", + ) + self.queue.put_nowait(dumps(self.cur_tool)) + self.out = False + break if token and self.out: self.cur_tool.update( - status=Status.running, - llm_token=token, + status=Status.running, + llm_token=token, ) self.queue.put_nowait(dumps(self.cur_tool)) diff --git a/server/agent/custom_agent/ChatGLM3Agent.py b/server/agent/custom_agent/ChatGLM3Agent.py new file mode 100644 index 00000000..b10b7818 --- /dev/null +++ b/server/agent/custom_agent/ChatGLM3Agent.py @@ -0,0 +1,280 @@ +""" +This file is a modified version for ChatGLM3-6B the original ChatGLM3Agent.py file from the langchain repo. + +""" +from __future__ import annotations + +import yaml +from langchain.agents.structured_chat.output_parser import StructuredChatOutputParser +from typing import Any, List, Sequence, Tuple, Optional, Union +import os +from langchain.agents.agent import Agent +from langchain.chains.llm import LLMChain +from langchain.prompts.chat import ( + ChatPromptTemplate, + HumanMessagePromptTemplate, + SystemMessagePromptTemplate, +) +import json +import logging +from langchain.agents.agent import AgentOutputParser +from langchain.output_parsers import OutputFixingParser +from langchain.pydantic_v1 import Field +from langchain.schema import AgentAction, AgentFinish, OutputParserException, BasePromptTemplate +from langchain.agents.agent import AgentExecutor +from langchain.callbacks.base import BaseCallbackManager +from langchain.schema.language_model import BaseLanguageModel +from langchain.tools.base import BaseTool + +PREFIX = """ +You can answer using the tools, or answer directly using your knowledge without using the tools. +Respond to the human as helpfully and accurately as possible. +You have access to the following tools: +""" +FORMAT_INSTRUCTIONS = """Use a json blob to specify a tool by providing an action key (tool name) and an action_input key (tool input). + +Valid "action" values: "Final Answer" or {tool_names} + +Provide only ONE action per $JSON_BLOB, as shown: + +``` +{{{{ + "action": $TOOL_NAME, + "action_input": $INPUT +}}}} +``` + +Follow this format: + +Question: input question to answer +Thought: consider previous and subsequent steps +Action: +``` +$JSON_BLOB +``` +Observation: action result +... (repeat Thought/Action/Observation N times) +Thought: I know what to respond +Action: +``` +{{{{ + "action": "Final Answer", + "action_input": "Final response to human" +}}}} +```""" +SUFFIX = """Begin! Reminder to ALWAYS respond with a valid json blob of a single action. Use tools if necessary. Respond directly if appropriate. Format is Action:```$JSON_BLOB```then Observation:. +Thought:""" + +HUMAN_MESSAGE_TEMPLATE = "{input}\n\n{agent_scratchpad}" +logger = logging.getLogger(__name__) + + +class StructuredChatOutputParserWithRetries(AgentOutputParser): + """Output parser with retries for the structured chat agent.""" + + base_parser: AgentOutputParser = Field(default_factory=StructuredChatOutputParser) + """The base parser to use.""" + output_fixing_parser: Optional[OutputFixingParser] = None + """The output fixing parser to use.""" + + def get_format_instructions(self) -> str: + return FORMAT_INSTRUCTIONS + + def parse(self, text: str) -> Union[AgentAction, AgentFinish]: + special_tokens = ["Action:", "<|observation|>"] + first_index = min([text.find(token) if token in text else len(text) for token in special_tokens]) + text = text[:first_index] + if "tool_call" in text: + tool_name_end = text.find("```") + tool_name = text[:tool_name_end].strip() + input_para = text.split("='")[-1].split("'")[0] + action_json = { + "action": tool_name, + "action_input": input_para + } + else: + action_json = { + "action": "Final Answer", + "action_input": text + } + action_str = f""" +Action: +``` +{json.dumps(action_json, ensure_ascii=False)} +```""" + try: + if self.output_fixing_parser is not None: + parsed_obj: Union[ + AgentAction, AgentFinish + ] = self.output_fixing_parser.parse(action_str) + else: + parsed_obj = self.base_parser.parse(action_str) + return parsed_obj + except Exception as e: + raise OutputParserException(f"Could not parse LLM output: {text}") from e + @property + def _type(self) -> str: + return "structured_chat_ChatGLM3_6b_with_retries" + + +class StructuredGLM3ChatAgent(Agent): + """Structured Chat Agent.""" + + output_parser: AgentOutputParser = Field( + default_factory=StructuredChatOutputParserWithRetries + ) + """Output parser for the agent.""" + + @property + def observation_prefix(self) -> str: + """Prefix to append the ChatGLM3-6B observation with.""" + return "Observation:" + + @property + def llm_prefix(self) -> str: + """Prefix to append the llm call with.""" + return "Thought:" + + def _construct_scratchpad( + self, intermediate_steps: List[Tuple[AgentAction, str]] + ) -> str: + agent_scratchpad = super()._construct_scratchpad(intermediate_steps) + if not isinstance(agent_scratchpad, str): + raise ValueError("agent_scratchpad should be of type string.") + if agent_scratchpad: + return ( + f"This was your previous work " + f"(but I haven't seen any of it! I only see what " + f"you return as final answer):\n{agent_scratchpad}" + ) + else: + return agent_scratchpad + + @classmethod + def _validate_tools(cls, tools: Sequence[BaseTool]) -> None: + pass + + @classmethod + def _get_default_output_parser( + cls, llm: Optional[BaseLanguageModel] = None, **kwargs: Any + ) -> AgentOutputParser: + return StructuredChatOutputParserWithRetries(llm=llm) + + @property + def _stop(self) -> List[str]: + return ["```"] + + @classmethod + def create_prompt( + cls, + tools: Sequence[BaseTool], + prefix: str = PREFIX, + suffix: str = SUFFIX, + human_message_template: str = HUMAN_MESSAGE_TEMPLATE, + format_instructions: str = FORMAT_INSTRUCTIONS, + input_variables: Optional[List[str]] = None, + memory_prompts: Optional[List[BasePromptTemplate]] = None, + ) -> BasePromptTemplate: + def tool_config_from_file(tool_name, directory="server/agent/tools/"): + """search tool yaml and return json format""" + file_path = os.path.join(directory, f"{tool_name.lower()}.yaml") + try: + with open(file_path, 'r', encoding='utf-8') as file: + return yaml.safe_load(file) + except FileNotFoundError: + print(f"File not found: {file_path}") + return None + except Exception as e: + print(f"An error occurred while reading {file_path}: {e}") + return None + + tools_json = [] + tool_names = "" + for tool in tools: + tool_config = tool_config_from_file(tool.name) + if tool_config: + tools_json.append(tool_config) + tool_names.join(tool.name + ", ") + + formatted_tools = "\n".join([ + json.dumps(tool, ensure_ascii=False).replace("\"", "\\\"").replace("{", "{{").replace("}", "}}") + for tool in tools_json + ]) + format_instructions = format_instructions.format(tool_names=tool_names) + template = "\n\n".join([prefix, formatted_tools, format_instructions, suffix]) + if input_variables is None: + input_variables = ["input", "agent_scratchpad"] + _memory_prompts = memory_prompts or [] + messages = [ + SystemMessagePromptTemplate.from_template(template), + *_memory_prompts, + HumanMessagePromptTemplate.from_template(human_message_template), + ] + return ChatPromptTemplate(input_variables=input_variables, messages=messages) + + @classmethod + def from_llm_and_tools( + cls, + llm: BaseLanguageModel, + tools: Sequence[BaseTool], + callback_manager: Optional[BaseCallbackManager] = None, + output_parser: Optional[AgentOutputParser] = None, + prefix: str = PREFIX, + suffix: str = SUFFIX, + human_message_template: str = HUMAN_MESSAGE_TEMPLATE, + format_instructions: str = FORMAT_INSTRUCTIONS, + input_variables: Optional[List[str]] = None, + memory_prompts: Optional[List[BasePromptTemplate]] = None, + **kwargs: Any, + ) -> Agent: + """Construct an agent from an LLM and tools.""" + cls._validate_tools(tools) + prompt = cls.create_prompt( + tools, + prefix=prefix, + suffix=suffix, + human_message_template=human_message_template, + format_instructions=format_instructions, + input_variables=input_variables, + memory_prompts=memory_prompts, + ) + llm_chain = LLMChain( + llm=llm, + prompt=prompt, + callback_manager=callback_manager, + ) + tool_names = [tool.name for tool in tools] + _output_parser = output_parser or cls._get_default_output_parser(llm=llm) + return cls( + llm_chain=llm_chain, + allowed_tools=tool_names, + output_parser=_output_parser, + **kwargs, + ) + + @property + def _agent_type(self) -> str: + raise ValueError + + +def initialize_glm3_agent( + tools: Sequence[BaseTool], + llm: BaseLanguageModel, + callback_manager: Optional[BaseCallbackManager] = None, + agent_kwargs: Optional[dict] = None, + *, + tags: Optional[Sequence[str]] = None, + **kwargs: Any, +) -> AgentExecutor: + tags_ = list(tags) if tags else [] + agent_kwargs = agent_kwargs or {} + agent_obj = StructuredGLM3ChatAgent.from_llm_and_tools( + llm, tools, callback_manager=callback_manager, **agent_kwargs + ) + return AgentExecutor.from_agent_and_tools( + agent=agent_obj, + tools=tools, + callback_manager=callback_manager, + tags=tags_, + **kwargs, + ) diff --git a/server/agent/custom_template.py b/server/agent/custom_template.py index c77066a4..b1f259df 100644 --- a/server/agent/custom_template.py +++ b/server/agent/custom_template.py @@ -54,21 +54,6 @@ class CustomOutputParser(AgentOutputParser): action = parts[1].split("Action Input:")[0].strip() action_input = parts[1].split("Action Input:")[1].strip() - - # 原来的正则化检查方式,更严格,但是成功率更低 - # regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)" - # print("llm_output",llm_output) - # match = re.search(regex, llm_output, re.DOTALL) - # print("match",match) - # if not match: - # return AgentFinish( - # return_values={"output": f"调用agent失败: `{llm_output}`"}, - # log=llm_output, - # ) - # action = match.group(1).strip() - # action_input = match.group(2) - - # Return the action and action input try: ans = AgentAction( tool=action, diff --git a/server/agent/tools/__init__.py b/server/agent/tools/__init__.py index 89527a62..43d57343 100644 --- a/server/agent/tools/__init__.py +++ b/server/agent/tools/__init__.py @@ -1,11 +1,11 @@ ## 导入所有的工具类 -from .search_knowledge_simple import knowledge_search_simple -from .search_all_knowledge_once import knowledge_search_once, KnowledgeSearchInput -from .search_all_knowledge_more import knowledge_search_more, KnowledgeSearchInput +from .search_knowledgebase_simple import search_knowledgebase_simple +from .search_knowledgebase_once import search_knowledgebase_once, KnowledgeSearchInput +from .search_knowledgebase_complex import search_knowledgebase_complex, KnowledgeSearchInput from .calculate import calculate, CalculatorInput -from .translator import translate, TranslateInput -from .weather import weathercheck, WhetherSchema +from .weather_check import weathercheck, WhetherSchema from .shell import shell, ShellInput from .search_internet import search_internet, SearchInternetInput from .wolfram import wolfram, WolframInput -from .youtube import youtube_search, YoutubeInput +from .search_youtube import search_youtube, YoutubeInput +from .arxiv import arxiv, ArxivInput diff --git a/server/agent/tools/arxiv.py b/server/agent/tools/arxiv.py new file mode 100644 index 00000000..5e91d35b --- /dev/null +++ b/server/agent/tools/arxiv.py @@ -0,0 +1,9 @@ +# LangChain 的 ArxivQueryRun 工具 +from pydantic import BaseModel, Field +from langchain.tools.arxiv.tool import ArxivQueryRun +def arxiv(query: str): + tool = ArxivQueryRun() + return tool.run(tool_input=query) + +class ArxivInput(BaseModel): + query: str = Field(description="The search query title") \ No newline at end of file diff --git a/server/agent/tools/arxiv.yaml b/server/agent/tools/arxiv.yaml new file mode 100644 index 00000000..5da8a9f9 --- /dev/null +++ b/server/agent/tools/arxiv.yaml @@ -0,0 +1,10 @@ +name: arxiv +description: A wrapper around Arxiv.org for searching and retrieving scientific articles in various fields. +parameters: + type: object + properties: + query: + type: string + description: The search query title +required: + - query \ No newline at end of file diff --git a/server/agent/tools/calculate.yaml b/server/agent/tools/calculate.yaml new file mode 100644 index 00000000..2976c011 --- /dev/null +++ b/server/agent/tools/calculate.yaml @@ -0,0 +1,10 @@ +name: calculate +description: Useful for when you need to answer questions about simple calculations +parameters: + type: object + properties: + query: + type: string + description: The formula to be calculated +required: + - query \ No newline at end of file diff --git a/server/agent/tools/search_internet.py b/server/agent/tools/search_internet.py index f2248494..48a8a629 100644 --- a/server/agent/tools/search_internet.py +++ b/server/agent/tools/search_internet.py @@ -29,7 +29,7 @@ def search_internet(query: str): return asyncio.run(search_engine_iter(query)) class SearchInternetInput(BaseModel): - location: str = Field(description="需要查询的内容") + location: str = Field(description="Query for Internet search") if __name__ == "__main__": diff --git a/server/agent/tools/search_internet.yaml b/server/agent/tools/search_internet.yaml new file mode 100644 index 00000000..57608e7e --- /dev/null +++ b/server/agent/tools/search_internet.yaml @@ -0,0 +1,10 @@ +name: search_internet +description: Use this tool to surf internet and get information +parameters: + type: object + properties: + query: + type: string + description: Query for Internet search +required: + - query \ No newline at end of file diff --git a/server/agent/tools/search_all_knowledge_more.py b/server/agent/tools/search_knowledgebase_complex.py similarity index 98% rename from server/agent/tools/search_all_knowledge_more.py rename to server/agent/tools/search_knowledgebase_complex.py index a42681a7..0b7884bd 100644 --- a/server/agent/tools/search_all_knowledge_more.py +++ b/server/agent/tools/search_knowledgebase_complex.py @@ -266,17 +266,17 @@ class LLMKnowledgeChain(LLMChain): return cls(llm_chain=llm_chain, **kwargs) -def knowledge_search_more(query: str): +def search_knowledgebase_complex(query: str): model = model_container.MODEL llm_knowledge = LLMKnowledgeChain.from_llm(model, verbose=True, prompt=PROMPT) ans = llm_knowledge.run(query) return ans class KnowledgeSearchInput(BaseModel): - location: str = Field(description="知识库查询的内容") + location: str = Field(description="The query to be searched") if __name__ == "__main__": - result = knowledge_search_more("机器人和大数据在代码教学上有什么区别") + result = search_knowledgebase_complex("机器人和大数据在代码教学上有什么区别") print(result) # 这是一个正常的切割 diff --git a/server/agent/tools/search_knowledgebase_complex.yaml b/server/agent/tools/search_knowledgebase_complex.yaml new file mode 100644 index 00000000..bccbfd8c --- /dev/null +++ b/server/agent/tools/search_knowledgebase_complex.yaml @@ -0,0 +1,10 @@ +name: search_knowledgebase_complex +description: Use this tool to search local knowledgebase and get information +parameters: + type: object + properties: + query: + type: string + description: The query to be searched +required: + - query \ No newline at end of file diff --git a/server/agent/tools/search_all_knowledge_once.py b/server/agent/tools/search_knowledgebase_once.py similarity index 97% rename from server/agent/tools/search_all_knowledge_once.py rename to server/agent/tools/search_knowledgebase_once.py index cf706b8d..c9a2d7b5 100644 --- a/server/agent/tools/search_all_knowledge_once.py +++ b/server/agent/tools/search_knowledgebase_once.py @@ -218,7 +218,7 @@ class LLMKnowledgeChain(LLMChain): return cls(llm_chain=llm_chain, **kwargs) -def knowledge_search_once(query: str): +def search_knowledgebase_once(query: str): model = model_container.MODEL llm_knowledge = LLMKnowledgeChain.from_llm(model, verbose=True, prompt=PROMPT) ans = llm_knowledge.run(query) @@ -226,9 +226,9 @@ def knowledge_search_once(query: str): class KnowledgeSearchInput(BaseModel): - location: str = Field(description="知识库查询的内容") + location: str = Field(description="The query to be searched") if __name__ == "__main__": - result = knowledge_search_once("大数据的男女比例") + result = search_knowledgebase_once("大数据的男女比例") print(result) diff --git a/server/agent/tools/search_knowledge_simple.py b/server/agent/tools/search_knowledgebase_simple.py similarity index 91% rename from server/agent/tools/search_knowledge_simple.py rename to server/agent/tools/search_knowledgebase_simple.py index fbffbd7e..65d7df3b 100644 --- a/server/agent/tools/search_knowledge_simple.py +++ b/server/agent/tools/search_knowledgebase_simple.py @@ -23,10 +23,10 @@ async def search_knowledge_base_iter(database: str, query: str) -> str: docs = data["docs"] return contents -def knowledge_search_simple(query: str): +def search_knowledgebase_simple(query: str): return asyncio.run(search_knowledge_base_iter(query)) if __name__ == "__main__": - result = knowledge_search_simple("大数据男女比例") + result = search_knowledgebase_simple("大数据男女比例") print("答案:",result) \ No newline at end of file diff --git a/server/agent/tools/youtube.py b/server/agent/tools/search_youtube.py similarity index 70% rename from server/agent/tools/youtube.py rename to server/agent/tools/search_youtube.py index 27eb8bd2..f02b3625 100644 --- a/server/agent/tools/youtube.py +++ b/server/agent/tools/search_youtube.py @@ -1,9 +1,9 @@ # Langchain 自带的 YouTube 搜索工具封装 from langchain.tools import YouTubeSearchTool from pydantic import BaseModel, Field -def youtube_search(query: str): +def search_youtube(query: str): tool = YouTubeSearchTool() return tool.run(tool_input=query) class YoutubeInput(BaseModel): - location: str = Field(description="要搜索视频关键字") \ No newline at end of file + location: str = Field(description="Query for Videos search") \ No newline at end of file diff --git a/server/agent/tools/search_youtube.yaml b/server/agent/tools/search_youtube.yaml new file mode 100644 index 00000000..ce446068 --- /dev/null +++ b/server/agent/tools/search_youtube.yaml @@ -0,0 +1,10 @@ +name: search_youtube +description: Use this tools to search youtube videos +parameters: + type: object + properties: + query: + type: string + description: Query for Videos search +required: + - query \ No newline at end of file diff --git a/server/agent/tools/shell.yaml b/server/agent/tools/shell.yaml new file mode 100644 index 00000000..22b94180 --- /dev/null +++ b/server/agent/tools/shell.yaml @@ -0,0 +1,10 @@ +name: shell +description: Use Linux Shell to execute Linux commands +parameters: + type: object + properties: + query: + type: string + description: The command to execute +required: + - query \ No newline at end of file diff --git a/server/agent/tools/translator.py b/server/agent/tools/translator.py deleted file mode 100644 index 2655d52f..00000000 --- a/server/agent/tools/translator.py +++ /dev/null @@ -1,38 +0,0 @@ -from langchain.prompts import PromptTemplate -from langchain.chains import LLMChain -from server.agent import model_container -from pydantic import BaseModel, Field - -_PROMPT_TEMPLATE = ''' -# 指令 -接下来,作为一个专业的翻译专家,当我给出句子或段落时,你将提供通顺且具有可读性的对应语言的翻译。注意: -1. 确保翻译结果流畅且易于理解 -2. 无论提供的是陈述句或疑问句,只进行翻译 -3. 不添加与原文无关的内容 - -问题: ${{用户需要翻译的原文和目标语言}} -答案: 你翻译结果 - -现在,这是我的问题: -问题: {question} - -''' - -PROMPT = PromptTemplate( - input_variables=["question"], - template=_PROMPT_TEMPLATE, -) - - -def translate(query: str): - model = model_container.MODEL - llm_translate = LLMChain(llm=model, prompt=PROMPT) - ans = llm_translate.run(query) - return ans - -class TranslateInput(BaseModel): - location: str = Field(description="需要被翻译的内容") - -if __name__ == "__main__": - result = translate("Can Love remember the question and the answer? 这句话如何诗意的翻译成中文") - print("答案:",result) \ No newline at end of file diff --git a/server/agent/tools/weather.py b/server/agent/tools/weather_check.py similarity index 100% rename from server/agent/tools/weather.py rename to server/agent/tools/weather_check.py diff --git a/server/agent/tools/weather_check.yaml b/server/agent/tools/weather_check.yaml new file mode 100644 index 00000000..e72676ac --- /dev/null +++ b/server/agent/tools/weather_check.yaml @@ -0,0 +1,10 @@ +name: weather_check +description: Use Weather API to get weather information +parameters: + type: object + properties: + query: + type: string + description: City name,include city and county,like "厦门市思明区" +required: + - query \ No newline at end of file diff --git a/server/agent/tools/wolfram.yaml b/server/agent/tools/wolfram.yaml new file mode 100644 index 00000000..532f8248 --- /dev/null +++ b/server/agent/tools/wolfram.yaml @@ -0,0 +1,10 @@ +name: wolfram +description: Useful for when you need to calculate difficult math formulas +parameters: + type: object + properties: + query: + type: string + description: The formula to be calculated +required: + - query \ No newline at end of file diff --git a/server/agent/tools_select.py b/server/agent/tools_select.py index 330db43d..821d324e 100644 --- a/server/agent/tools_select.py +++ b/server/agent/tools_select.py @@ -6,50 +6,50 @@ from server.agent.tools import * tools = [ Tool.from_function( func=calculate, - name="计算器工具", - description="进行简单的数学运算, 只是简单的, 使用Wolfram数学工具进行更复杂的运算", + name="calculate", + description="Useful for when you need to answer questions about simple calculations", args_schema=CalculatorInput, ), Tool.from_function( - func=translate, - name="翻译工具", - description="如果你无法访问互联网,并且需要翻译各种语言,应该使用这个工具", - args_schema=TranslateInput, + func=arxiv, + name="arxiv", + description="A wrapper around Arxiv.org for searching and retrieving scientific articles in various fields.", + args_schema=ArxivInput, ), Tool.from_function( func=weathercheck, - name="天气查询工具", - description="无需访问互联网,使用这个工具查询中国各地未来24小时的天气", + name="weather_check", + description="", args_schema=WhetherSchema, ), Tool.from_function( func=shell, - name="shell工具", - description="使用命令行工具输出", + name="shell", + description="Use Shell to execute Linux commands", args_schema=ShellInput, ), Tool.from_function( - func=knowledge_search_more, - name="知识库查询工具", - description="优先访问知识库来获取答案", + func=search_knowledgebase_complex, + name="search_knowledgebase_complex", + description="Use Use this tool to search local knowledgebase and get information", args_schema=KnowledgeSearchInput, ), Tool.from_function( func=search_internet, - name="互联网查询工具", - description="如果你无法访问互联网,这个工具可以帮助你访问Bing互联网来解答问题", + name="search_internet", + description="Use this tool to use bing search engine to search the internet", args_schema=SearchInternetInput, ), Tool.from_function( func=wolfram, - name="Wolfram数学工具", - description="高级的数学运算工具,能够完成非常复杂的数学问题", + name="Wolfram", + description="Useful for when you need to calculate difficult formulas", args_schema=WolframInput, ), Tool.from_function( - func=youtube_search, - name="Youtube搜索工具", - description="使用这个工具在Youtube上搜索视频", + func=search_youtube, + name="search_youtube", + description="use this tools to search youtube videos", args_schema=YoutubeInput, ), ] diff --git a/server/chat/agent_chat.py b/server/chat/agent_chat.py index ccbc1e19..b094caf8 100644 --- a/server/chat/agent_chat.py +++ b/server/chat/agent_chat.py @@ -1,14 +1,16 @@ from langchain.memory import ConversationBufferWindowMemory + +from server.agent.custom_agent.ChatGLM3Agent import initialize_glm3_agent from server.agent.tools_select import tools, tool_names from server.agent.callbacks import CustomAsyncIteratorCallbackHandler, Status -from langchain.agents import AgentExecutor, LLMSingleActionAgent, initialize_agent, BaseMultiActionAgent +from langchain.agents import LLMSingleActionAgent, AgentExecutor from server.agent.custom_template import CustomOutputParser, CustomPromptTemplate from fastapi import Body from fastapi.responses import StreamingResponse from configs import LLM_MODELS, TEMPERATURE, HISTORY_LEN, Agent_MODEL from server.utils import wrap_done, get_ChatOpenAI, get_prompt_template from langchain.chains import LLMChain -from typing import AsyncIterable, Optional, Dict +from typing import AsyncIterable, Optional import asyncio from typing import List from server.chat.utils import History @@ -73,12 +75,6 @@ async def agent_chat(query: str = Body(..., description="用户输入", examples ) output_parser = CustomOutputParser() llm_chain = LLMChain(llm=model, prompt=prompt_template_agent) - agent = LLMSingleActionAgent( - llm_chain=llm_chain, - output_parser=output_parser, - stop=["\nObservation:", "Observation:", "<|im_end|>", "<|observation|>"], # Qwen模型中使用这个 - allowed_tools=tool_names, - ) # 把history转成agent的memory memory = ConversationBufferWindowMemory(k=HISTORY_LEN * 2) for message in history: @@ -89,11 +85,27 @@ async def agent_chat(query: str = Body(..., description="用户输入", examples else: # 添加AI消息 memory.chat_memory.add_ai_message(message.content) - agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, - tools=tools, - verbose=True, - memory=memory, - ) + + if "chatglm3" in model_container.MODEL.model_name: + agent_executor = initialize_glm3_agent( + llm=model, + tools=tools, + callback_manager=None, + verbose=True, + memory=memory, + ) + else: + agent = LLMSingleActionAgent( + llm_chain=llm_chain, + output_parser=output_parser, + stop=["\nObservation:", "Observation"], + allowed_tools=tool_names, + ) + agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, + tools=tools, + verbose=True, + memory=memory, + ) while True: try: task = asyncio.create_task(wrap_done( diff --git a/webui_pages/dialogue/dialogue.py b/webui_pages/dialogue/dialogue.py index 80b0d945..70448b07 100644 --- a/webui_pages/dialogue/dialogue.py +++ b/webui_pages/dialogue/dialogue.py @@ -63,7 +63,7 @@ def dialogue_page(api: ApiRequest, is_lite: bool = False): ] dialogue_mode = st.selectbox("请选择对话模式:", dialogue_modes, - index=0, + index=3, on_change=on_mode_change, key="dialogue_mode", )