In LangChain, before formatting a message created with PromptTemplate.from_template
, string inputs need to be escaped (as explained here) to prevent curly braces from being treated as references to nonexistent variables, which would raise key errors.
However, the final prompt string will still include these double curly braces, and depending on the task, this might be confusing to the LLM.
example:
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
from langchain_core.messages import SystemMessage, HumanMessage
template = PromptTemplate.from_template(
"""
{{user}}
Here is your input:
{user_input}
""".strip()
)
def escape_for_prompt_template(text: str) -> str:
return text.replace("{", "{{").replace("}", "}}")
formatted_message = template.format(
user_input=escape_for_prompt_template("example user inputs with {doubled curly braces}")
)
final_prompt = ChatPromptTemplate.from_messages([
HumanMessage(formatted_message),
])
example string output: Here is your input: example user inputs with {{doubled curly braces}}
How is this addressed in LangChain?
Hey Obada,
IIUC, you have a template that you want to only take a single templated variable “user_input”.
If you use the code that you shared without escape_for_prompt_template
, it does exactly this.
- {{user}} in the string is escaped, so this is not treated as a template variable, and in the
formatted_message
, it looks like “{user}”
- The {user_input} is a template variable! And it gets replaced with the user input that you pass in, which is just a string. So if you pass this in “example user inputs with {doubled curly braces}”, the final
formatted_message
is “Hello, {user}!\nHere is your input:\nexample user inputs with {doubled curly braces}”
At this point you have already formatted the template, so formatted_message
is just a string.
LangChain doesn’t automatically handle this - the escaped braces remain in the final prompt sent to the LLM. You have two better approaches: use ChatPromptTemplate
directly with proper variable scoping to avoid needing escapes, or post-process by unescaping after formatting with formatted_message.replace("{{", "{").replace("}}", "}")
. The cleaner solution is restructuring your templates to separate static text from dynamic content, like using system/human message templates where only the human message contains user input, eliminating the need for escaping altogether.
To clarify, with the below code, formatted_message
does not contain any double curly braces, and if you pass formatted_message
to a chat model, it won’t see double curly braces. The {user_input} template variable is set to the input string that is passed in.
from langchain.prompts import PromptTemplate
template = PromptTemplate.from_template(
"""
Hello, {{user}}!
Here is your input:
{user_input}
""".strip()
)
formatted_message = template.format(
user_input="example user inputs with {doubled curly braces}"
)
formatted_message
= “Hello, {user}!\nHere is your input:\nexample user inputs with {doubled curly braces}”
thanks @nhuang @nhuang
I think I figured out what is the issue for me was.
so doing template.format
won’t throw a KeyError
, which makes sense, because the curly braces are used in the input, and there is no need to escape them because LangChain wouldn’t go looking for variables to use in user input.
what is interesting though, is that the error will be thrown when I call final_prompt.format_messages
to get the messages to be passed to the model. which makes sense, because at this point the previous user input as now part of the template being processed by LangChain, and curly braces will be considered variables that the user must provide values for, thus the need for escaping.
however, this problem can be avoided altogether (at least for me) by using messages classes (like HumanMessage
) in ChatPromptTemplate.from_messages
which doesn’t support templates unlike the tuple syntax (("user", "value")
)