Jinja Templates

Key Takeaways

  • Jinja is a text templating language originally built for Python, widely used across web frameworks, DevOps tooling (Ansible), and now agent recipes.
  • A Jinja template mixes static text with substitution and control expressions like {{ variable }} and {% for item in items %}, rendered against a context at runtime.
  • Guild honors Jinja in Goose recipe files. The instructions and prompt fields in a recipe.yaml are Jinja templates rendered against the recipe's parameters.
  • For most recipe authoring, you only need the basic {{ variable }} substitution. The template renders once when the agent starts a session with its parameters bound.

What Is Jinja?

Jinja is a general-purpose text templating engine that came out of the Python ecosystem. It takes a template (any text with special expressions in it) plus a context (the values those expressions should resolve to) and produces rendered text. If you have written a Python web app with Flask, an Ansible playbook, or a docs site with Pelican, you have almost certainly touched Jinja.

The core idea is straightforward. Static text stays static. Anything wrapped in {{ }} gets substituted with a value. Anything wrapped in {% %} runs a control expression (a loop, a conditional, an include). The template is inert until it is rendered; rendering is where the substitution happens.

Jinja has become the de facto templating language for a lot of tools built on Python, and its syntax has spread to non-Python worlds (Nunjucks in JS, tera in Rust) because it is simple to read and expressive without being programming-language-heavy.

Jinja Basics

Variable substitution

{{ name }} in a template becomes the value of name in the context. {{ user.email }} accesses a property. {{ items | length }} runs a filter (the number of items in a list).

Control expressions

{% if condition %} ... {% endif %} conditionally renders a section. {% for item in items %} ... {% endfor %} loops. {% set x = 42 %} assigns a local variable. Templates can also include other templates and inherit from base templates.

Filters and tests

Filters transform values (| upper, | length, | default("hi")). Tests check conditions (is defined, is none). Both make templates readable without dropping into full code.

Jinja in Guild's Goose Recipes

Guild supports Goose recipes as one of its three agent-type scaffolds. Inside a recipe.yaml, the instructions and prompt fields are Jinja templates. When an agent starts a session, Guild renders those templates against the recipe's declared parameters and sends the result to the LLM as the system prompt (from instructions) and the initial message (from prompt).

A typical parameterized recipe looks like this:

description: Review code with a configurable focusinstructions: You are a {{ language }} reviewer focused on {{ focus }}.prompt: Review the code provided below.parameters:  - key: language    input_type: select    requirement: required    options: [python, typescript]  - key: focus    input_type: string    requirement: optional    default: best practices

When the agent runs, {{ language }} and {{ focus }} are substituted with the values the user supplies (or the declared defaults). Same recipe, many behaviors, one declarative file.

Because Jinja rendering happens at recipe load time, the substitution is invisible to the LLM. The model sees a normal prompt. The parameterization lives in the recipe author's editor, not in the runtime.

Why Jinja Matters for Agent Recipes

Parameters make one recipe cover many use cases

Without templating, a code-review recipe would either be hard-coded for a specific language or force the recipe author to write branching logic in code. With Jinja parameters, one recipe covers TypeScript, Python, Go, and anything else you want to pass in.

Recipes stay declarative

Jinja lets recipes stay YAML-plus-text rather than requiring recipe authors to write TypeScript. That is the whole point of choosing a Goose-recipe agent over a coded agent: portable, declarative, and easy to review.

The syntax is familiar

Anyone who has touched a Python web framework or Ansible has already read Jinja. That lowers the bar for someone contributing a recipe versus asking them to learn a new DSL.

Key Considerations

Only instructions and prompt are Jinja

In a Goose recipe on Guild, the fields that get rendered as Jinja templates are the ones Guild sends to the LLM as text (instructions and prompt). Other fields (description, parameters, response.json_schema) are plain YAML.

Missing variables fail loudly

If a template references {{ focus }} and no focus is passed and no default is declared, Jinja raises an error at render time. Declare defaults on optional parameters or make them required.

Keep templates simple when you can

Jinja supports loops, conditionals, filters, macros, and template inheritance. For most recipe prompts, {{ variable }} substitution is all you need. Reach for more elaborate constructs only when the recipe genuinely calls for them.

The full Jinja documentation is the canonical reference for the syntax and features.

The Future We're Building at Guild

Guild is a control plane for AI agents, a place to build, deploy, and govern the agents your teams run in production. Jinja is what makes Goose recipes on Guild parameterizable without dropping into code.

Build, deploy, and govern AI agents on Guild.

The operating system for enterprise AI agents. Sign in and start building in minutes.

FAQs

Jinja is a general-purpose text templating engine that originated in Python. It renders text templates with expressions like {{ variable }} against a context of values, producing final text.

Guild honors Jinja syntax in Goose recipes. The instructions and prompt fields of a recipe.yaml are rendered as Jinja templates against the recipe's declared parameters when an agent starts a session.

No. Basic {{ variable }} substitution covers most recipe authoring. Reach for loops, conditionals, and filters only when a specific recipe genuinely calls for them.

Jinja raises an error at render time. Declare defaults on optional parameters, or make them required so the CLI prompts the user for a value.

The canonical Jinja documentation at jinja.palletsprojects.com is the authoritative reference.