Evidence-grounded SFT data

FinFlow generates three types of Chinese text SFT examples from verified, locally approved v7 releases:

Recipe Input Output and checks
document_qa Approved original text Questions, answers and exact quotations; independent evidence review
extraction Approved original text JSON field extraction; every value must occur in its evidence quotation
table_calculation Table OCR, surrounding context and original image from an approved visual candidate Two-operand calculation, recomputed in code and reviewed against the image

This guide covers text SFT: training inputs contain context and questions, not images. Original table images are used for review only. Optional visual SFT exports real image inputs. Use the single-GPU SFT launcher to train exported datasets.

1. Configure models

Use the existing project environment; no additional dependencies are needed. Set credentials only in local .env:

FIN_DOC_SFT_GENERATE_API_URL=https://your-provider.example/v1
FIN_DOC_SFT_GENERATE_API_KEY=YOUR_LOCAL_KEY
FIN_DOC_SFT_GENERATE_MODEL=YOUR_GENERATION_MODEL
FIN_DOC_SFT_REVIEW_API_URL=https://your-provider.example/v1
FIN_DOC_SFT_REVIEW_API_KEY=YOUR_LOCAL_KEY
FIN_DOC_SFT_REVIEW_MODEL=YOUR_REVIEW_MODEL

Both endpoints must support Chat Completions. Generation and review use separate calls. The review model must accept images when table_calculation is enabled. Optional *_MODEL_VERSION, *_TIMEOUT and *_MAX_TOKENS settings are listed in .env.example. Existing CPT credentials are not reused implicitly.

cp -n config/sft.json config/sft.local.json
python -m training.sft_cli --config config/sft.local.json preflight

Preflight makes no model requests and does not verify connectivity or visual capability. The standalone SFT entry point does not require OCR or a CPT tokenizer, but requires the original local data directory, database and v7 release.

2. Build a dataset

Read release.release_id from a daily flywheel summary. The source must still have source_policy.<source_name>.training set to approved in the selected flywheel configuration. Use --flywheel-config config/flywheel.local.json when using a local policy file.

python -m training.sft_cli \
  --config config/sft.local.json \
  --flywheel-config config/flywheel.local.json \
  --data-root data \
  build --dataset-id sft-001 --release YOUR_V7_RELEASE_ID

Global options precede build. Only candidates accepted both in the v7 release and the current local database are eligible. Republish v7 evidence after an upstream review decision changes. A copied release without matching local lineage records is not sufficient.

The pipeline:

  1. Verifies release, candidate and evidence checksums and current source admission and candidate decisions.
  2. Freezes train/validation assignments by logical work and identical context before generation.
  3. Generates candidates, then checks JSON, exact quotations, extracted values and arithmetic.
  4. Independently reviews support, answerability, units, dates and instruction following; table review includes the image.
  5. Exports only accepted examples and records rejected, uncertain, skipped and failed results.

The generator may return zero examples. Empty or over-limit evidence is excluded with an audit reason; tables and conversations are never silently truncated or split into separate examples.

3. Recipe and arithmetic limits

config/sft.json enables all three text tasks with up to two samples per evidence/task pair:

  • max_jobs: maximum newly completed generation jobs per invocation; completed cache entries do not count.
  • max_model_requests: per-invocation SFT request cap, separate from CPT; cached responses do not count.
  • max_seconds: checked before starting each model request; an in-flight request may continue until its role timeout.
  • max_evidence_chars, max_question_chars, max_answer_chars: character limits.
  • validation_fraction: initial assignment ratio; previously frozen or CPT-assigned works retain their split.
  • generation_prompt_version, review_prompt_version: prompt revision identifiers.

Exports retain complete messages; the downstream trainer supplies its tokenizer, chat template, length validation and packing policy.

Supported calculations use two operands with identical units:

Operation Formula
sum a + b
difference a − b
ratio a / b
percentage a / b × 100%
growth_rate (a − b) / b × 100%, requiring b > 0

Values and units use exact evidence quotations. Decimal arithmetic uses ROUND_HALF_UP with 0–6 decimal places and two amounts in the same unit. The reviewer checks operand selection, row/column meaning, period, and reporting scope.

4. Outputs, audit and lineage

Output directory: data/training/sft/datasets/<dataset-id>/.

File Contents
train.jsonl, validation.jsonl Text-only sample_id and messages for conversation-aware training programs
train.vision.jsonl, validation.vision.jsonl, images.jsonl Visual exports and image inventory; empty for text-only recipes
samples.jsonl Full examples with task, split, generation/review artifacts, quotations and calculation details
evidence.jsonl Context, source, work identity, chunk/table position and upstream decision
audit.jsonl Review results, deterministic validation failures, exclusions and duplicates
jobs.jsonl Generation jobs and local checkpoint references
manifest.json, checksums.sha256 Recipe, model identities, input release, counts, status and integrity inventory

Each text training row has three messages: system, user with evidence/task, and assistant. sample_id is metadata; use messages as the training input.

python -m training.sft_cli verify data/training/sft/datasets/sft-001
python -m workflow.flywheel_cli --data-root data trace --sample-id YOUR_SFT_SAMPLE_ID

Lineage reaches model responses, upstream candidates, text/table evidence and OCR/PDF artifacts. Training rows contain the context and can be used independently for text training. Full audits refer to the original data directory; preserve it and the release when archiving.

5. Resume and splits

Model responses and completed jobs are cached by inputs, model identity and recipe. Network failures or request/time limits publish the already accepted examples with partial status and exit code 1; configuration/fatal failures use exit code 2. Continue with a new dataset ID and the same recipe/release to reuse completed work and process pending jobs.

An existing dataset ID returns its verified historical snapshot; it is never appended to. The current engine/export is sft-v2: upgrading changes recipe identity, so use a new dataset ID. Existing v1 packages remain unchanged. Each new export contains cumulative results for the selected release and recipe, not a daily delta. Do not concatenate successive resume snapshots without deduplication. Rejected/uncertain results are not regenerated indefinitely. Change a prompt version or model to create a new recipe while keeping old audit records. There is no SFT manual-approval command yet; upstream human candidate approval does not approve derived SFT examples.

Splits are persisted before generation and inherit existing CPT assignments. Identical context, identical image bytes and same-work derivatives share a split. New CPT builds also respect SFT assignments. Historical conflicts exclude affected SFT evidence with split_conflict. Exports use train/validation splits grouped by logical work, identical context, and image bytes.

6. Daily generation

Enable the following in the local flywheel configuration:

"sft": {"enabled": true, "config": "config/sft.local.json"}

Relative paths resolve against the parent of the directory containing the flywheel configuration, usually the project root. Run the existing daily command:

python -m workflow.flywheel_cli --config config/flywheel.local.json run

SFT runs after evidence publication and CPT generation, using remaining flywheel time and its own request cap. Completed jobs reuse cached results. Daily summaries and channel reports include sft_dataset. SFT failures make the daily run partial while preserving published CPT data. Set enabled=true to include SFT in daily generation; the default is false.

Additional generation strategies

Direct generation remains the default. Optional grounded Self-Instruct, answer-first, Evol-Instruct and CodecLM workflows are documented in Generation methods. See also Data mixtures.