diff --git a/src/codeinterpreterapi/agents/agents.py b/src/codeinterpreterapi/agents/agents.py index 6ace288a..1f6792ff 100644 --- a/src/codeinterpreterapi/agents/agents.py +++ b/src/codeinterpreterapi/agents/agents.py @@ -83,6 +83,6 @@ def create_agent_and_executor_experimental(llm, tools, verbose, is_ja) -> AgentE print("create_agent_and_executor agent=", str(type(agent))) # agent_executor - agent_executor = load_agent_executor(llm, tools, verbose=verbose) + agent_executor = load_agent_executor(llm, tools, verbose=verbose, is_ja=is_ja) return agent_executor diff --git a/src/codeinterpreterapi/agents/plan_and_execute/agent_executor.py b/src/codeinterpreterapi/agents/plan_and_execute/agent_executor.py index cf8d87da..59099cf2 100644 --- a/src/codeinterpreterapi/agents/plan_and_execute/agent_executor.py +++ b/src/codeinterpreterapi/agents/plan_and_execute/agent_executor.py @@ -1,22 +1,13 @@ from typing import List, Optional from langchain.agents.agent import AgentExecutor, AgentOutputParser -from langchain.agents.structured_chat.base import StructuredChatAgent +from langchain.agents.structured_chat.base import StructuredChatAgent, create_structured_chat_agent 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, -) +from codeinterpreterapi.agents.plan_and_execute.prompts import create_structured_chat_agent_prompt def load_agent_executor( @@ -40,34 +31,20 @@ def load_agent_executor( 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( + input_variables = ["previous_steps", "current_step", "agent_scratchpad", "tools", "tool_names"] + print("input_variables=", input_variables) + prompt = create_structured_chat_agent_prompt(is_ja) + print("prompt=", prompt.get_prompts()) + agent = create_structured_chat_agent( 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, + # callback_manager=callback_manager, + # output_parser=output_parser, + # prefix=tools_prefix, + # suffix=suffix, + prompt=prompt, + # 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) diff --git a/src/codeinterpreterapi/agents/plan_and_execute/prompts.py b/src/codeinterpreterapi/agents/plan_and_execute/prompts.py index f7972fb2..e624d4ff 100644 --- a/src/codeinterpreterapi/agents/plan_and_execute/prompts.py +++ b/src/codeinterpreterapi/agents/plan_and_execute/prompts.py @@ -1,88 +1,121 @@ from textwrap import dedent -from langchain_core.prompts import PromptTemplate +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate from langchain_experimental.tot.prompts import JSONListOutputParser -HUMAN_MESSAGE_TEMPLATE = """Previous steps: {previous_steps} +SYSTEM_MESSAGE_TEMPLATE = '''Respond to the human as helpfully and accurately as possible. You have access to the following tools: -Current objective: {current_step} + {tools} -{agent_scratchpad}""" + Use a json blob to specify a tool by providing an action key (tool name) and an action_input key (tool input). -TASK_PREFIX = """{objective} + Valid "action" values: "Final Answer" or {tool_names} -""" + Provide only ONE action per $JSON_BLOB, as shown: -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). + ``` + {{ + "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" + }} + + 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 + ''' + +HUMAN_MESSAGE_TEMPLATE = '''{input} + + {agent_scratchpad} + + (reminder to respond in a JSON blob no matter what)''' + +SYSTEM_MESSAGE_TEMPLATE_JA = '''初期質問にできる限り丁寧かつ正確に答えてください。以下のツールが利用可能です: + {tools} + +1つの $JSON_BLOB では常に単一のアクションで応答してください。 +action は (TOOL_NAME) 、action_input は (INPUT) を使ってください。 Valid "action" values: "Final Answer" or {tool_names} -Provide only ONE action per $JSON_BLOB, as shown: +フォーマットは次のようになります。 -``` -{{{{ - "action": $TOOL_NAME, - "action_input": $INPUT -}}}} -``` + ``` + {{ + "action": $TOOL_NAME, + "action_input": $INPUT + }} + ``` -Follow this format: +その後のシーケンスは以下の形式に従ってください: -Question: input question to answer -Thought: consider previous and subsequent steps +Question: 初期質問に正しく答えるための質問 +Thought: 前後のステップを検討する Action: + ``` $JSON_BLOB ``` -Observation: action result -... (repeat Thought/Action/Observation N times) -Thought: I know what to respond + +Observation: アクションの結果 + +(Thought/Action/Observation をN回繰り返す) + +Thought: 何を答えるべきかわかった 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 +{{ + "action": "最終回答", + "action_input": "初期質問への最終的な回答" +}} +``` + +それでは始めましょう。常に単一のアクションの有効なJSONブロブで応答することを忘れないでください。 +必要に応じてツールを使用してください。 +適切な場合は直接回答してください。 + ''' + +HUMAN_MESSAGE_TEMPLATE_JA = '''{input} + + {agent_scratchpad} + + リマインダ: 何があっても $JSON_BLOB だけで応答するようにしてください。 +''' -def get_plan_and_execute_prompt(is_ja: bool) -> str: - """Get the main prompt for plan_and_execute.""" +def create_structured_chat_agent_prompt(is_ja: bool = True): if is_ja: - return FORMAT_INSTRUCTIONS_JA + prompt = ChatPromptTemplate.from_messages( + [ + ("system", SYSTEM_MESSAGE_TEMPLATE_JA), + MessagesPlaceholder("chat_history", optional=True), + ("human", HUMAN_MESSAGE_TEMPLATE_JA), + ] + ) else: - return FORMAT_INSTRUCTIONS + prompt = ChatPromptTemplate.from_messages( + [ + ("system", SYSTEM_MESSAGE_TEMPLATE), + MessagesPlaceholder("chat_history", optional=True), + ("human", HUMAN_MESSAGE_TEMPLATE), + ] + ) + return prompt diff --git a/src/codeinterpreterapi/session.py b/src/codeinterpreterapi/session.py index 4c7d04a1..37b1321f 100644 --- a/src/codeinterpreterapi/session.py +++ b/src/codeinterpreterapi/session.py @@ -75,8 +75,10 @@ def __init__( if self.is_local: run_handler_func = self._run_handler_local arun_handler_func = self._arun_handler_local - self.tools: list[BaseTool] = CodeInterpreterTools.get_all(additional_tools, run_handler_func, arun_handler_func) self.llm: BaseLanguageModel = llm or CodeInterpreterLlm.get_llm() + self.tools: list[BaseTool] = CodeInterpreterTools.get_all( + additional_tools, run_handler_func, arun_handler_func, self.llm + ) self.log("self.llm=" + str(self.llm)) self.callbacks = callbacks diff --git a/src/codeinterpreterapi/tools/tools.py b/src/codeinterpreterapi/tools/tools.py index f558a290..c23449bc 100644 --- a/src/codeinterpreterapi/tools/tools.py +++ b/src/codeinterpreterapi/tools/tools.py @@ -1,6 +1,9 @@ -from langchain_community.tools.shell.tool import ShellTool +from langchain_community.tools.shell.tool import BaseTool, ShellTool from langchain_community.tools.tavily_search import TavilySearchResults -from langchain_core.tools import BaseTool, StructuredTool +from langchain_core.language_models import LLM, BaseLanguageModel, BaseLLM +from langchain_core.tools import StructuredTool, Tool +from langchain_experimental.chat_models.llm_wrapper import ChatWrapper +from langchain_experimental.llm_bash.base import LLMBashChain from codeinterpreterapi.config import settings from codeinterpreterapi.schema import CodeInput @@ -8,7 +11,9 @@ class CodeInterpreterTools: @staticmethod - def get_all(additional_tools: list[BaseTool], run_handler_func, arun_handler_func) -> list[BaseTool]: + def get_all( + additional_tools: list[BaseTool], llm: BaseLanguageModel, run_handler_func, arun_handler_func + ) -> list[BaseTool]: additional_tools = CodeInterpreterTools.get_python(additional_tools, run_handler_func, arun_handler_func) additional_tools = CodeInterpreterTools.get_shell(additional_tools) additional_tools = CodeInterpreterTools.get_web_search(additional_tools) @@ -39,11 +44,37 @@ def get_python(additional_tools: list[BaseTool], run_handler_func, arun_handler_ @staticmethod def get_shell(additional_tools: list[BaseTool]) -> list[BaseTool]: - tools = [ShellTool()] + """ + ShellTool cause this error. Should not use this. + pydantic.v1.error_wrappers.ValidationError: 1 validation error for ShellInput + commands + field required (type=value_error.missing) + """ + shell_tool = ShellTool() + shell_tool.description = shell_tool.description + f"args {shell_tool.args}".replace("{", "{{").replace( + "}", "}}" + ) + tools = [shell_tool] + return additional_tools + tools + + @staticmethod + def get_shell_v2(additional_tools: list[BaseTool], llm: BaseLanguageModel) -> list[BaseTool]: + """ + ShellTool cause this error. Should not use this. + pydantic.v1.error_wrappers.ValidationError: 1 validation error for ShellInput + commands + field required (type=value_error.missing) + """ + llm_runnable = ChatWrapper(llm=llm) + bash_chain = LLMBashChain.from_llm(llm=llm) + bash_tool = Tool( + name="Bash", func=bash_chain.invoke, description="Executes bash commands in a terminal environment." + ) + + tools = [bash_tool] return additional_tools + tools @staticmethod def get_web_search(additional_tools: list[BaseTool]) -> list[BaseTool]: - # TODO: use ShellInput tools = [TavilySearchResults(max_results=1)] return additional_tools + tools