mirror of
https://github.com/hwchase17/langchain.git
synced 2025-07-08 14:05:16 +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:
|
||||
tool_strings = []
|
||||
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}")
|
||||
formatted_tools = "\n".join(tool_strings)
|
||||
tool_names = ", ".join([tool.name for tool in tools])
|
||||
|
@ -1,8 +1,16 @@
|
||||
"""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.prompts.chat import (
|
||||
ChatPromptTemplate,
|
||||
HumanMessagePromptTemplate,
|
||||
SystemMessagePromptTemplate,
|
||||
)
|
||||
from langchain.schema import AgentAction, AgentFinish
|
||||
from langchain.tools import Tool
|
||||
|
||||
output_parser = StructuredChatOutputParser()
|
||||
|
||||
@ -103,3 +111,137 @@ def test_parse_case_matched_and_final_answer() -> None:
|
||||
output, log = get_action_and_input(llm_output)
|
||||
assert output == "This is the final answer"
|
||||
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