Structural Tag Usage¶
Structural tags describe LLM outputs as a tree of composable format objects. Use them when a plain JSON schema is not enough, such as:
tool calling with model-specific wrappers
reasoning tags like
<think>...</think>responses that mix free-form text with structured fragments
token-level delimiters that are not represented as normal text
Structural tags are also compatible with the OpenAI-style response_format request shape.
Request Shape¶
Pass a structural tag as the response_format:
{
"model": "...",
"messages": [
...
],
"response_format": {
"type": "structural_tag",
"format": {
"type": "...",
...
}
}
}
The format field is required. It contains one format object, and that object can recursively nest other format objects. Each format object represents a “chunk” of text.
Quick Start¶
Minimal tag¶
Use tag when the output must look like begin + content + end.
{
"type": "tag",
"begin": "<think>",
"content": {
"type": "any_text"
},
"end": "</think>"
}
This matches <think>...</think>.
Format Reference¶
Primitive Formats¶
const_string¶
Matches one exact string.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: the output must contain a fixed literal
{
"type": "const_string",
"value": "Let's think step by step."
}
json_schema¶
Matches content that conforms to a JSON Schema.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
|
|
|
Use it when: the structured part is naturally expressed as schema-constrained data
style values:
"json": standard JSON"qwen_xml": Qwen-style XML parameters, such as<parameter=name>value</parameter>"minimax_xml": MiniMax-style XML parameters, such as<parameter name="name">value</parameter>"deepseek_xml": DeepSeek-v3.2 XML parameter format
{
"type": "json_schema",
"json_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
}
Use a non-JSON style only when the surrounding model format expects it:
{
"type": "json_schema",
"json_schema": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
},
"style": "qwen_xml"
}
grammar¶
Matches text with an EBNF grammar.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: JSON Schema is too restrictive or the structure is better described directly as a grammar
{
"type": "grammar",
"grammar": "root ::= (\"yes\" | \"no\")"
}
If the grammar is too broad, it can make later constraints ineffective. Avoid patterns that can consume almost anything when you still need precise structure afterward.
regex¶
Matches text with a regular expression.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: a small local text fragment is easiest to describe with regex
{
"type": "regex",
"pattern": "[A-Z]{3}-[0-9]{4}"
}
As with grammar, avoid overly broad patterns when later structure still needs to be enforced.
any_text¶
Matches arbitrary text.
Field |
Type |
Default |
|---|---|---|
|
|
|
Use it when: the content should remain free-form until some enclosing boundary is reached
{
"type": "any_text",
"excludes": ["<function="]
}
When any_text appears inside a string-level tag, the enclosing end string is automatically
treated as a stop condition.
Composition Formats¶
sequence¶
Matches several formats in order.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: the output is “first this, then that”
{
"type": "sequence",
"elements": [
{
"type": "const_string",
"value": "Answer: "
},
{
"type": "json_schema",
"json_schema": {
"type": "object"
}
}
]
}
or¶
Matches any one of several alternatives.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: multiple shapes are valid at the same position
{
"type": "or",
"elements": [
{
"type": "const_string",
"value": "yes"
},
{
"type": "const_string",
"value": "no"
}
]
}
optional¶
Matches the inner format zero or one time.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: a fragment may be present or omitted
{
"type": "optional",
"content": {
"type": "const_string",
"value": "Optional prefix: "
}
}
plus¶
Matches the inner format one or more times.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: at least one repetition is required
{
"type": "plus",
"content": {
"type": "const_string",
"value": "item"
}
}
star¶
Matches the inner format zero or more times.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: repetition is allowed but not required
{
"type": "star",
"content": {
"type": "const_string",
"value": "x"
}
}
repeat¶
Matches the inner format between min and max times, inclusive. Use max: -1 for an unbounded
upper limit.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
|
|
(required, |
|
|
(required) |
Use it when: repetition needs an explicit range
{
"type": "repeat",
"min": 1,
"max": 3,
"content": {
"type": "const_string",
"value": "item"
}
}
Tagging and Dispatch Formats¶
tag¶
Matches begin + content + end.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
|
|
(required) |
|
|
(required) |
Use it when: one region is wrapped by known delimiters
{
"type": "tag",
"begin": "<response>",
"content": {
"type": "json_schema",
"json_schema": {
"type": "object"
}
},
"end": ["</response>", "</answer>"]
}
dispatch¶
Allows free-form text, but when a specified pattern string appears, the following output must conform to the corresponding format. Uses Aho-Corasick matching internally for efficient multi-pattern detection.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
|
|
|
|
|
|
Use it when: you need pattern-triggered structured regions inside free-form text, with string-level pattern matching
{
"type": "dispatch",
"rules": [
["<function=func1>", {"type": "json_schema", "json_schema": ...}],
["<function=func2>", {"type": "json_schema", "json_schema": ...}]
],
"loop": true,
"excludes": ["</end>"]
}
To end matching at a specific string, put it in excludes and follow the dispatch with a
const_string in a sequence, or use the end of an enclosing tag:
{
"type": "tag",
"begin": "<response>",
"content": {
"type": "dispatch",
"rules": [
["<function=func1>", {"type": "json_schema", "json_schema": ...}],
["<function=func2>", {"type": "json_schema", "json_schema": ...}]
],
"loop": true,
"excludes": ["</response>"]
},
"end": "</response>"
}
Here dispatch allows free text and tool calls inside <response>...</response>, but
stops when </response> appears because it is in excludes. The enclosing tag then
consumes that end delimiter.
Token-level Formats¶
token¶
Matches one token by token ID or token string.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
Use it when: the delimiter is a tokenizer-level symbol rather than normal text
{"type": "token", "token": 42}
{"type": "token", "token": "<|tool_call|>"}
When token is a string, it is resolved with tokenizer_info.
exclude_token¶
Matches any single token except those in exclude_tokens.
Field |
Type |
Default |
|---|---|---|
|
|
|
Use it when: one token is needed, but some token values must be blocked
{"type": "exclude_token", "exclude_tokens": [42, "</s>"]}
When used inside a token-level tag, the enclosing end token is automatically excluded.
any_tokens¶
Matches zero or more tokens, excluding those in exclude_tokens.
Field |
Type |
Default |
|---|---|---|
|
|
|
Use it when: content should remain unconstrained until a token-level boundary is reached
{"type": "any_tokens", "exclude_tokens": ["<|eos|>"]}
Semantically, any_tokens is equivalent to star(exclude_token(...)).
token_dispatch¶
Token-level version of dispatch. Patterns are token IDs or token strings instead of text
strings.
Field |
Type |
Default |
|---|---|---|
|
|
(required) |
|
|
|
|
|
|
Use it when: dispatch must happen on special tokens rather than text patterns
{
"type": "token_dispatch",
"rules": [
[100, {"type": "const_string", "value": "x"}],
["<|tool|>", {"type": "json_schema", "json_schema": ...}]
],
"loop": true,
"exclude_tokens": ["</s>"]
}
Common Recipes¶
Force Exactly One Tool Call¶
To require exactly one tool call from a tool set, use triggered_tags with both control flags:
{
"type": "triggered_tags",
"triggers": ["<function="],
"tags": [
{
"type": "tag",
"begin": "<function=func1>",
"content": {"type": "json_schema", "json_schema": ...},
"end": "</function>"
},
{
"type": "tag",
"begin": "<function=func2>",
"content": {"type": "json_schema", "json_schema": ...},
"end": "</function>"
}
],
"at_least_one": true,
"stop_after_first": true
}
If the function is fixed in advance, a single tag is simpler.
Mix Reasoning, Free Text, and Tool Calls¶
Use sequence to force an initial reasoning region, then allow later tool calls inside normal text:
{
"type": "sequence",
"elements": [
{
"type": "tag",
"begin": "<think>",
"content": {"type": "any_text"},
"end": "</think>"
},
{
"type": "triggered_tags",
"triggers": ["<function="],
"tags": [
{
"type": "tag",
"begin": "<function=func1>",
"content": {"type": "json_schema", "json_schema": ...},
"end": "</function>"
},
{
"type": "tag",
"begin": "<function=func2>",
"content": {"type": "json_schema", "json_schema": ...},
"end": "</function>"
}
]
}
]
}
Token-level Delimiters¶
Some models use special tokens instead of literal strings to start or end a structured region.
Combine tag, any_tokens, and token_triggered_tags for that case:
{
"type": "sequence",
"elements": [
{
"type": "tag",
"begin": {"type": "token", "token": "<|think_start|>"},
"content": {"type": "any_tokens"},
"end": {"type": "token", "token": "<|think_end|>"}
},
{
"type": "token_triggered_tags",
"trigger_tokens": ["<|tool_call_start|>"],
"tags": [
{
"type": "tag",
"begin": {"type": "token", "token": "<|tool_call_start|>"},
"content": {"type": "json_schema", "json_schema": ...},
"end": {"type": "token", "token": "<|tool_call_end|>"}
}
],
"exclude_tokens": ["<|eos|>"],
"at_least_one": true
}
]
}
Built-in Model Styles¶
If you only need a standard tool-calling layout for a supported model family, prefer the built-in helper instead of hand-writing every wrapper:
Llama / Gemma-style text wrappers
Qwen and Qwen Coder variants
Kimi
DeepSeek variants
Harmony
MiniMax
See Advanced Topics of the Structural Tag for
get_builtin_structural_tag and the list of supported models.
Mapping to OpenAI Tool Calling Options¶
Structural tags are flexible enough to implement the strict-format parts of the OpenAI tool-calling API. The exact wrapper still depends on the target model’s syntax, but the control knobs map cleanly:
tool_choice = "auto": usetriggered_tagswithat_least_one: falsetool_choice = "required": useTagsWithSeparatorFormatorOrFormat.tool_choice = {"type": "function", "function": {"name": ...}}: use a fixedtagformat to describe the tool-calling format.parallel_tool_calls = false: setstop_after_first: trueparallel_tool_calls = true: keepstop_after_first: false, or usetags_with_separatorif the model expects a pure separated list of calls
Next Steps¶
For API reference, see Structural Tag API Reference.
For built-in helpers, automatic end detection details, and deprecated APIs, see Advanced Topics of the Structural Tag.