Wizard Config Builder¶
The WizardConfigBuilder provides a fluent, programmatic API for creating wizard configurations. It complements the YAML-based approach by enabling code-driven wizard generation with built-in validation, serialization, and full roundtrip compatibility with WizardConfigLoader.
Overview¶
| Component | Class | Purpose |
|---|---|---|
| Builder | WizardConfigBuilder |
Fluent API for constructing wizard configs |
| Config | WizardConfig |
Immutable, validated wizard configuration |
| Stage | StageConfig |
Configuration for a single wizard stage |
| Transition | TransitionConfig |
Configuration for a stage-to-stage transition |
| Intent Detection | IntentDetectionConfig |
Intent detection settings for conversation stages |
| Context Generation | ContextGenerationConfig |
LLM-generated context variable settings |
Quick Start¶
Minimal Wizard¶
from dataknobs_bots.config import WizardConfigBuilder
wizard = (
WizardConfigBuilder("onboarding")
.add_structured_stage("welcome", "What is your name?", is_start=True)
.add_end_stage("done", "All set!")
.add_transition("welcome", "done")
.build()
)
wizard.to_file("configs/wizards/onboarding.yaml")
Conversation-Start Pattern¶
For the common single-conversation-stage pattern:
from dataknobs_bots.config import WizardConfigBuilder
wizard = (
WizardConfigBuilder.conversation_start(
name="assistant",
prompt="You are a helpful assistant.",
tools=["knowledge_search", "web_search"],
tool_reasoning="react",
max_tool_iterations=5,
)
.build()
)
Multi-Stage Wizard¶
from dataknobs_bots.config import WizardConfigBuilder
wizard = (
WizardConfigBuilder("survey")
.set_version("2.0.0")
.set_description("Customer feedback survey")
.set_settings(
tool_reasoning="react",
max_tool_iterations=3,
auto_advance_filled_stages=True,
)
.add_conversation_stage(
name="chat",
prompt="Welcome! Let's collect your feedback.",
tools=["knowledge_search"],
is_start=True,
suggestions=["Tell me about your experience", "I have a complaint"],
intent_detection={
"method": "keyword",
"intents": [
{"id": "start_survey", "keywords": ["survey", "feedback"]},
],
},
)
.add_structured_stage(
name="rating",
prompt="How would you rate our service? (1-5)",
schema={"type": "object", "properties": {"score": {"type": "integer"}}},
help_text="Enter a number from 1 to 5.",
suggestions=["1", "2", "3", "4", "5"],
)
.add_end_stage("thanks", "Thank you for your feedback!")
.add_transition("chat", "rating", condition="data.get('_intent') == 'start_survey'")
.add_transition("rating", "thanks")
.build()
)
Builder API¶
Constructor¶
Creates a builder with defaults: version "1.0", empty description, no settings.
Metadata Methods¶
| Method | Description |
|---|---|
set_version(version) |
Set the wizard version string |
set_description(description) |
Set a human-readable description |
Settings¶
builder.set_settings(
tool_reasoning="react",
max_tool_iterations=3,
auto_advance_filled_stages=True,
extraction_scope="current_message",
conflict_strategy="latest_wins",
extraction_grounding=True,
extraction_hints={"enum_normalize": True, "normalize_threshold": 0.7},
timeout_seconds=300,
scope_escalation={
"enabled": True,
"escalation_scope": "wizard_session",
"recent_messages_count": 3,
},
)
Settings are passed through to the wizard runtime. Common keys include tool_reasoning, max_tool_iterations, auto_advance_filled_stages, extraction_scope, conflict_strategy, extraction_grounding, grounding_overlap_threshold, merge_filter, extraction_hints, timeout_seconds, ephemeral_keys, store_trace, verbose, and scope_escalation.
Extraction Scope¶
The extraction_scope setting controls what context the extraction model receives:
"wizard_session"(default): All user messages in the wizard session — comprehensive but heavier."recent_messages": The last N user messages, controlled byscope_escalation.recent_messages_count(default 3). A middle ground between session and current message."current_message": Only the user's latest message — fast and focused.
Scope Escalation¶
When extraction_scope is "current_message", the extraction model may miss fields that were mentioned in earlier turns. Scope escalation automatically retries with a broader scope when required fields are still missing after the initial extraction:
builder.set_settings(
extraction_scope="current_message",
scope_escalation={
"enabled": True, # Default: False
"escalation_scope": "wizard_session", # Or: "recent_messages"
"recent_messages_count": 3, # For recent_messages scope
},
)
Escalation only fires when (a) required fields are missing, (b) the current scope is narrower than the target, and © there is prior conversation history to draw on. The grounding filter protects existing data during escalated re-extraction.
The store_trace and verbose settings propagate to ReAct stages. Both support per-stage overrides (set directly on the stage dict) that take precedence over the wizard-level default.
Use ephemeral_keys to declare data keys that should not be persisted to storage
(e.g., per-step display data, intermediate computation results):
Extraction Grounding Settings¶
| Key | Type | Default | Description |
|---|---|---|---|
extraction_grounding |
bool |
True |
Enable schema-driven grounding checks. When enabled, extracted values must be grounded in the user's message before they can overwrite existing data. |
grounding_overlap_threshold |
float |
0.5 |
Minimum word-overlap ratio for string grounding (0.0--1.0). |
merge_filter |
str \| None |
None |
Dotted import path to a custom MergeFilter class. Composes with the built-in grounding check (grounding runs first, then the custom filter). |
skip_builtin_grounding |
bool |
False |
When True and a merge_filter is set, bypass the built-in grounding check entirely — only the custom filter runs. |
extraction_hints |
dict |
{} |
Class-level extraction hints. enum_normalize (default true): normalize extracted enum values to canonical entries. normalize_threshold (default 0.7): fuzzy match threshold. reject_unmatched (default true): reject enum values that don't match any entry. boolean_recovery (default true): enable signal-word recovery for boolean fields that extraction fails to fill; only active when "boolean_recovery" is in the recovery pipeline. |
See Extraction Grounding and Enum Normalization for full documentation.
Stage Methods¶
add_conversation_stage()¶
Adds a mode: conversation stage for free-form chat with optional tool access and intent detection.
builder.add_conversation_stage(
name="chat",
prompt="Let's discuss your project.",
tools=["knowledge_search"],
is_start=True,
suggestions=["Tell me more", "What options do I have?"],
intent_detection={
"method": "keyword",
"intents": [
{"id": "switch_topic", "keywords": ["change", "different"]},
],
},
)
Parameters:
| Parameter | Type | Description |
|---|---|---|
name |
str |
Stage identifier |
prompt |
str |
User-facing prompt |
tools |
list[str] \| None |
Available tool names |
is_start |
bool |
Whether this is the start stage |
suggestions |
list[str] \| None |
Quick-reply suggestions |
intent_detection |
dict \| None |
Intent detection config (method + intents) |
**kwargs |
Any |
Additional StageConfig fields |
add_structured_stage()¶
Adds a data-collection stage with optional JSON Schema validation.
builder.add_structured_stage(
name="collect_name",
prompt="What is your full name?",
schema={"type": "object", "properties": {"name": {"type": "string"}}},
is_start=True,
can_skip=True,
skip_default="Anonymous",
reasoning="react",
max_iterations=5,
response_template="Got it, {{ name }}!",
help_text="Enter your first and last name.",
context_generation={"variables": {"greeting": "Generate a greeting for {{ name }}"}},
)
Parameters:
| Parameter | Type | Description |
|---|---|---|
name |
str |
Stage identifier |
prompt |
str |
User-facing prompt |
schema |
dict \| None |
JSON Schema for input validation |
tools |
list[str] \| None |
Available tool names |
is_start |
bool |
Whether this is the start stage |
is_end |
bool |
Whether this is an end stage |
can_skip |
bool |
Whether the user can skip |
skip_default |
Any |
Default value when skipped |
suggestions |
list[str] \| None |
Quick-reply suggestions |
response_template |
str \| None |
Template-driven response (bypasses LLM) |
help_text |
str \| None |
Help message |
reasoning |
str \| None |
Reasoning mode: "single" or "react" |
max_iterations |
int \| None |
Max iterations for ReAct reasoning |
context_generation |
dict \| None |
LLM context generation config |
**kwargs |
Any |
Additional StageConfig fields |
add_end_stage()¶
Adds a terminal stage (sets is_end=True automatically).
add_stage()¶
Adds a pre-built StageConfig directly for advanced use cases.
from dataknobs_bots.config import StageConfig
stage = StageConfig(
name="custom",
prompt="Custom stage with full control.",
is_start=True,
llm_assist=True,
llm_assist_prompt="Help the user with their query.",
)
builder.add_stage(stage)
Transitions¶
builder.add_transition(
from_stage="chat",
to_stage="quiz",
condition="data.get('_intent') == 'quiz'",
transform="transform_chat_to_quiz",
priority=10,
derive={"quiz_topic": {"from_field": "selected_topic"}},
metadata={"description": "User wants to start a quiz"},
)
Transitions are stored separately and attached to their source stage at build time. This means stages and transitions can be added in any order.
Parameters:
| Parameter | Type | Description |
|---|---|---|
from_stage |
str |
Source stage name |
to_stage |
str |
Target stage name |
condition |
str \| None |
Python expression evaluated with data in scope |
transform |
str \| list[str] \| None |
Transform function name(s) |
priority |
int \| None |
Evaluation priority |
derive |
dict \| None |
Data derivation rules |
metadata |
dict \| None |
Custom metadata |
Intent Detection¶
Intent detection can be configured inline on conversation stages or added separately:
# Method 1: Inline (via add_conversation_stage)
builder.add_conversation_stage(
name="chat",
prompt="...",
intent_detection={
"method": "keyword",
"intents": [{"id": "quiz", "keywords": ["quiz", "test"]}],
},
)
# Method 2: Separate (via add_intent_detection)
builder.add_intent_detection(
stage="chat",
method="llm",
intents=[{"id": "quiz", "description": "User wants to take a quiz"}],
)
Global Tasks¶
builder.add_global_task(
task_id="collect_name",
description="Get the user's name",
required=True,
completed_by="field",
field_name="user_name",
)
builder.add_global_task(
task_id="search_kb",
description="Search the knowledge base",
required=False,
depends_on=["collect_name"],
completed_by="tool",
tool_name="knowledge_search",
)
Validation¶
The builder validates the configuration at build time and provides both errors and warnings.
Errors (block build)¶
| Condition | Error |
|---|---|
| No stages | "Wizard must have at least one stage" |
| No start stage | "Wizard must have exactly one start stage" |
| Multiple start stages | "Wizard has multiple start stages: [...]" |
| Duplicate stage names | "Duplicate stage name: '...'" |
| Transition to unknown stage | "Stage '...' has transition to unknown stage '...'" |
| Transition from unknown stage | "Transition from unknown stage '...' to '...'" |
| Intent detection on unknown stage | "Intent detection references unknown stage '...'" |
| Invalid reasoning value | "Stage '...' has invalid reasoning value '...'" |
| Invalid mode value | "Stage '...' has invalid mode value '...'" |
Warnings (logged, don't block build)¶
| Condition | Warning |
|---|---|
max_iterations without reasoning |
"Stage '...' sets max_iterations but has no reasoning mode" |
| End stage with transitions | "End stage '...' has transitions that will never be followed" |
| Unreachable stages | "Stage '...' is not reachable from the start stage" |
Pre-Build Validation¶
Use validate() to check without building:
result = builder.validate()
if not result.valid:
for error in result.errors:
print(f"Error: {error}")
for warning in result.warnings:
print(f"Warning: {warning}")
Serialization¶
To Dict¶
config = builder.build()
config_dict = config.to_dict()
# config_dict is compatible with WizardConfigLoader.load_from_dict()
To YAML¶
To File¶
Roundtrip¶
Existing wizard YAML files can be loaded into a builder, modified, and re-exported:
# Load from file
builder = WizardConfigBuilder.from_file("configs/wizards/existing.yaml")
# Modify
builder.add_structured_stage("new_stage", "New question?")
builder.add_transition("existing_stage", "new_stage")
# Rebuild
wizard = builder.build()
wizard.to_file("configs/wizards/updated.yaml")
Or from a dict:
Integration with DynaBotConfigBuilder¶
Use set_reasoning_wizard() on DynaBotConfigBuilder to combine bot and wizard configs:
from dataknobs_bots.config import DynaBotConfigBuilder, WizardConfigBuilder
# Build the wizard config
wizard = (
WizardConfigBuilder("assistant")
.add_conversation_stage("chat", "How can I help?", is_start=True)
.build()
)
# Write it to disk
wizard.to_file("configs/wizards/assistant.yaml")
# Reference it from the bot config
bot_config = (
DynaBotConfigBuilder()
.set_llm("ollama", model="llama3.2")
.set_conversation_storage("memory")
.set_reasoning_wizard(wizard) # Uses wizard.name as the config path
.build()
)
You can also pass a file path string directly:
bot_config = (
DynaBotConfigBuilder()
.set_llm("ollama", model="llama3.2")
.set_conversation_storage("memory")
.set_reasoning_wizard("configs/wizards/assistant.yaml")
.build()
)
StageConfig Reference¶
All fields available on StageConfig:
| Field | Type | Default | Description |
|---|---|---|---|
name |
str |
(required) | Stage identifier |
prompt |
str |
(required) | User-facing prompt |
is_start |
bool |
False |
Whether this is the start stage |
is_end |
bool |
False |
Whether this is an end stage |
can_skip |
bool |
False |
Whether the user can skip this stage |
skip_default |
Any |
None |
Default value if skipped |
can_go_back |
bool |
True |
Whether the user can go back |
auto_advance |
bool \| None |
None |
Auto-advance past this stage. true overrides global to enable, false overrides global to disable, absent/None defers to auto_advance_filled_stages. See Message Stages |
confirm_first_render |
bool |
True |
Whether to pause for confirmation on first render. Set to false to skip and evaluate transitions immediately |
confirm_on_new_data |
bool |
False |
Re-render confirmation when schema values change |
label |
str \| None |
None |
Display label |
suggestions |
tuple[str, ...] |
() |
Quick-reply suggestions |
help_text |
str \| None |
None |
Help message |
schema |
dict \| None |
None |
JSON Schema for validation |
transitions |
tuple[TransitionConfig, ...] |
() |
Stage transitions |
tools |
tuple[str, ...] |
() |
Available tool names |
reasoning |
str \| None |
None |
"single" or "react" |
max_iterations |
int \| None |
None |
Max ReAct iterations |
extraction_model |
str \| None |
None |
Model for extraction |
response_template |
str \| None |
None |
Template-driven response |
llm_assist |
bool |
False |
Enable LLM-assisted responses |
llm_assist_prompt |
str \| None |
None |
Custom LLM assist prompt |
context_generation |
ContextGenerationConfig \| None |
None |
LLM context generation |
mode |
str \| None |
None |
"conversation" for chat stages |
intent_detection |
IntentDetectionConfig \| None |
None |
Intent detection settings |
tasks |
tuple[dict, ...] |
() |
Stage-level tasks |
capture_mode |
str \| None |
None |
Extraction control: "auto", "verbatim", or "extract" |
TransitionConfig Reference¶
| Field | Type | Default | Description |
|---|---|---|---|
target |
str |
(required) | Target stage name |
condition |
str \| None |
None |
Python expression with data in scope |
transform |
str \| list[str] \| None |
None |
Transform function name(s) |
priority |
int \| None |
None |
Evaluation priority |
derive |
dict \| None |
None |
Data derivation rules |
metadata |
dict \| None |
None |
Custom metadata |
subflow |
dict \| None |
None |
Subflow configuration |