From 0361f9da15b90cd07280fed387742b134cc37395 Mon Sep 17 00:00:00 2001 From: jinno Date: Wed, 15 May 2024 09:10:07 +0900 Subject: [PATCH] feat: add agents/plan_and_execute --- src/codeinterpreterapi/agents/__init__.py | 1 + src/codeinterpreterapi/agents/agents.py | 3 +- .../agents/plan_and_execute/__init__.py | 2 + .../agents/plan_and_execute/agent_executor.py | 74 ++++++++++++++++ .../agents/plan_and_execute/prompts.py | 88 +++++++++++++++++++ src/codeinterpreterapi/planners/planners.py | 1 + 6 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 src/codeinterpreterapi/agents/__init__.py create mode 100644 src/codeinterpreterapi/agents/plan_and_execute/__init__.py create mode 100644 src/codeinterpreterapi/agents/plan_and_execute/agent_executor.py create mode 100644 src/codeinterpreterapi/agents/plan_and_execute/prompts.py diff --git a/src/codeinterpreterapi/agents/__init__.py b/src/codeinterpreterapi/agents/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/codeinterpreterapi/agents/__init__.py @@ -0,0 +1 @@ + diff --git a/src/codeinterpreterapi/agents/agents.py b/src/codeinterpreterapi/agents/agents.py index 0d4cb0a6..6ace288a 100644 --- a/src/codeinterpreterapi/agents/agents.py +++ b/src/codeinterpreterapi/agents/agents.py @@ -2,14 +2,13 @@ from langchain.agents import AgentExecutor, BaseSingleActionAgent, ConversationalAgent, ConversationalChatAgent from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent -from langchain.chat_models.base import BaseChatModel from langchain.memory.buffer import ConversationBufferMemory from langchain_anthropic import ChatAnthropic from langchain_core.prompts.chat import MessagesPlaceholder -from langchain_experimental.plan_and_execute import PlanAndExecute, load_agent_executor, load_chat_planner from langchain_google_genai import ChatGoogleGenerativeAI from langchain_openai import AzureChatOpenAI, ChatOpenAI +from codeinterpreterapi.agents.plan_and_execute.agent_executor import load_agent_executor from codeinterpreterapi.config import settings diff --git a/src/codeinterpreterapi/agents/plan_and_execute/__init__.py b/src/codeinterpreterapi/agents/plan_and_execute/__init__.py new file mode 100644 index 00000000..2c0f5324 --- /dev/null +++ b/src/codeinterpreterapi/agents/plan_and_execute/__init__.py @@ -0,0 +1,2 @@ +# agent_executor.py +# https://github.com/langchain-ai/langchain/blob/3ee07473821906a29d944866a2ededb41148f234/libs/experimental/langchain_experimental/plan_and_execute/executors/agent_executor.py diff --git a/src/codeinterpreterapi/agents/plan_and_execute/agent_executor.py b/src/codeinterpreterapi/agents/plan_and_execute/agent_executor.py new file mode 100644 index 00000000..cf8d87da --- /dev/null +++ b/src/codeinterpreterapi/agents/plan_and_execute/agent_executor.py @@ -0,0 +1,74 @@ +from typing import List, Optional + +from langchain.agents.agent import AgentExecutor, AgentOutputParser +from langchain.agents.structured_chat.base import StructuredChatAgent +from langchain.tools import BaseTool +from langchain_core.callbacks import BaseCallbackManager +from langchain_core.language_models import BaseLanguageModel +from langchain_experimental.plan_and_execute.executors.base import ChainExecutor + +from codeinterpreterapi.agents.plan_and_execute.prompts import ( + FORMAT_INSTRUCTIONS, + FORMAT_INSTRUCTIONS_JA, + HUMAN_MESSAGE_TEMPLATE, + SUFFIX, + SUFFIX_JA, + TASK_PREFIX, + TOOLS_PREFIX, + TOOLS_PREFIX_JA, +) + + +def load_agent_executor( + llm: BaseLanguageModel, + tools: List[BaseTool], + callback_manager: Optional[BaseCallbackManager] = None, + output_parser: Optional[AgentOutputParser] = None, + verbose: bool = False, + include_task_in_prompt: bool = False, + is_ja: str = True, +) -> ChainExecutor: + """ + Load an agent executor. + + Args: + llm: BaseLanguageModel + tools: List[BaseTool] + verbose: bool. Defaults to False. + include_task_in_prompt: bool. Defaults to False. + + Returns: + ChainExecutor + """ + input_variables = ["previous_steps", "current_step", "agent_scratchpad"] + + # message_template + message_template = "" + if include_task_in_prompt: + input_variables.append("objective") + message_template += TASK_PREFIX + message_template += HUMAN_MESSAGE_TEMPLATE + + # format_instructions, tools_prefix, suffix + if is_ja: + format_instructions = FORMAT_INSTRUCTIONS_JA + tools_prefix = TOOLS_PREFIX_JA + suffix = SUFFIX_JA + else: + format_instructions = FORMAT_INSTRUCTIONS + tools_prefix = TOOLS_PREFIX + suffix = SUFFIX + agent = StructuredChatAgent.from_llm_and_tools( + llm=llm, + tools=tools, + callback_manager=callback_manager, + output_parser=output_parser, + prefix=tools_prefix, + suffix=suffix, + human_message_template=message_template, + format_instructions=format_instructions, + input_variables=input_variables, + # memory_prompts = memory_prompts, + ) + agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=verbose) + return ChainExecutor(chain=agent_executor) diff --git a/src/codeinterpreterapi/agents/plan_and_execute/prompts.py b/src/codeinterpreterapi/agents/plan_and_execute/prompts.py new file mode 100644 index 00000000..f7972fb2 --- /dev/null +++ b/src/codeinterpreterapi/agents/plan_and_execute/prompts.py @@ -0,0 +1,88 @@ +from textwrap import dedent + +from langchain_core.prompts import PromptTemplate +from langchain_experimental.tot.prompts import JSONListOutputParser + +HUMAN_MESSAGE_TEMPLATE = """Previous steps: {previous_steps} + +Current objective: {current_step} + +{agent_scratchpad}""" + +TASK_PREFIX = """{objective} + +""" + +TOOLS_PREFIX = ( + """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 like this. +Action:```$JSON_BLOB``` +Observation: +Thought:""" + +TOOLS_PREFIX_JA = """できる限り丁寧かつ正確に人間の質問に答えてください。以下のツールが利用可能です:""" + +FORMAT_INSTRUCTIONS_JA = """ +始めましょう!常に単一のアクションの有効なJSONブロブで応答することを忘れないでください。 +必要に応じてツールを使用してください。適切な場合は直接回答してください。 +フォーマットは次のようにしてください。 +Action:```$JSON_BLOB``` +Observation: +Thought: +""" +SUFFIX_JA = """それでは始めましょう。常に単一のアクションの有効なJSON_BLOBで応答してください。 +必要に応じてツールを使用してください。適切な場合は直接回答してください。 +フォーマットは次のようにしてください。 +Action:```$JSON_BLOB``` +Observation: +Thought:""" + + +def get_tools_prefix_prompt(is_ja: bool) -> str: + """Get the prefix prompt for plan_and_execute.""" + if is_ja: + return TOOLS_PREFIX_JA + else: + return TOOLS_PREFIX + + +def get_plan_and_execute_prompt(is_ja: bool) -> str: + """Get the main prompt for plan_and_execute.""" + if is_ja: + return FORMAT_INSTRUCTIONS_JA + else: + return FORMAT_INSTRUCTIONS diff --git a/src/codeinterpreterapi/planners/planners.py b/src/codeinterpreterapi/planners/planners.py index 5de726b8..845ec66c 100644 --- a/src/codeinterpreterapi/planners/planners.py +++ b/src/codeinterpreterapi/planners/planners.py @@ -26,5 +26,6 @@ class CodeInterpreterPlanner: @staticmethod def choose_planner(llm: BaseLanguageModel, is_ja: bool) -> LLMPlanner: system_prompt = SYSTEM_PROMPT_PLANNER_JA if is_ja else SYSTEM_PROMPT_PLANNER + print("system_prompt(planner)=", system_prompt) planner = load_chat_planner(llm, system_prompt=system_prompt) return planner