Prompt templates

Use prompt templates to inject dynamic context, user identity, or other runtime information into your LLM prompts. Agentgateway supports both static template patterns (prepend/append) and dynamic variable-based templating using CEL expressions.

About prompt templates

Prompt templates allow you to standardize prompts across your organization with consistent instructions, inject dynamic context such as user identity or JWT claims, customize behavior per user, and add metadata like request IDs for tracking.

Unlike simple {{variable}} substitution systems, agentgateway uses CEL (Common Expression Language) expressions. This gives you full expression logic including conditionals, functions, and complex transformations.

Templating approaches

Agentgateway provides two complementary approaches to prompt templating.

ApproachUse CaseExample
Static templatesFixed prompts that apply to all requests“Answer all questions in French.”
Dynamic templatesVariable injection from JWT claims, headers, or request context“You are assisting user {jwt.sub} from organization {jwt.org}.”

You can use these approaches individually or combine them for maximum flexibility.

Before you begin

  1. Set up an agentgateway proxy.
  2. Set up access to the OpenAI LLM provider.

Static prompt templates

Static templates use prompt enrichment to prepend or append fixed messages to every request. This is ideal for setting consistent behavior guidelines, adding organizational policies, or defining output formats.

  1. Create an AgentgatewayPolicy resource with static prompt enrichment.

    kubectl apply -f- <<EOF
    apiVersion: agentgateway.dev/v1alpha1
    kind: AgentgatewayPolicy
    metadata:
      name: static-prompt-template
      namespace: agentgateway-system
    spec:
      targetRefs:
      - group: gateway.networking.k8s.io
        kind: HTTPRoute
        name: openai
      backend:
        ai:
          prompt:
            prepend:
            - role: system
              content: "You are a helpful customer service assistant. Always be polite and professional."
            append:
            - role: system
              content: "If you cannot answer a question, say so clearly rather than making up information."
    EOF
  2. Send a request without system prompts. The static template is automatically applied.

    curl "$INGRESS_GW_ADDRESS/openai" -H content-type:application/json -d '{
      "model": "gpt-3.5-turbo",
      "messages": [
        {
          "role": "user",
          "content": "How do I return a product?"
        }
      ]
    }' | jq -r '.choices[].message.content'
    curl "localhost:8080/openai" -H content-type:application/json -d '{
      "model": "gpt-3.5-turbo",
      "messages": [
        {
          "role": "user",
          "content": "How do I return a product?"
        }
      ]
    }' | jq -r '.choices[].message.content'

    The response follows the prepended and appended guidelines even though they were not in the original request.

Dynamic prompt templates

Dynamic templates use CEL transformations to inject variables from the request context into prompts. This is ideal for personalizing prompts with user identity, adding request metadata, or applying conditional prompt modification based on headers or claims.

ℹ️
JWT claims in transformations require JWT authentication to be configured. See the JWT authentication guide for setup instructions.

Inject user identity from headers

  1. Create an AgentgatewayPolicy resource that injects user identity from request headers.

    kubectl apply -f- <<EOF
    apiVersion: agentgateway.dev/v1alpha1
    kind: AgentgatewayPolicy
    metadata:
      name: dynamic-prompt-template
      namespace: agentgateway-system
    spec:
      targetRefs:
      - group: gateway.networking.k8s.io
        kind: HTTPRoute
        name: openai
      traffic:
        transformation:
          request:
            body: |
              json(request.body).with(body,
                {
                  "model": body.model,
                  "messages": [{"role": "system", "content": "You are assisting user: " + default(request.headers["x-user-id"], "anonymous")}]
                    + body.messages
                }
              ).toJson()
    EOF
  2. Send a request with a user ID header.

    curl "$INGRESS_GW_ADDRESS/openai" -H content-type:application/json -H "x-user-id: alice" -d '{
      "model": "gpt-3.5-turbo",
      "messages": [
        {
          "role": "user",
          "content": "What are my recent orders?"
        }
      ]
    }' | jq -r '.choices[].message.content'
    curl "localhost:8080/openai" -H content-type:application/json -H "x-user-id: alice" -d '{
      "model": "gpt-3.5-turbo",
      "messages": [
        {
          "role": "user",
          "content": "What are my recent orders?"
        }
      ]
    }' | jq -r '.choices[].message.content'

    The request body includes a system message: "You are assisting user: alice".

