from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser from langchain.prompts import StringPromptTemplate from langchain import OpenAI, LLMChain from langchain.chat_models import ChatOpenAI from typing import List, Union from langchain.schema import AgentAction, AgentFinish from langchain.callbacks import CallbackManager, StdOutCallbackHandler import re from typing import Any, Dict, List, Optional, Union from tools import TiDBKB, OpenAIGPT, GoogleSearch, HumanInputRun from queue import Queue from callback_handler import grpcCallbackHandler from langchain.tools.python.tool import PythonREPLTool from langchain.utilities import PythonREPL from langchain.utilities.wikipedia import WikipediaAPIWrapper from langchain.tools.wikipedia.tool import WikipediaQueryRun # Set up a prompt template class CustomPromptTemplate(StringPromptTemplate): # The template to use template: str # The list of tools available tools: List[Tool] def format(self, **kwargs) -> str: # Get the intermediate steps (AgentAction, Observation tuples) # Format them in a particular way intermediate_steps = kwargs.pop("intermediate_steps") thoughts = "" for action, observation in intermediate_steps: thoughts += action.log thoughts += f"\nObservation: {observation}\n" # Set the agent_scratchpad variable to that value kwargs["agent_scratchpad"] = thoughts # Create a tools variable from the list of tools provided kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools]) # Create a list of tool names for the tools provided kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools]) return self.template.format(**kwargs) class CustomOutputParser(AgentOutputParser): def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]: # Check if agent should finish if "Final Answer:" in llm_output: return AgentFinish( # Return values is generally always a dictionary with a single `output` key # It is not recommended to try anything else at the moment :) return_values={"output": llm_output.split("Final Answer:")[-1].strip()}, log=llm_output, ) # Parse out the action and action input regex = r"Action: (.*?)[\n]*Action Input:[\s]*(.*)" match = re.search(regex, llm_output, re.DOTALL|re.IGNORECASE) if not match: print(f"Could not parse LLM output: `{llm_output}`") return AgentAction(tool=llm_output.strip(" ").strip('"'), tool_input=llm_output.strip(" ").strip('"'), log=llm_output) action = match.group(1).strip() action_input = match.group(2) # Return the action and action input return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output) output_parser = CustomOutputParser() wikiPlugin = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()) python_repl = PythonREPLTool() plugins = [TiDBKB(), OpenAIGPT(), GoogleSearch(), wikiPlugin, python_repl] # Set up the base template template = """Answer the following questions as best you can, but speaking as a pirate might speak. You have access to the following tools: {tools} Performance Evaluation: 1. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps. Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take. Choose the most appropriate action from the following action name list: [{tool_names}]. Don't take the useless tools. Action Input: the input to the action Observation: the result of the action ... (this Thought/Action/Action Input/Observation can repeat N times) Thought: I now know the final answer Final Answer: the final answer to the original input question Begin! Remember to speak as a pirate when giving your final answer and flow the above format. Use lots of "Arg"s Question: {input} {agent_scratchpad}""" output_parser = CustomOutputParser() def cmd(query: str, response_queue:Optional[Queue] = None): handlers = [StdOutCallbackHandler()] if response_queue is not None: handlers.append(grpcCallbackHandler(response_queue)) cm = CallbackManager(handlers) tools = [Tool(name=plugin.name, func=plugin.run, description=plugin.description, callback_manager=cm) for plugin in plugins] humanTools = HumanInputRun(response_queue) tools.append(Tool(name=humanTools.name, func=humanTools.run, description=humanTools.description, callback_manager=cm)) prompt = CustomPromptTemplate( template=template, tools=tools, # This omits the `agent_scratchpad`, `tools`, and `tool_names` variables because those are generated dynamically # This includes the `intermediate_steps` variable because that is needed input_variables=["input", "intermediate_steps"] ) llm = ChatOpenAI(temperature=0) # LLM chain consisting of the LLM and a prompt llm_chain = LLMChain(llm=llm, prompt=prompt) tool_names = [tool.name for tool in tools] agent = LLMSingleActionAgent( llm_chain=llm_chain, output_parser=output_parser, stop=["\nObservation:"], allowed_tools=tool_names ) agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True, callback_manager=cm) res = agent_executor.run(query)