agentlane/ docs
On this page

Prompt templating

Prompt code gets hard to maintain when message strings, runtime data, and expected output shape are all mixed together. AgentLane keeps those concerns separate so a prompt can stay readable while each run still provides its own values.

Most prompt flows use PromptTemplate for text messages or MultiPartPromptTemplate when a message needs multiple content parts. PromptSpec is the small object that binds one of those templates to the values for the current run.

A Template Is Structure Plus Values

The usual flow is:

  1. define a template once
  2. pass values later
  3. let the harness or model layer render the final messages

That is what PromptSpec is for. It keeps the prompt definition and the current values together so the rest of the framework can build a request at the right time.

Text Prompts

Use PromptTemplate when your prompt fits naturally into optional system and user messages.

This is the most common case. Template strings are rendered with Jinja, so you can keep the prompt readable and still pass typed values into it.

from typing import TypedDict

from agentlane.models import PromptSpec, PromptTemplate


class IntakeVars(TypedDict):
    clinic_name: str
    question: str


# `output_schema` defaults to plain string output, so instruction-only and
# plain-text prompts can omit it entirely.
template = PromptTemplate[IntakeVars, str](
    system_template="You are helping with {{ clinic_name }} patient intake.",
    user_template="{{ question }}",
)

prompt = PromptSpec(
    template=template,
    values={
        "clinic_name": "Harborview Endocrinology",
        "question": "I feel dizzy after starting a new medication.",
    },
)

Multipart Prompts

Use MultiPartPromptTemplate when the model input needs more than plain text.

That usually means one of two things:

  1. the message content should be built from multiple parts
  2. the prompt should include file or image input alongside text

Multipart templates use TextPart, FilePart, and ImagePart. TextPart renders a template string with values. FilePart and ImagePart carry already-prepared payloads.

from agentlane.models import ImagePart, MultiPartPromptTemplate
from agentlane.models import PromptSpec, TextPart


receipt_image_b64 = "..."

template = MultiPartPromptTemplate[dict[str, str], str](
    system_parts=[TextPart("Summarize the issue shown in the image.")],
    user_parts=[
        TextPart("Customer note: {{ note }}"),
        ImagePart(base64_data=receipt_image_b64, media_type="image/png"),
    ],
)

prompt = PromptSpec(
    template=template,
    values={"note": "The package arrived damaged."},
)

How OutputSchema Fits

Every prompt template carries an OutputSchema. That schema is how the model layer knows whether you expect plain text or structured output, and whether a provider should enforce a JSON schema at request time.

output_schema is optional. When omitted, the template defaults to plain string output (OutputSchema(str)), which renders no structured response_format. Pass an explicit OutputSchema only when the template drives structured output:

from pydantic import BaseModel

from agentlane.models import OutputSchema, PromptTemplate


class Triage(BaseModel):
    urgency: str
    summary: str


structured = PromptTemplate[IntakeVars, Triage](
    system_template="Classify the intake request.",
    output_schema=OutputSchema(Triage),
)

It helps to think of the pieces this way:

  1. the template describes what goes in
  2. the output schema describes what should come back

When To Render Directly

Templates ultimately render through render_messages(...), but most application code does not need to call that method itself.

If you are building on the harness, keep prompts at the PromptSpec level and let the runner build the final request. Reach for direct rendering when you are implementing a lower-level model flow and need the canonical message list immediately.

Rendering System Instruction Text

When you need the system-instruction text a PromptSpec produces — for logging, assertions, or feeding another component — use render_instruction_text instead of re-implementing role filtering over render_messages(...). It renders the spec, keeps only the system-role messages, and joins their plain text:

from agentlane.models import render_instruction_text

system_text = render_instruction_text(prompt)

It raises ValueError if the spec renders no system message or if the system content is not plain text, so a misconfigured prompt fails loudly instead of silently producing an empty string.

  1. Models Overview
  2. Harness Architecture
  3. Harness Runner