Upload 6 files
Browse files- rmrkl/__init__.py +3 -0
- rmrkl/agent.py +105 -0
- rmrkl/executor.py +23 -0
- rmrkl/output_parser.py +37 -0
- rmrkl/prompts.py +37 -0
- rmrkl/version.py +1 -0
rmrkl/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .version import __version__
|
| 2 |
+
from .agent import ChatZeroShotAgent
|
| 3 |
+
from .executor import RetryAgentExecutor
|
rmrkl/agent.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
from langchain.chat_models.base import BaseChatModel
|
| 3 |
+
from langchain.prompts import PromptTemplate
|
| 4 |
+
from langchain.chains import LLMChain
|
| 5 |
+
from langchain.callbacks.base import BaseCallbackManager
|
| 6 |
+
from .prompts import FORMAT_INSTRUCTIONS, SUFFIX, QUESTION_PROMPT, PREFIX
|
| 7 |
+
from langchain.agents.agent import Agent, AgentOutputParser
|
| 8 |
+
from typing import Any, Optional, Sequence
|
| 9 |
+
from langchain.tools import BaseTool
|
| 10 |
+
from langchain.agents.mrkl.base import ZeroShotAgent
|
| 11 |
+
from langchain.prompts.chat import (
|
| 12 |
+
ChatPromptTemplate,
|
| 13 |
+
SystemMessagePromptTemplate,
|
| 14 |
+
HumanMessagePromptTemplate,
|
| 15 |
+
AIMessagePromptTemplate,
|
| 16 |
+
)
|
| 17 |
+
from .output_parser import ChatZeroShotOutputParser
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class ChatZeroShotAgent(ZeroShotAgent):
|
| 21 |
+
"""Agent for the MRKL chain."""
|
| 22 |
+
|
| 23 |
+
@classmethod
|
| 24 |
+
def create_prompt(
|
| 25 |
+
cls,
|
| 26 |
+
tools: Sequence[BaseTool],
|
| 27 |
+
prefix: str = PREFIX,
|
| 28 |
+
suffix: str = SUFFIX,
|
| 29 |
+
format_instructions: str = FORMAT_INSTRUCTIONS,
|
| 30 |
+
question_prompt: str = QUESTION_PROMPT,
|
| 31 |
+
) -> PromptTemplate:
|
| 32 |
+
"""Create prompt in the style of the zero shot agent.
|
| 33 |
+
|
| 34 |
+
Args:
|
| 35 |
+
tools: List of tools the agent will have access to, used to format the
|
| 36 |
+
prompt.
|
| 37 |
+
prefix: String to put before the list of tools.
|
| 38 |
+
suffix: String to put after the list of tools.
|
| 39 |
+
input_variables: List of input variables the final prompt will expect.
|
| 40 |
+
|
| 41 |
+
Returns:
|
| 42 |
+
A PromptTemplate with the template assembled from the pieces here.
|
| 43 |
+
"""
|
| 44 |
+
tool_strings = "\n".join(
|
| 45 |
+
[f" {tool.name}: {tool.description}" for tool in tools]
|
| 46 |
+
)
|
| 47 |
+
tool_names = ", ".join([tool.name for tool in tools])
|
| 48 |
+
format_instructions = format_instructions.format(
|
| 49 |
+
tool_names=tool_names, tool_strings=tool_strings
|
| 50 |
+
)
|
| 51 |
+
human_prompt = PromptTemplate(
|
| 52 |
+
template=question_prompt,
|
| 53 |
+
input_variables=["input"],
|
| 54 |
+
partial_variables={"tool_strings": tool_strings},
|
| 55 |
+
)
|
| 56 |
+
human_message_prompt = HumanMessagePromptTemplate(prompt=human_prompt)
|
| 57 |
+
ai_message_prompt = AIMessagePromptTemplate.from_template(suffix)
|
| 58 |
+
system_message_prompt = SystemMessagePromptTemplate.from_template(
|
| 59 |
+
'\n\n'.join(
|
| 60 |
+
[
|
| 61 |
+
prefix,
|
| 62 |
+
format_instructions
|
| 63 |
+
]
|
| 64 |
+
)
|
| 65 |
+
)
|
| 66 |
+
# ignore suffix
|
| 67 |
+
return ChatPromptTemplate.from_messages(
|
| 68 |
+
[system_message_prompt, human_message_prompt, ai_message_prompt]
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
@classmethod
|
| 72 |
+
def from_llm_and_tools(
|
| 73 |
+
cls,
|
| 74 |
+
llm: BaseChatModel,
|
| 75 |
+
tools: Sequence[BaseTool],
|
| 76 |
+
callback_manager: Optional[BaseCallbackManager] = None,
|
| 77 |
+
output_parser: Optional[AgentOutputParser] = ChatZeroShotOutputParser(),
|
| 78 |
+
prefix: str = PREFIX,
|
| 79 |
+
suffix: str = SUFFIX,
|
| 80 |
+
format_instructions: str = FORMAT_INSTRUCTIONS,
|
| 81 |
+
question_prompt: str = QUESTION_PROMPT,
|
| 82 |
+
**kwargs: Any,
|
| 83 |
+
) -> Agent:
|
| 84 |
+
"""Construct an agent from an LLM and tools."""
|
| 85 |
+
cls._validate_tools(tools)
|
| 86 |
+
prompt = cls.create_prompt(
|
| 87 |
+
tools,
|
| 88 |
+
prefix=prefix,
|
| 89 |
+
suffix=suffix,
|
| 90 |
+
format_instructions=format_instructions,
|
| 91 |
+
question_prompt=question_prompt,
|
| 92 |
+
)
|
| 93 |
+
llm_chain = LLMChain(
|
| 94 |
+
llm=llm,
|
| 95 |
+
prompt=prompt,
|
| 96 |
+
callback_manager=callback_manager,
|
| 97 |
+
)
|
| 98 |
+
tool_names = [tool.name for tool in tools]
|
| 99 |
+
_output_parser = output_parser or cls._get_default_output_parser()
|
| 100 |
+
return cls(
|
| 101 |
+
llm_chain=llm_chain,
|
| 102 |
+
allowed_tools=tool_names,
|
| 103 |
+
output_parser=_output_parser,
|
| 104 |
+
**kwargs,
|
| 105 |
+
)
|
rmrkl/executor.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Dict, List, Tuple, Union, Optional
|
| 2 |
+
|
| 3 |
+
from langchain.agents import AgentExecutor
|
| 4 |
+
from langchain.schema import AgentAction, AgentFinish, OutputParserException
|
| 5 |
+
from langchain.tools import BaseTool
|
| 6 |
+
from langchain.callbacks.manager import CallbackManagerForChainRun
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ExceptionTool(BaseTool):
|
| 10 |
+
name: str = "_Exception"
|
| 11 |
+
description: str = "Exception tool"
|
| 12 |
+
|
| 13 |
+
def _run(self, query: str) :
|
| 14 |
+
return query
|
| 15 |
+
|
| 16 |
+
async def _arun(self, query: str) :
|
| 17 |
+
return query
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class RetryAgentExecutor(AgentExecutor):
|
| 21 |
+
"""Agent executor that retries on output parser exceptions."""
|
| 22 |
+
# for backwards compatibility
|
| 23 |
+
handle_parsing_errors: bool = True
|
rmrkl/output_parser.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
| 3 |
+
|
| 4 |
+
import langchain
|
| 5 |
+
from langchain import LLMChain
|
| 6 |
+
from langchain.agents.agent import AgentOutputParser
|
| 7 |
+
from langchain.schema import AgentAction, AgentFinish, OutputParserException
|
| 8 |
+
|
| 9 |
+
from .prompts import (FINAL_ANSWER_ACTION, FORMAT_INSTRUCTIONS,
|
| 10 |
+
QUESTION_PROMPT, SUFFIX)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class ChatZeroShotOutputParser(AgentOutputParser):
|
| 14 |
+
def get_format_instructions(self) -> str:
|
| 15 |
+
return FORMAT_INSTRUCTIONS
|
| 16 |
+
|
| 17 |
+
def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
|
| 18 |
+
if FINAL_ANSWER_ACTION in text:
|
| 19 |
+
return AgentFinish(
|
| 20 |
+
{"output": text.split(FINAL_ANSWER_ACTION)[-1].strip()}, text
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# Remove 'Thought' SUFFIX
|
| 25 |
+
if text.startswith('Thought:'):
|
| 26 |
+
text = text[8:]
|
| 27 |
+
|
| 28 |
+
# \s matches against tab/newline/whitespace
|
| 29 |
+
regex = (
|
| 30 |
+
r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
|
| 31 |
+
)
|
| 32 |
+
match = re.search(regex, text, re.DOTALL)
|
| 33 |
+
if not match:
|
| 34 |
+
raise OutputParserException(f"Could not parse LLM output: `{text}`")
|
| 35 |
+
action = match.group(1).strip()
|
| 36 |
+
action_input = match.group(2)
|
| 37 |
+
return AgentAction(action, action_input.strip(" ").strip('"'), text.strip())
|
rmrkl/prompts.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# flake8: noqa
|
| 2 |
+
|
| 3 |
+
PREFIX = """
|
| 4 |
+
You are an AI system called TeLLAgent and your task is to respond to the question or
|
| 5 |
+
solve the problem to the best of your ability using the provided tools.
|
| 6 |
+
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
FORMAT_INSTRUCTIONS = """
|
| 10 |
+
You should only respond with a single complete
|
| 11 |
+
Thought, Action, Action Input format
|
| 12 |
+
OR a single Final Answer format.
|
| 13 |
+
|
| 14 |
+
Complete Format:
|
| 15 |
+
|
| 16 |
+
Thought: (reflect on your progress and decide what to do next)
|
| 17 |
+
Action: (the action name, should be one of [{tool_names}])
|
| 18 |
+
Action Input: (the input string to the action)
|
| 19 |
+
|
| 20 |
+
OR
|
| 21 |
+
|
| 22 |
+
Final Answer: (the final answer to the original input question)
|
| 23 |
+
|
| 24 |
+
"""
|
| 25 |
+
QUESTION_PROMPT = """
|
| 26 |
+
Answer the question below using the following tools:
|
| 27 |
+
Note: Direct output the results when using the following tools:Imageanalysis, codewriter, pdfreader, rag without further processing.
|
| 28 |
+
|
| 29 |
+
{tool_strings}
|
| 30 |
+
|
| 31 |
+
Question: {input}
|
| 32 |
+
"""
|
| 33 |
+
SUFFIX = """
|
| 34 |
+
Direct output the results when using Imageanalysis, codewriter, pdfreader, rag tools without further processing.
|
| 35 |
+
Thought: {agent_scratchpad}
|
| 36 |
+
"""
|
| 37 |
+
FINAL_ANSWER_ACTION = "Final Answer:"
|
rmrkl/version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
__version__ = "0.0.3"
|