Available CEL variables for templating

You can use these variables in your CEL transformation expressions.

VariableDescriptionExample
request.headers["name"]Request header valuesrequest.headers["x-user-id"]
request.pathRequest pathrequest.path returns /openai
request.methodHTTP methodrequest.method returns POST
jwt.subJWT subject claimjwt.sub returns "user123"
jwt.issJWT issuer claimjwt.iss returns "https://auth.example.com"
jwt.audJWT audience claimjwt.aud returns "api://myapp"
jwt['custom-claim']Custom JWT claimsjwt['org-id'] returns custom claim value

For a complete list of available variables and functions, see the CEL reference documentation.

Common templating patterns

User context from JWT claims

⚠️
JWT claims are not currently available in CEL transformations when using mcpAuthentication. This is tracked in agentgateway issue #870. Use jwtAuthentication in the traffic policy instead.

Inject user identity and organization from JWT claims into the prompt.

traffic:
  transformation:
    request:
      body: |
        json(request.body).with(body,
          {
            "model": body.model,
            "messages": [
              {
                "role": "system",
                "content": "You are assisting " + jwt.sub + " from organization " + jwt['org-id'] + ". Tailor responses to their role: " + default(jwt.role, "user") + "."
              }
            ] + body.messages
          }
        ).toJson()

Conditional templates based on headers

Route premium users to enhanced instructions.

traffic:
  transformation:
    request:
      body: |
        json(request.body).with(body,
          request.headers["x-user-tier"] == "premium" ?
            {
              "model": body.model,
              "messages": [{"role": "system", "content": "Provide detailed, comprehensive answers with examples."}] + body.messages
            } :
            {
              "model": body.model,
              "messages": [{"role": "system", "content": "Provide concise, brief answers."}] + body.messages
            }
        ).toJson()

Add request tracking metadata

Inject request ID and timestamp for debugging.

traffic:
  transformation:
    request:
      body: |
        json(request.body).with(body,
          {
            "model": body.model,
            "messages": [
              {
                "role": "system",
                "content": "Request ID: " + uuid() + " | Timestamp: " + string(request.startTime)
              }
            ] + body.messages
          }
        ).toJson()

Combine static and dynamic templates

Use prompt enrichment for static guidelines and transformations for dynamic context.

# Static guidelines via prompt enrichment
backend:
  ai:
    prompt:
      prepend:
      - role: system
        content: "You are a helpful assistant. Always be polite."
      append:
      - role: system
        content: "If uncertain, say so clearly."

# Dynamic user context via transformation
traffic:
  transformation:
    request:
      body: |
        json(request.body).with(body,
          {
            "model": body.model,
            "messages": body.messages + [
              {
                "role": "system",
                "content": "User context: " + default(request.headers["x-user-id"], "anonymous")
              }
            ]
          }
        ).toJson()

This applies both static prompts (prepend/append) and dynamic user context (from headers).

Cleanup

You can remove the resources that you created in this guide.
kubectl delete AgentgatewayPolicy static-prompt-template dynamic-prompt-template -n agentgateway-system

Next steps

Agentgateway assistant

Ask me anything about agentgateway configuration, features, or usage.

Note: AI-generated content might contain errors; please verify and test all returned information.

Tip: one topic per conversation gives the best results. Use the + button in the chat header to start a new conversation.

Switching topics? Starting a new conversation improves accuracy.
↑↓ navigate select esc dismiss

What could be improved?

Your feedback helps us improve assistant answers and identify docs gaps we should fix.

Need more help? Join us on Discord: https://discord.gg/y9efgEmppm

Want to use your own agent? Add the Solo MCP server to query our docs directly. Get started here: https://search.solo.io/.