mirror of
https://github.com/hwchase17/langchain.git
synced 2025-07-08 22:15:08 +00:00
Fix tool arguments formatting in StructuredChatAgent (#10480)
In the `FORMAT_INSTRUCTIONS` template, 4 curly braces (escaping) are used to get single curly brace after formatting: ``` "{{{ ... }}}}" -> format_instructions.format() -> "{{ ... }}" -> template.format() -> "{ ... }". ``` Tool's `args_schema` string contains single braces `{ ... }`, and is also transformed to `{{{{ ... }}}}` form. But this is not really correct since there is only one `format()` call: ``` "{{{{ ... }}}}" -> template.format() -> "{{ ... }}". ``` As a result we get double curly braces in the prompt: ```` Respond to the human as helpfully and accurately as possible. You have access to the following tools: foo: Test tool FOO, args: {{'tool_input': {{'type': 'string'}}}} # <--- !!! ... Provide only ONE action per $JSON_BLOB, as shown: ``` { "action": $TOOL_NAME, "action_input": $INPUT } ``` ```` This PR fixes curly braces escaping in the `args_schema` to have single braces in the final prompt: ```` Respond to the human as helpfully and accurately as possible. You have access to the following tools: foo: Test tool FOO, args: {'tool_input': {'type': 'string'}} # <--- !!! ... Provide only ONE action per $JSON_BLOB, as shown: ``` { "action": $TOOL_NAME, "action_input": $INPUT } ``` ```` --------- Co-authored-by: Sergey Kozlov <sergey.kozlov@ludditelabs.io>
This commit is contained in:
parent
ef7802b325
commit
df03267edf
@ -81,7 +81,7 @@ class StructuredChatAgent(Agent):
|
|||||||
) -> BasePromptTemplate:
|
) -> BasePromptTemplate:
|
||||||
tool_strings = []
|
tool_strings = []
|
||||||
for tool in tools:
|
for tool in tools:
|
||||||
args_schema = re.sub("}", "}}}}", re.sub("{", "{{{{", str(tool.args)))
|
args_schema = re.sub("}", "}}", re.sub("{", "{{", str(tool.args)))
|
||||||
tool_strings.append(f"{tool.name}: {tool.description}, args: {args_schema}")
|
tool_strings.append(f"{tool.name}: {tool.description}, args: {args_schema}")
|
||||||
formatted_tools = "\n".join(tool_strings)
|
formatted_tools = "\n".join(tool_strings)
|
||||||
tool_names = ", ".join([tool.name for tool in tools])
|
tool_names = ", ".join([tool.name for tool in tools])
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
"""Unittests for langchain.agents.chat package."""
|
"""Unittests for langchain.agents.chat package."""
|
||||||
from typing import Tuple
|
from textwrap import dedent
|
||||||
|
from typing import Any, Tuple
|
||||||
|
|
||||||
|
from langchain.agents.structured_chat.base import StructuredChatAgent
|
||||||
from langchain.agents.structured_chat.output_parser import StructuredChatOutputParser
|
from langchain.agents.structured_chat.output_parser import StructuredChatOutputParser
|
||||||
|
from langchain.prompts.chat import (
|
||||||
|
ChatPromptTemplate,
|
||||||
|
HumanMessagePromptTemplate,
|
||||||
|
SystemMessagePromptTemplate,
|
||||||
|
)
|
||||||
from langchain.schema import AgentAction, AgentFinish
|
from langchain.schema import AgentAction, AgentFinish
|
||||||
|
from langchain.tools import Tool
|
||||||
|
|
||||||
output_parser = StructuredChatOutputParser()
|
output_parser = StructuredChatOutputParser()
|
||||||
|
|
||||||
@ -103,3 +111,137 @@ def test_parse_case_matched_and_final_answer() -> None:
|
|||||||
output, log = get_action_and_input(llm_output)
|
output, log = get_action_and_input(llm_output)
|
||||||
assert output == "This is the final answer"
|
assert output == "This is the final answer"
|
||||||
assert log == llm_output
|
assert log == llm_output
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: add more tests.
|
||||||
|
# Test: StructuredChatAgent.create_prompt() method.
|
||||||
|
class TestCreatePrompt:
|
||||||
|
# Test: Output should be a ChatPromptTemplate with sys and human messages.
|
||||||
|
def test_create_prompt_output(self) -> None:
|
||||||
|
prompt = StructuredChatAgent.create_prompt(
|
||||||
|
[Tool(name="foo", description="Test tool FOO", func=lambda x: x)]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert isinstance(prompt, ChatPromptTemplate)
|
||||||
|
assert len(prompt.messages) == 2
|
||||||
|
assert isinstance(prompt.messages[0], SystemMessagePromptTemplate)
|
||||||
|
assert isinstance(prompt.messages[1], HumanMessagePromptTemplate)
|
||||||
|
|
||||||
|
# Test: Format with a single tool.
|
||||||
|
def test_system_message_single_tool(self) -> None:
|
||||||
|
prompt: Any = StructuredChatAgent.create_prompt(
|
||||||
|
[Tool(name="foo", description="Test tool FOO", func=lambda x: x)]
|
||||||
|
)
|
||||||
|
actual = prompt.messages[0].prompt.format()
|
||||||
|
|
||||||
|
expected = dedent(
|
||||||
|
"""
|
||||||
|
Respond to the human as helpfully and accurately as possible. You have access to the following tools:
|
||||||
|
|
||||||
|
foo: Test tool FOO, args: {'tool_input': {'type': 'string'}}
|
||||||
|
|
||||||
|
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 foo
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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:
|
||||||
|
""" # noqa: E501
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
assert actual == expected
|
||||||
|
|
||||||
|
# Test: Format with multiple tools.
|
||||||
|
#
|
||||||
|
# Check:
|
||||||
|
#
|
||||||
|
# You have access to the following tools:
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
# and
|
||||||
|
#
|
||||||
|
# Valid "action" values: "Final Answer" or ...
|
||||||
|
#
|
||||||
|
def test_system_message_multiple_tools(self) -> None:
|
||||||
|
prompt: Any = StructuredChatAgent.create_prompt(
|
||||||
|
[
|
||||||
|
Tool(name="foo", description="Test tool FOO", func=lambda x: x),
|
||||||
|
Tool(name="bar", description="Test tool BAR", func=lambda x: x),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
actual = prompt.messages[0].prompt.format()
|
||||||
|
|
||||||
|
expected = dedent(
|
||||||
|
"""
|
||||||
|
Respond to the human as helpfully and accurately as possible. You have access to the following tools:
|
||||||
|
|
||||||
|
foo: Test tool FOO, args: {'tool_input': {'type': 'string'}}
|
||||||
|
bar: Test tool BAR, args: {'tool_input': {'type': 'string'}}
|
||||||
|
|
||||||
|
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 foo, bar
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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:
|
||||||
|
""" # noqa: E501
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
assert actual == expected
|
||||||
|
Loading…
Reference in New Issue
Block a user