Prompt Engineering Advanced: Multi-Turn Context, Structured Outputs, and GPT-5.6 Tuning
Part two of Prompt Engineering: multi-turn context management, structured outputs (JSON Schema), few-shot patterns, long-context strategy, and reasoning.effort interaction.
How to
Force structured output with JSON Schema
In the Responses API, set text.format to type='json_schema' with a strict schema; the model guarantees token-level conformance — no regex post-processing.
Design few-shot prompts
Pick 3-5 edge cases, ordered by increasing difficulty; each example includes input and expected output; place them in a user message, not system.
Manage multi-turn context
Static rules in system, conversation keeps only the most recent 5-10 turns; when over threshold, summarize; key decisions explicitly reference history.
Enable Prompt Caching
Repeat prefixes (system instructions, long docs) are billed at cache rates — usually half price — when reused.
Tune reasoning.effort together with prompting
Medium for daily, high for hard; raising effort is usually more effective than lengthening prompts; reach for Sol only last.
Part one of Prompt Engineering covered 8 foundational templates (role, goal, constraint, example, format…). This part goes deeper: multi-turn context, structured outputs, few-shot in practice, long-context strategy, and how it interacts with reasoning.effort. GPT-5.6 plus the Responses API makes these patterns much more reliable than the GPT-4o era.
1. Multi-turn context management
GPT-5.6 has a 1.05M token context window, but you're billed per token and the model's attention weakens the further back it looks. Three tools:
Sliding window
Keep only the most recent N turns (typically 5-10). Simplest, but loses earlier context.
messages = [
{"role": "system", "content": STATIC_INSTRUCTION}, # never changes
*messages[-10:] # keep only last 10 turns
]
Summary compression
Call the model once to summarize older turns into 1-2 paragraphs, then re-inject. Keeps key facts, saves tokens.
def summarize(messages):
summary_resp = client.responses.create(
model="gpt-5.6-luna",
input=messages,
instructions="Summarize the conversation below in 2-3 sentences, keeping decisions, facts, and open questions:"
)
return summary_resp.output_text
# Trigger summarization every N turns
if len(messages) > 20:
summary = summarize(messages[:-10])
messages = [{"role": "system", "content": STATIC_INSTRUCTION},
{"role": "system", "content": f"Conversation summary: {summary}"},
*messages[-10:]]
Static instructions in system
Fixed rules (role, constraints, style) live in system; the conversation only carries the newest turns. System is small, debuggable, and consistent.
Production combo: sliding window + system instructions; add summarization once you exceed 15-20 turns.
2. Structured outputs
GPT-5.6 + the Responses API supports strict JSON Schema enforcement — the model guarantees, token-by-token, that output matches the schema. Format errors become nearly impossible. 100× more robust than regex post-processing.
schema = {
"type": "object",
"properties": {
"intent": {"type": "string", "enum": ["search", "order", "cancel", "other"]},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"qty": {"type": "integer", "minimum": 1}
},
"required": ["name", "qty"],
"additionalProperties": False
}
}
},
"required": ["intent", "confidence"],
"additionalProperties": False
}
response = client.responses.create(
model="gpt-5.6",
input="I'd like two Americanos",
text={"format": {"type": "json_schema", "json_schema": {"schema": schema, "strict": True}}}
)
# response.output_text is guaranteed schema-compliant, no validation needed
Key details:
additionalProperties: False— prevents the model from sneaking in extra onesenumtypes sharply constrain output spacerequiredlists every must-have field explicitly
3. Few-shot in practice
3-5 examples is the sweet spot. Pick edge cases, not the happy path you want — pick the cases the model commonly gets wrong.
[Example 1 — simple positive]
Input: "I'd like a latte"
Output: {"intent": "order", "items": [{"name": "latte", "qty": 1}]}
[Example 2 — modify]
Input: "Make that two"
Output: {"intent": "modify", "items": [{"name": "latte", "qty": 2}]}
[Example 3 — cancel]
Input: "Actually, never mind"
Output: {"intent": "cancel", "items": []}
[Example 4 — chitchat]
Input: "What coffee do you recommend?"
Output: {"intent": "other", "items": []}
[Example 5 — multi-intent]
Input: "One more Americano, plus a sugar"
Output: {"intent": "order", "items": [{"name": "Americano", "qty": 1}, {"name": "sugar", "qty": 1}]}
Order by increasing difficulty; place examples in a user message (not system) so the model treats them as part of the conversation.
4. Long-context strategy
1.05M tokens sounds like a lot until you do 1M × Sol at $5/MTok = $5/call. Three cost levers:
- Prompt Caching: repeated prefixes (system instructions, long docs) bill at cache rate (typically half off)
- Chunking: split long docs, extract with Luna + effort=none, aggregate — about 10× cheaper than stuffing everything in one call
- Layered summary: put the summary at the front, full detail at the back (attention is stronger at both ends); don't rely on the model reading precisely the middle
Enable Prompt Caching:
response = client.responses.create(
model="gpt-5.6",
input=[
{"role": "system", "content": LONG_DOCUMENT}, # cache hit
{"role": "user", "content": "Which products are mentioned in the doc?"}
],
prompt_cache_key="doc-product-overview", # same key → cache hit
prompt_cache_options={"mode": "explicit"}, # explicit / implicit (default)
)
Two modes:
implicit(default): OpenAI decides cache boundaries automaticallyexplicit: you markprompt_cache_breakpointon individual content blocks — more control
Up to 4 cache writes per request, 50 breakpoints considered for reads; keep traffic per key under ~15 requests/min.
5. Interaction with reasoning.effort
reasoning.effort decides how deeply to think; prompting decides what to think about. Tune effort first, prompting second.
| Task type | effort | Prompt complexity |
|---|---|---|
| Classification / extraction | none | minimal (1-2 lines) |
| Everyday conversation | medium | medium (4-8 lines) |
| Complex planning | high | medium (4-8 lines) |
| Multi-step reasoning | xhigh | medium (shorter prompts let the model stretch) |
Experience: bumping effort to high usually beats rewriting the prompt to perfection. When the model is thinking deeper, shorter prompts let it perform — give it clear sub-questions rather than piles of rules.
6. Common errors and troubleshooting
- Multi-turn context overflow → 400 → sliding window + summarize
- JSON parse fails → switch to structured outputs + strict schema; never regex
- Few-shot doesn't take → too few (
<3) or all similar cases; order by difficulty - Long-context quality drops → Prompt Caching + summary first; don't put key info in the middle
- Model answers the wrong thing → raise effort + tighten constraints; don't pile rules into the prompt
7. What's Next
- Prompt Engineering Core Patterns: 8 Templates That 2× GPT Output — part one
- GPT-5.6 Selection Guide: Sol, Terra, Luna
- Function Calling with the OpenAI API: A Complete Guide to Tool Use in the Responses API
Key points
- Multi-turn context: regularly trim early turns, summarize to compress, move static instructions to system, keep a sliding window of the last N turns
- Structured outputs: Responses API + strict JSON Schema — model guarantees field types and required fields; no post-hoc regex needed
- Few-shot: 3-5 edge-case examples outperform 20 similar ones; order by increasing difficulty
- Long context: 1.05M tokens isn't free; use Prompt Caching for repeated prefixes; chunking is usually cheaper than stuffing
- reasoning.effort and prompting are synergistic: tune effort first, then prompt; don't stack effort=max on a one-line instruction
Frequently asked questions
Official references
Related articles
Subscribe to GPTMap Weekly
One email every Monday: curated OpenAI updates, deep dives, and best practices. No ads, unsubscribe anytime.