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>.
Unlimited Formats and End Detection¶
Some formats can consume an unbounded amount of output:
any_textany_tokenstriggered_tagstoken_triggered_tags
When one of these formats appears inside a tag, the compiler automatically uses the enclosing
end marker as part of the stop condition when the levels match:
string end -> string-level unlimited format
token end -> token-level unlimited format
This is why a format like tag("<think>", any_text, "</think>") can stop cleanly at
</think>. For more details, see Advanced Topics of the Structural Tag.
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 Tool Calling and Reasoning for get_model_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
See Tool Calling and Reasoning for the mapping from get_model_structural_tag to OpenAI Tool Calling Options.
Next Steps¶
For API reference, see Structural Tag API Reference.
For tool calling and reasoning, see Tool Calling and Reasoning.
For automatic end detection details, and deprecated APIs, see Advanced Topics of the Structural Tag.