| Format | Syntax | Best for |
|---|---|---|
| f-string | {variable} | Simple prompts with basic variable substitution |
| mustache | {{variable}} | Complex prompts with loops, conditionals, nested data, or evaluators |
Use the prompt playground to test out the examples on this page. Switch the Prompt format under the prompt settings menu in the Playground.
F-string syntax
F-string templates use Python-style formatting with single curly braces{variable}. LangSmith uses a simplified subset of Python’s f-string syntax—it only supports basic variable substitution, not the full range of Python expressions and formatting options. When you have a flat data structure and only need to insert values into your prompt, f-strings are ideal.
Basic variables
Variables are replaced with their values from the input data. Variable names must match exactly (case-sensitive):{name} with the value of the name key.
Variable names
F-string variable names are treated as simple string identifiers. They cannot contain dots, brackets, or special characters—just alphanumeric characters and underscores.{"user": {"name": "Ashley"}}, you cannot access the nested value with {user.name} in f-string format. The dot would be treated as part of the variable name (literally looking for a key called "user.name"), not as a path separator. For nested access, use mustache format instead.
Literal braces
Sometimes you need to include actual curly braces in your output (for example, in JSON examples or code snippets). To do this, double the braces:{{ as an escaped brace, not a variable placeholder. Only single braces {...} are treated as variables.
Limitations
LangSmith’s f-string implementation is limited to keep templates simple and predictable. The following features are not supported:- Dot notation for nested access: Cannot use
{user.name}to access nested objects. The entire string"user.name"would be treated as a single variable name. - Format specifiers: Cannot use
{price:.2f}for number formatting or{rate:.1%}for percentages. - Expressions: Cannot use
{x + y},{len(items)}, or{value if condition else default}. - Function calls: Cannot use
{str.upper()}or other method calls. - Loops or conditionals: No control flow structures.
- Array indexing: Cannot use
{items[0]}to access array elements.
Mustache syntax
Mustache is a “logic-less” templating language, meaning it doesn’t allow arbitrary code execution but does provide structured control flow through sections. It’s called “logic-less” because you can’t write complex expressions—instead, you structure your data to control what renders. Mustache is designed for complex data structures and dynamic rendering. It’s essential for:- Evaluators: Processing thread histories and conversation context.
- Few-shot prompting: Iterating over example lists.
- Nested data: Accessing deeply nested objects and arrays.
- Conditional content: Showing different text based on data presence.
{{variable}} distinguishes it from f-strings.
Basic variables
Like f-strings, mustache replaces variables with their values:{{!-- ... --}} is a mustache comment and won’t appear in the output. Refer to the Comments section.Nested object access
You can traverse nested objects using dot notation:user → profile → email through your data structure. Each dot represents one level of nesting.
Real-world data is often nested (for example: API responses, database records, etc.). Mustache lets you work with this data naturally without flattening it first.
Sections
Sections are mustache’s core feature. A section starts with{{#name}} and ends with {{/name}}. What happens inside depends on the value:
- Array: Repeats the content for each element.
- Object: Renders once with that object as context.
- Truthy value: Renders once.
- Falsy value (false, null, undefined, empty array): Doesn’t render.
{{#items}} iterates over the items array. For each iteration, the variables inside the section (like {{name}} and {{price}}) are resolved against the current array element:
Nested loops
You can nest sections to handle multi-level data structures:{{#categories}} sets the context to each category object. Inside that context, {{name}} refers to the category name, and the inner section {{#products}} iterates over that category’s products.
Use nested loops when your data has hierarchical relationships—categories with products, departments with employees, or conversation threads with multiple exchanges.
Array elements by index
Sometimes you need a specific element rather than looping. Use dot notation with numeric indices:{{all_messages.0}} for the first message or pre-calculate the last message in your data.
Conditionals
You can use sections as conditionals. They only render if the value exists, is non-empty, and is notfalse:
{{#user}} checks if user exists and is truthy. If so, it renders the content inside with user as the context (so {{name}} looks for name inside user).
Show optional content like “Welcome back” messages only when user data is available, or display error messages only when errors exist.
Inverted sections
Inverted sections render only when a value doesn’t exist, is false, null, undefined, or an empty array. Inverted sections are commonly used to handle empty states, such as missing data or empty lists. In the following example:{{#results}}iterates over each result and renders one line per item.{{^results}}renders only when the results array is empty or missing.- The inverted section provides a clear fallback when there are no results to display.
{{#username}} renders only if username exists. The inverted section {{^username}} renders only if it doesn’t. Together, they create an if/else branch. This is useful for personalizing prompts when user data is optional or showing default instructions when custom ones aren’t provided:
Comments
Comments document your templates without affecting output. Use{{! comment }} or {{!-- comment --}}:
Special variables for evaluators and threads
When building evaluators or working with conversational AI, LangSmith automatically provides special variables that structure conversation data in useful ways. These variables are only available in evaluator contexts, not in regular playground prompts. Evaluators need to analyze conversations holistically—looking at patterns across multiple messages, comparing the first question to the final answer, or examining how well the AI responds to follow-up questions. These variables make it easy to access conversation structure without manual data manipulation.Thread message variables
LangSmith provides three pre-structured views of conversation threads:all_messages: Every message in chronological order withrole(user/assistant/system) andcontentfields. Use this to show the full conversation flow.human_ai_pairs: Messages grouped into question-answer pairs. Each pair hashuman(user message) andai(assistant response). Use this when evaluating response quality.first_human_last_ai: Just the initial question (first_human) and final answer (last_ai). Use this to check if the AI ultimately answered the original question, ignoring the middle conversation.
Example with thread context
The following example is a practical evaluator prompt that uses thread context:{{#all_messages}} to loop over the conversation array. For each iteration, the section sets the context to that message object, so {{role}} and {{content}} access the properties of the current message. The loop automatically iterates through all four messages in order, displaying each as "role: content". This gives the evaluator LLM the full conversation history to assess helpfulness.
When you create an evaluator in LangSmith, select which thread variables you want to include. LangSmith will automatically populate them from the conversation being evaluated.
Few-shot examples
Few-shot prompting teaches the LLM by example. You provide several input-output pairs demonstrating the task, then ask it to perform the same task on new input. Few-shot examples help the LLM understand:- Format expectations (e.g., “respond with JSON” or “use this tone”)
- Edge cases (e.g., how to handle ambiguous input)
- Task nuances (e.g., the difference between “positive” and “very positive” sentiment)
Few-shot placeholder
In LangSmith, use the{{few_shot_examples}} placeholder where you want your examples to appear:
{{few_shot_examples}} placeholder. This keeps your prompt template clean and lets you manage examples independently.
Example output with configured examples:
Conversion between formats
F-string to mustache always works for basic variables. Format specifiers are converted, but the formatting is removed. Mustache to f-string only works for basic variables. Mustache features like dot notation, sections, conditionals, and comments have no equivalent in f-strings and cannot be converted:- Dot notation:
{{user.name}}F-strings would treat"user.name"as a single variable name rather than nested access. - Sections/loops:
{{#items}}...{{/items}}No equivalent in f-strings. - Conditionals:
{{#value}}...{{/value}}No equivalent in f-strings. - Inverted sections:
{{^value}}...{{/value}}No equivalent in f-strings. - Comments:
{{! comment }}No equivalent in f-strings.
Additional resources
- LangSmith Prompt Engineering Concepts: Higher-level guidance on effective prompting strategies.
- Mustache Manual: Full mustache specification with all features.
- Python f-string Documentation: Official Python f-string syntax (note: LangSmith uses a simplified subset).