Prompt Templates in LM-Kit.NET: Dynamic Prompt Generation with Logic
TL;DR
Prompt Templates in LM-Kit.NET are compiled template objects that separate prompt structure from runtime data. Using Mustache-style syntax ({{variable}}), they support variable substitution, conditionals (if/else/unless), loops (each), filters (trim, upper, truncate, etc.), scoping (with), inline defaults, and custom helpers. Templates are parsed once via PromptTemplate.Parse() and rendered many times with different PromptTemplateContext instances, making them efficient for high-throughput production systems. They live in the LMKit.TextGeneration.Prompts namespace.
What Are Prompt Templates?
Definition: A Prompt Template is a reusable text structure with placeholders and logic constructs that produces a final prompt string when rendered with runtime data. Unlike simple string interpolation, prompt templates support:
- Control flow: conditionally include or exclude sections based on variables
- Iteration: loop over collections to generate lists of tools, constraints, or examples
- Transformation: apply filters to format values inline (trim whitespace, change case, truncate)
- Scoping: access nested object properties cleanly
- Extensibility: register custom helpers and global filters
Why Prompt Templates Matter
+--------------------------------------------------------------------------+
| String Concatenation vs. Prompt Templates |
+--------------------------------------------------------------------------+
| |
| WITHOUT TEMPLATES WITH TEMPLATES |
| +---------------------------+ +---------------------------+ |
| | string prompt = | | var t = PromptTemplate | |
| | "You are " + role + | | .Parse("You are {{role}}| |
| | ". " + | | .{{#if tools}}Tools: | |
| | (tools != null | | {{#each tools}} | |
| | ? "Tools:\n" + | | - {{name}} | |
| | string.Join(...) | | {{/each}}{{/if}}"); | |
| | : "") + | | | |
| | "Respond in " + lang; | | string p = t.Render(ctx); | |
| +---------------------------+ +---------------------------+ |
| Fragile, untestable, Clean, testable, versionable |
| hard to maintain separates structure from data |
+--------------------------------------------------------------------------+
Key Characteristics
| Feature | Description |
|---|---|
| Parse once, render many | Template compiles to an AST for fast repeated rendering |
| Three syntaxes | Mustache ({{var}}), Dollar (${var}), Percent (%var%) |
| Block constructs | if/else, unless, each, with (Mustache syntax) |
| Filter chaining | {{name\|trim\|upper\|truncate:20}} |
| Inline defaults | {{language:English}} |
| Variable introspection | template.Variables lists all referenced names |
| Custom helpers | Register functions callable from template expressions |
| Error reporting | PromptTemplateException with line and column info |
Prompt Templates in LM-Kit.NET
Core API
using LMKit.TextGeneration.Prompts;
// Parse a template
var template = PromptTemplate.Parse("Hello {{name|capitalize}}!");
// Inspect variables
IReadOnlyList<string> vars = template.Variables; // ["name"]
// Render with context
var context = new PromptTemplateContext { ["name"] = "alice" };
string result = template.Render(context); // "Hello Alice!"
Template Constructs
// Conditionals
var t = PromptTemplate.Parse(@"
{{#if premium}}Detailed answers.{{#else}}Concise answers.{{/if}}
{{#unless banned}}Use examples.{{/unless}}");
// Loops
var t2 = PromptTemplate.Parse(@"Tools:
{{#each tools}}- {{name}}: {{description}}
{{/each}}");
// Scoping
var t3 = PromptTemplate.Parse("{{#with user}}{{Name}} ({{Email}}){{/with}}");
// Custom helpers
var ctx = new PromptTemplateContext();
ctx.RegisterHelper("now", _ => DateTime.UtcNow.ToString("yyyy-MM-dd"));
Prompt Templates vs. String Interpolation
| Aspect | String Interpolation | Prompt Templates |
|---|---|---|
| Conditionals | Manual if/else in C# | {{#if}} in template |
| Loops | Manual string.Join | {{#each}} in template |
| Testability | Test entire method | Test template + context separately |
| Version control | Embedded in code | Store templates as files |
| Runtime config | Requires recompile | Swap context at runtime |
| Error handling | Runtime exceptions | Parse-time validation |
Key Terms
- Template: A string with placeholders and logic that compiles into a reusable object
- Context: A
PromptTemplateContextholding variable values and helpers - Filter: A pipe-separated transformation applied to a variable (
{{name|upper}}) - Helper: A custom function registered on the context and callable from the template
- AST: The internal compiled representation parsed from the template string
- Strict mode: Throws
PromptTemplateExceptionon missing variables instead of substituting empty strings
Related API Documentation
PromptTemplate: Parse and render templatesPromptTemplateContext: Variables and helpersPromptTemplateFilters: Built-in and custom filtersPromptTemplateOptions: Syntax and strict modePromptTemplateSyntax: Mustache, Dollar, Percent
Related Glossary Topics
- Prompt Engineering: Core prompting techniques that templates automate
- AI Agents: Autonomous systems that benefit from dynamic prompt configuration
- AI Agent Tools: Tool lists that templates can render dynamically
- Context Windows: Token budgets that template filters (
truncate) help manage - Chat Completion: The generation process that consumes rendered templates
External Resources
- Mustache Templates: The template syntax that inspired LM-Kit.NET's default syntax
- Prompt Engineering Guide: Comprehensive prompting techniques
Summary
Prompt Templates in LM-Kit.NET provide a parse-once, render-many template engine for building dynamic prompts. With Mustache-style syntax, they support variables, conditionals, loops, filters, scoping, inline defaults, and custom helpers. Templates separate prompt structure from runtime data, making prompts testable, versionable, and configurable without code changes. They integrate naturally with MultiTurnConversation, AgentBuilder, and all text generation APIs in the SDK.