AgentGenerator Output Assembly Contract¶
Status: CANONICAL — describes what actually exists Last verified: 2026-04-08 Source files: - factory_app/workflows/AgentGenerator/tools/workflow_converter.py - factory_app/workflows/AgentGenerator/tools/generate_and_download.py - factory_app/workflows/AgentGenerator/structured_outputs.yaml - factory_app/workflows/AgentGenerator/agents.yaml
Related:
structured-output-extraction-contract.md— the general runtime contract for structured outputs and auto-tool-call. This document covers the AgentGenerator-specific pattern where agent outputs are accumulated via persistence and assembled into workflow YAML files.
How This Differs From Normal Workflows¶
Normal mozaiks workflows use the auto-tool-call pattern: agent emits → runtime validates → tool auto-called inline → tool persists/emits UI.
AgentGenerator uses a deferred assembly pattern: agents emit → outputs persist to MongoDB → DownloadAgent triggers assembly → gather_latest_agent_jsons() scrapes all prior outputs → create_workflow_files() assembles → files written under MOZAIKS_GENERATED_ARTIFACTS_PATH → zip presented to user.
There are no auto-tool-call tools per planning agent. Every planning agent's output is collected in a single final pass by the DownloadAgent.
Generated workflow bundles are staged at:
They do not become active runtime-loaded workflows until an explicit promotion step copies them into an active app root's workflows/ directory.
Canonical live regression target for this workflow:
python scripts/run_live_mfj_smoke.py \
--workflow AgentGenerator \
--workflows-root factory_app/workflows \
--prompt-file factory_app/workflows/AgentGenerator/smoke_prompt.txt \
--tool-response-file factory_app/workflows/AgentGenerator/smoke_responses.json \
--timeout-seconds 300
The checked-in smoke pair:
factory_app/workflows/AgentGenerator/smoke_prompt.txtfactory_app/workflows/AgentGenerator/smoke_responses.json
is the stable regression contract for:
- multi-turn AG2 interview replies
- composer fallback via
default_input_reply ProjectOverviewAgentdiagram review through the composer reply lane- shipped/shared workflow primitives such as
DownloadCenter - final bundle handoff through
generate_and_download
Agent Pipeline¶
16 agents run in sequence. Each is mapped to a structured output model.
| # | Agent | Output Model | Notes |
|---|---|---|---|
| 1 | InterviewAgent | none (null) | Conversational only |
| 2 | PatternAgent | PatternSelectionOutput | Selects orchestration pattern; feeds PatternSelection to all downstream |
| 3 | WorkflowStrategyAgent | WorkflowStrategyOutput | Defines modules, startup_mode, MFJ decomposition config |
| 4 | DatabaseIntentAgent | DatabaseIntentOutput | Collections, fields, access patterns → db_intent.json |
| 5 | AgentRosterAgent | AgentRosterOutput | Agent names, module_index, human_interaction; reads ToolPlanning.ui_requirements |
| 6 | ToolPlanningAgent | ToolPlanningOutput | Absorbs UX decisions: ui_requirements (inline/artifact per module) + backend tools + lifecycle tools + hooks |
| 7 | ProjectOverviewAgent | MermaidSequenceDiagramOutput | Renders Mermaid sequence diagram as UI artifact; user reviews the agent pipeline before continuing |
| 8 | ContextVariablesAgent | ContextVariablesPlanOutput | Absorbs state architecture: derives context var types, lifecycle_requirements, assets from WorkflowStrategy directly |
| 9 | ToolsManagerAgent | ToolsManifestOutput | Normalizes tools manifest → tools.yaml |
| 10 | UIFileGenerator | UIToolsFilesOutput | Generates canonical CodeFile entries for Python UI tool wrappers and only the workflow-local React files actually required |
| 11 | AgentToolsFileGenerator | AgentToolsFilesOutput | Generates canonical CodeFile entries for backend tool stubs |
| 12 | StructuredOutputsAgent | StructuredModelsOutput | Generates structured_outputs.yaml models + registry |
| 13 | AgentsAgent | RuntimeAgentsOutput | Generates full agents.yaml with prompt_sections |
| 14 | HookAgent | HookFilesOutput | Generates hooks.yaml + hook implementations |
| 15 | HandoffsAgent | HandoffRulesOutput | Generates handoffs.yaml |
| 16 | OrchestratorAgent | OrchestrationConfigOutput | Generates orchestrator.yaml |
| 17 | PackMetadataAgent | PackMetadataOutput | Generates global extended_orchestration/extension_registry.json (cross-workflow pack registry) |
| 18 | DownloadAgent | DownloadRequestOutput | Triggers final assembly |
Removed agents (collapsed): - StateArchitectAgent → merged into ContextVariablesAgent (context var taxonomy, lifecycle requirements, and assets are derived directly from WorkflowStrategy) - UXArchitectAgent → merged into ToolPlanningAgent (UI surface decisions — inline vs artifact — are one field decision per module, part of tool planning)
Output Collection: gather_latest_agent_jsons()¶
When DownloadAgent triggers the generate_and_download tool, it calls:
This method scans MongoDB for assistant messages in the chat, parses their content as JSON, and returns a dict keyed by agent_name:
{
"WorkflowStrategyAgent": { "WorkflowStrategy": { ... } },
"AgentsAgent": { "agents": [ ... ] },
"ContextVariablesAgent": { "context_variables": [ ... ] },
# ...
}
Collection constraints: - Only messages with role="assistant" and a valid agent_name field - Only messages whose content parses as valid JSON (after markdown fence stripping) - Takes the latest message per agent name (last turn wins) - If an agent never emitted valid JSON, its key is absent from collected
Brittleness at scale: If an agent produces malformed JSON, wraps output in markdown, or emits multiple turns, only the last valid JSON is kept. Agents that re-ran during iteration will silently overwrite earlier outputs.
Assembly: create_workflow_files()¶
The collected dict is passed to create_workflow_files(data, context_variables), which builds a unified config dict, then calls _save_modular_workflow().
Input Key → Config Section Mapping¶
| Collected Key | Config Section | Target File |
|---|---|---|
orchestrator_output | merged into config root | orchestrator.yaml |
agents_output.agents (list→dict transform) | config["agents"] | agents.yaml |
handoffs_output.handoff_rules | config["handoffs"] | handoffs.yaml |
hooks_output.hooks (metadata only) | config["hooks"] | hooks.yaml |
context_variables_output | config["context_variables"] | context_variables.yaml |
tools_manager_output (tools + lifecycle_tools) | config["tools"] | tools.yaml |
ui_config | merged into config root | ui_config.yaml |
structured_outputs (static) + structured_outputs_agent_output (dynamic) | merged → config["structured_outputs"] | structured_outputs.yaml |
Files Written by _save_modular_workflow()¶
{WorkflowName}/
├── orchestrator.yaml ← OrchestratorAgent output (merged root keys)
├── agents.yaml ← AgentsAgent output (list-to-dict transform)
├── handoffs.yaml ← HandoffsAgent output
├── hooks.yaml ← HookAgent metadata (filecontent → extra_files)
├── context_variables.yaml ← ContextVariablesAgent output
├── tools.yaml ← ToolsManagerAgent output
├── ui_config.yaml ← visual_agents config
├── structured_outputs.yaml ← merged static + StructuredOutputsAgent
├── capability_spec.json ← generated from config metadata
├── websocket_config.yaml ← generated from config metadata
├── db_intent.json ← DatabaseIntentAgent output (if present)
├── extended_orchestration/
│ └── mfj_extension.json ← built from WorkflowStrategy.decomposition (if MFJ)
└── tools/
├── *.py ← `CodeFile.filename` entries from UIFileGenerator / AgentToolsFileGenerator
└── ...
Generated workflow tools are workflow-local. AgentGenerator must not create or reference workflows/_shared, app.workflows._shared, sibling workflow tool folders, or root-level shared tool paths. Reusable framework-owned support code belongs under mozaiksai.core.*; workflow-specific helpers stay beside the workflow tools that call them.
Factory-owned builder infrastructure is separate from generated bundle output. Builder-only shared helpers may live under factory_app/workflows/_shared/, but generated workflow bundles must still emit workflow-local files only.
Frontend React components (if any UI tools):
{WorkflowName}/ui/
├── index.js ← synthesized deterministically from workflow-local component files
├── <workflow-local paths> ← `CodeFile.filename` entries emitted by UIFileGenerator
└── ...
Rules:
UIFileGeneratoremits canonicalCodeFileobjects withfilename+content;workflow_converter.pyno longer consumes ad hoctool_name/py_content/js_contentstructures on the live path.- Workflow-local React files stay under the workflow's own
ui/tree and preserve the emitted relative path when safe. ui.realizationis the canonical realization contract for workflow UI assembly.- If
ui.realization=shipped_component, no workflow-local React file is required. The converter skips any accidental duplicate React file and still keeps the Python wrapper/tool file. - If
ui.realization=workflow_wrapper, the converter keeps that wrapper and synthesizesui/index.jsfrom the declared workflow-local component files. - If
ui.realization=generated_component, the converter preserves the full workflow-local React surface.
Special Cases¶
extended_orchestration/mfj_extension.json (per-workflow MFJ)¶
Not produced by a dedicated agent. Built programmatically in _build_workflow_local_pack_graph() from WorkflowStrategyAgent's output.
Required WorkflowStrategy.decomposition fields to trigger graph generation:
{
"required": true,
"mode": "single_stage_mfj",
"decomposition_agent": "DecomposerAgent",
"child_initial_agent": "WorkerAgent",
"resume_agent": "ResumeRouterAgent",
"resume_entry_agent": "ResumeRouterAgent",
"inject_as": "mfj_results",
"max_children": 3,
"contracts": {
"input_required": ["field1"],
"input_optional": [],
"output_required": ["result_field"],
"output_optional": []
}
}
If decomposition.required is false or mode is not single_stage_mfj, no extended_orchestration/mfj_extension.json is written.
Structured Outputs Merge¶
Two sources are merged:
-
Static base (
structured_outputskey indata): Model library from the AgentGenerator's ownstructured_outputs.yaml— these are the meta-models used by the planning agents (e.g.,WorkflowAgent,AgentTool,ToolSpec, etc.) -
Dynamic (
structured_outputs_agent_output): WhatStructuredOutputsAgentproduced for the generated workflow's own models.
The merge strategy: dynamic models and registry entries overwrite static ones. All agents listed in agent_names (from AgentsAgent output) are ensured to have a registry entry (defaulting to null if not mapped by StructuredOutputsAgent).
Agents List-to-Dict Transform¶
AgentsAgent emits a list: [{"name": "MyAgent", "prompt_sections": [...], ...}]
create_workflow_files() transforms this to a dict keyed by agent name:
The name and display_name fields are excluded from the stored config.
Hook Files¶
HookAgent output includes both: - Metadata entries (stored in hooks.yaml): hook_type, hook_agent, filename, function - Implementation content (filecontent): written to tools/{filename} as extra files
Function names are normalized (module prefix and dot notation stripped). Hook implementation files follow the same workflow-local path rule and may not target shared workflow folders.
Workflow Name Resolution¶
The generated workflow name is resolved in priority order:
context_variables.action_plan.workflow.name(auto-tool context injection)collected["WorkflowStrategyAgent"]["WorkflowStrategy"]["workflow_name"]collected["OrchestratorAgent"]["workflow_name"]- Fallback:
"Generated_Workflow"
All names are converted to PascalCase for the output folder and zip file name.
Agent Output Key Reference¶
When WorkflowStrategyAgent emits:
{
"WorkflowStrategy": {
"workflow_name": "StoryCreator",
"startup_mode": "UserDriven",
"orchestration_pattern": "PipelinePattern",
"decomposition": { "required": false }
}
}
create_workflow_files() unwraps this via:
Other agents follow similar wrapping patterns. The converter handles both wrapped ({"WorkflowStrategy": {...}}) and unwrapped ({...}) forms.
At-Scale File Generation Considerations¶
The current approach has known brittleness points:
| Risk | Description | Mitigation |
|---|---|---|
| JSON parse failure | Agent wraps output in markdown or adds extra text | clean_agent_content() strips fences; still fails on partial JSON |
| Agent re-runs | Iterating agents overwrite earlier outputs | Only latest message per agent is kept — early outputs are lost |
| Missing agent output | Agent skipped or failed | Corresponding config section is absent; file may be incomplete |
| Model mismatch | StructuredOutputsAgent emits wrong wrapper key | _normalize_dynamic_structured() tries common fallback keys |
| Agents list order | AgentsAgent list order sets agent sequence | No runtime validation that list order matches handoffs |
Consistent file generation at scale requires that each planning agent: 1. Emits exactly one valid JSON turn (no conversational turns before the JSON turn) 2. Uses the canonical wrapper key matching the model name (e.g., "WorkflowStrategy": {...}) 3. Does not re-run after its designated turn
To verify output quality, inspect logs/agent_outputs/agent_outputs_{chat_id}_{ts}.json and logs/workflow_converter/converter_input_{WorkflowName}_{ts}.json written during each generation run.
What Does NOT Exist¶
| Concept from Aspirational Spec | Reality |
|---|---|
extraction_contracts.yaml | Does not exist — mapping is hardcoded in workflow_converter.py |
BaseExtractor class / extractor classes | Does not exist — monolithic create_workflow_files() function |
| Separate "extraction" phase | Does not exist — assembly happens in generate_and_download tool |
workflow_strategy.yaml / agent_roster.yaml output files | Intermediate planning only — NOT written as workflow files |
StateArchitectAgent | Removed — its work (context var types, lifecycle requirements, assets) is now done inline by ContextVariablesAgent |
UXArchitectAgent | Removed — UI surface decisions (inline/artifact) are now part of ToolPlanningAgent output |
StateArchitectAgent | Removed — merged into ContextVariablesAgent |
UXArchitectAgent | Removed — merged into ToolPlanningAgent |
Cross References¶
- structured-output-extraction-contract.md — general auto-tool-call pattern
- workflow-authoring-contracts.md —
extended_orchestration/mfj_extension.jsonformat factory_app/workflows/AgentGenerator/tools/workflow_converter.py— assembly implementationfactory_app/workflows/AgentGenerator/tools/generate_and_download.py— collection + triggerfactory_app/workflows/AgentGenerator/— current working MFJ-enabled workflow example