Generate Retrieval Data From Text and Images¶
Use the multimodal workflow to generate retrieval queries paired with supporting text passages or images. A successful run produces a portable bundle containing queries, supporting sources with relevance grades, and records for reviewing the model judgments. You can use the bundle in downstream retrieval training.
This guide assumes you have extracted your source text and prepared local images. The pipeline generates queries with supporting source units. For generated question-answer pairs from text documents, use Text QA. The two pipelines have separate configuration and output formats.
For a first run with supplied data, start with Create Your First Retrieval Bundle. This guide covers your own prepared sources.
Before You Start¶
You need the following:
- Python 3.10 or later, an active Python environment, and
uvfor installation. The installed plugin must containdata_designer_retrieval_sdg.multimodal. - An OpenAI-compatible chat-completions endpoint and credentials for explicitly selected generator and judge models. Both models must accept image inputs when your corpus contains images.
- A small, representative source collection and a new output directory.
From a repository checkout containing this workflow, install the plugin in your active Python environment:
uv pip install ./plugins/data-designer-retrieval-sdg
python -m data_designer_retrieval_sdg.multimodal --help
The help output lists the required --config argument. Optional semantic context
combinations require the multimodal extra; basic text and image generation does
not require that extra. Refer to the
configuration reference
when you need it.
1. Prepare Source Records¶
Supply prepared source records, or use directory input to prepare units from independent text and image files.
Create a UTF-8 sources.jsonl file with one JSON object per retrievable unit.
Each unit needs a unique unit_id, a document_id, and text, one image, or both.
The following illustrative records show the three supported input shapes:
{"unit_id":"manual-a-text","document_id":"manual-a","text":"The pump supports three operating modes. Eco mode reduces power use, standard mode maintains normal flow, and boost mode temporarily increases flow.","images":[],"language":"en"}
{"unit_id":"manual-a-chart","document_id":"manual-a","text":"","images":["pages/flow-chart.png"],"language":"en"}
{"unit_id":"manual-b-page","document_id":"manual-b","text":"Extracted text from the specifications page.","images":["pages/specifications.png"],"language":"en","page_number":2}
Replace the illustrative text and image paths with your own content. Image paths
resolve relative to sources.jsonl; the referenced files must exist. For a
text-only corpus, use records with nonempty text and an empty images list.
Extract PDF text, perform optical character recognition (OCR), and render page images before this step. The plugin does not ingest PDFs or infer page relationships from filenames. Keep IDs stable and make each unit small enough to fit a model request without splitting its content. Refer to the source-record reference for all fields.
Compare extracted tables with the original page before generation. Preserve row and column headings, and keep footnote markers separate from numeric values. Extraction errors can propagate into summaries, support explanations, and labels.
Choose instructions and models for the content you supply:
| Corpus | Configuration Choice | Evidence to Review |
|---|---|---|
| Text only | Use the text-only instructions; image-capable models are not required. | Text quotations and source relevance. |
| Images only | Use image-capable generator and judge models; request chart, figure, or table tasks that fit the images. | Visual support against the original image. |
| Text and images | Use image-capable models and instructions for the information needs you want. | Whether each query needs text, images, or both, and whether its positives fit the selected view. |
An instruction label requests a modality; it does not prove that the resulting query uses it. Refer to requested tasks and observed evidence.
Load a Directory of Files¶
To load independent text and image files, replace sources_file in the run
configuration with source_directory:
source_directory:
path: ./documents
max_tokens_per_chunk: 1024
sentences_per_chunk: null
tokenizer: nvidia/Nemotron-3.5-Embed-1B-BF16
The settings shown are the defaults. Each text file becomes a document whose
retrieval units contain up to 1,024 tokens, counted with the Nemotron embedding
tokenizer. Sentence boundaries use the same splitter as Text QA.
There is no sentence-count cap by default. Set sentences_per_chunk to a positive
integer to apply both limits: a chunk closes before the next sentence would
exceed either one.
tokenizer accepts a Hugging Face model ID or a local directory created with
save_pretrained. Tokenizer assets download on first use for a
model ID; model weights are not loaded. Choose the tokenizer and cap for your
embedding deployment. Counts include configured special tokens.
To use only a sentence-count limit, set max_tokens_per_chunk: null. To keep each
file as one unit with internal formatting preserved, set text_chunking: none;
the token cap still applies. A sentence or whole file that exceeds the cap fails
by default. Refer to token limits for
prefix allowances, explicit sentence splitting, and offline tokenizer setup.
Each image becomes a separate document and unit. Use source JSONL for paired text
and images or page relationships. Select the mixed export view to retain both
text-only and image-only units.
Keep the model and run settings from the next step, and place output_dir outside
the source directory. Omit contexts_file unless it references the generated IDs.
Inspect inputs/sources.jsonl after a small run before scaling up. Refer to
Directory Input for file filters,
offline sentence-tokenizer setup, and resume behavior.
2. Configure the Run¶
Save the following as sdg.yaml. Replace the model and endpoint placeholders
with values from your deployment. Paths in YAML resolve from the directory where
you run the command, not from the YAML file's location.
sources_file: ./sources.jsonl
output_dir: ./runs/retrieval-data
dataset_id: service-manuals
context_strategy: unit
generator:
model: <generator-model>
endpoint: https://api.example.com/v1
credential_env: SDG_API_KEY
max_tokens: 8192
judge:
model: <judge-model>
endpoint: https://api.example.com/v1
credential_env: SDG_API_KEY
max_tokens: 8192
concurrency: 1
batch_size: 1
missing_response_attempts: 1
seed: 42
Set SDG_API_KEY in your environment using your credential-management process.
The configuration stores the variable name, not its secret value. There is no
fallback model: verify image support, structured-response behavior, context
limits, and output limits for both roles before scaling up.
The initial configuration uses one source unit per context. A context is the set of units shown together to generate a query. To combine pages or documents, refer to context planning.
Customize Query Instructions¶
The initial configuration omits instructions and instructions_per_context.
This uses the built-in sampler, which requests one text, one figure, and one
table query per selected context. It samples weighted profiles that vary query
type, format, and full or partial answerability. The configured seed makes profile
selection reproducible. Unsupported tasks can abstain.
Start with these defaults and inspect the output. Add custom instructions when
you want to focus the dataset on information needs relevant to your application:
- Target evidence in your corpus, such as software explanations or diagram relationships, or avoid visual tasks for a text-only collection.
- Request the query forms your users submit, such as keyword searches or questions that compare products.
- Guide how much of a query the supplied sources should answer. A partially answerable query can require evidence beyond the supplied context.
Each instruction is a query profile with a unique name and an instruction
that describes the task. Optional fields specify the requested type, format,
modality, persona, and answerability. Refer to the
query profile reference
for all fields.
A custom list replaces the entire default profile set. Each selected profile
requests one query or an explicit abstention. By default, every custom profile is
used for each selected context. Set instructions_per_context to sample a smaller
number of profiles without replacement from your list. To restore the built-in
sampler, omit both fields. An empty instructions list is invalid.
When changing profiles, use a new output_dir; changed generation settings are
incompatible with resume. Compare a sample of the new queries and evidence with
the default run before scaling up. Custom profiles guide generation; they do not
guarantee the requested style, evidence modality, or answerability.
Request Only Text Questions¶
For example, add the following custom profiles to sdg.yaml to replace the
default sampler with two text questions per selected context:
instructions:
- name: text-facts
instruction: >-
Ask a standalone question about an important fact supported by the source
text. Include the subject in the question. Return null if no suitable
question is supported.
modality: text
format: question
answerability: fully answerable
- name: text-explanation
instruction: >-
Ask a standalone question that explains a process or compares concepts
using only the source text. Return null if the text cannot support it.
modality: text
format: question
answerability: fully answerable
These profiles request two queries per selected context. Their labels guide the model; they do not enforce complete answerability or guarantee two accepted queries. Review the resulting evidence as described below. Image-description requests already skip sources without images.
3. Generate and Inspect the Bundle¶
Run the configuration with the module command:
A successful run prints the path to bundle/generation_result.json inside your
output directory. This handoff references the portable bundle manifest. The run
fails without a completed handoff if no candidates pass its quality and evidence
checks.
Before selecting a training view, save the following as review_bundle.py in the
directory where you ran generation. It reads bundle/report.json:
import json
from pathlib import Path
bundle = Path("runs/retrieval-data/bundle")
report = json.loads((bundle / "report.json").read_text())
print("Generated:", report["candidate_records"])
print("Accepted:", report["accepted_query_ids"])
print("Rejections:", report["rejection_reasons"])
print("Views:", report["views"])
Run the script from the same directory:
To refine generation for your corpus, optionally review a sample of accepted and
rejected records in bundle/candidate_diagnostics.jsonl against their original
text and images. Check that queries make sense without seeing the source, quoted
text is accurate, and visual explanations identify the correct figure elements.
Judge scores and valid source IDs do not establish factual correctness.
Refer to sample review.
Select a view that preserves the required evidence. An image attached to a
text-supported source does not make that query suitable for image-only training.
Refer to evidence and views.
Use mixed to retain text-only, image-only, and paired source units together.
The image_and_text view includes only units with images, so it can exclude
queries that also require a text-only unit. Confirm that your downstream consumer
supports the selected view. A small run can produce no training records for a
view even when that view has synthetic evaluation queries.
The bundle contains known positives only. Hard-negative mining, fine-tuning, and model evaluation are downstream steps. Synthetic evaluation queries are useful for workflow checks; use independently reviewed data to measure retrieval quality.
Continue to Mining and Training¶
Keep the complete bundle/ directory when moving the result. Its
generation_result.json handoff references a manifest and checksummed artifacts;
copying only the handoff file is insufficient.
In a compatible Nemotron retrieval recipe checkout,
data preparation consumes this handoff through sdg_input_path and requires an
explicit retrieval_view. For example, the
preview multimodal preparation profile
uses these fields:
Apply these settings in the recipe configuration, not the plugin configuration.
Use image_and_text only when its evidence rules fit your task and it contains
training records. The linked profile requires a compatible multimodal embedding
checkpoint and its own mining environment. Follow the recipe's setup instructions
before running data preparation or fine-tuning. This pinned profile demonstrates
image_and_text; it does not establish mixed support in mining or training.
Check the view and corpus-field support of the recipe version you use, including
nullable images for text-only units. Plugin export support alone is insufficient.
The single-page tutorial verifies generation and inspection. It does not supply enough data to assess training quality. Generate a larger dataset that represents your corpus before mining negatives. Use independent evaluation queries and reviewed relevance labels when comparing retrieval models.
Resume an Interrupted Run¶
For runs that should continue past persistently malformed model responses,
set failed_record_policy: skip when creating a new run. See
Continue After Failed Records
for stage behavior and failure diagnostics. The default remains error.
Preserve the run directory and inspect the error and saved artifacts. After
resolving an external issue, set resume: true in the same configuration and run
the command again. Keep the inputs, remaining configuration, package code, and
dependencies unchanged.
Resume reuses compatible cached responses. A completed run verifies its artifacts and returns the existing handoff. Changed inputs or corrupted artifacts fail integrity checks. Bundle export uses a staging directory and publishes only a complete bundle. An interrupted export can resume from compatible cached work. If publication finished before the completion record was saved, resume reuses the bundle only when every file matches the regenerated export. An existing partial or different destination is not overwritten; preserve it for investigation and use a new output directory for a separate run.
Troubleshoot a Small Run¶
If generation finishes with no accepted queries, inspect candidates.json for
query judgments and localized evidence, and context_outcomes.json for summary
selection. Both files are in output_dir. The files bundle/report.json and
bundle/candidate_diagnostics.jsonl are not written in this case. If inference
fails earlier, inspect the command error and any saved artifacts under
inference/attempts/ instead. Missing or invalid required responses stop the
workflow before export; they are not query-quality rejections.
Use the following checks before increasing the corpus size:
| Symptom | What to Check |
|---|---|
| No candidates pass | Check the saved selection decisions and judgments before lowering thresholds. |
| Many answer-leakage rejections | Confirm that the judge distinguishes a question that contains its answer from a question that can be answered. Inspect its reasoning. |
| Missing image or provider image error | Check file paths, image readability, and both models' image-input support and limits. |
| A source unit exceeds a request limit | Split it during preprocessing and assign stable IDs to the new units. Context splitting cannot divide one unit. |
| Incomplete structured responses | Inspect the attempt's errors and saved responses. Check output-token limits, including reasoning-token usage, and the endpoint's JSON formatting support. |
| A training view is empty | Inspect its accepted-query and split counts. Choose an evidence-compatible view with training records; do not substitute another view silently. |
| An existing run is rejected | Use explicit resume only for a compatible run, or choose a new output directory for changed inputs or configuration. |
Batch size and missing-response attempts are not aggregate request, token, or spending limits. Monitor provider usage and begin with a bounded source sample. Use an external job time limit when you need a hard elapsed-time bound. Keep run directories protected because they contain source content and model responses.