Skip to main content
All posts
Batch ETL & Localisation

Pipelines batch de 10 000 lignes sans interruption & localisation fidèle de jeux sur bf16.ai

Comment les blocages du filtre de contenu corrompent vos tables ETL et édulcorent les dialogues narratifs — et comment exécuter les mêmes jobs sur bf16.ai avec des modèles abliterated à cache préfixe, le glossaire partagé étant facturé au tarif du cache.

TL;DR · Ingénierie Batch & Localisation

  • Corruption silencieuse : Sur 10 000 lignes juridiques, cliniques ou RPG, un taux de refus de seulement 4,8 % laisserait 480 entrées vides — ou pire, injecterait des excuses d’IA dans vos fichiers livrés.
  • Routage bi-modèle sur bf16.ai : zai-org/glm-5.3-flash-abliterated ($0.24/$0.06/$1.00) pour le volume UI/ETL et zai-org/glm-5.3-abliterated ($1.40/$0.30/$4.40, sous le tarif officiel) pour les dialogues clés.
  • Glossaire 50K en cache + Idempotency-Key : Une bible terminologique de 50K tokens figée dans le prompt système est facturée au tarif du cache à chaque relecture ($0.06 au lieu de $0.24 par 1M sur GLM-5.3 Flash), sans double facturation lors des réessais.

1. Comment les API grand public sabotent l’ETL massif et la localisation narrative

Dépositions judiciaires, notes cliniques d’urgence et scripts de dark fantasy contiennent inévitablement des passages tendus. Les API filtrées déclenchent des tempêtes de réessais 400 content_policy_violation ou lissent en douce le ton d’un antagoniste, allant jusqu’à écrire un refus poli directement dans la base JSON :

[etl-worker:chunk-0902] Localizing row_id="ch4_syndicate_interrogation_12" (EN -> JA, ZH-CN, DE)
[upstream:cloud-api] HTTP 400 content_policy_violation (attempt 1/3 -> retry backoff 2s)
[upstream:cloud-api] HTTP 400 content_policy_violation (attempt 2/3 -> retry backoff 4s)
[upstream:cloud-api] Fallback row output contaminated:
  {"ja": "申し訳ありませんが、脅迫や暴力的な表現を含む台詞は翻訳できません。", "zh": "..."}
[qa-gate] CRITICAL: Refusal string written into production localization table! 480 / 10,000 rows failed build.

2. Architecture batch avec cache préfixe : GLM-5.3 Flash + GLM-5.3

Sur bf16.ai/models, deux modèles abliterated à contexte 1M se complètent parfaitement :

  1. zai-org/glm-5.3-flash-abliterated (1M contexte) : Entrée cachée à $0.06/1M pour l’extraction JSON massive et l’interface.
  2. zai-org/glm-5.3-abliterated (1M contexte) : Préserve argot, registres littéraires et tension dramatique ($1.40/$0.30/$4.40).

Pipeline batch zéro abandon : Glossaire 50K partagé en cache + JSON Schema strict + Idempotency-Key.

▲ Pipeline batch zéro abandon : Glossaire 50K partagé en cache + JSON Schema strict + Idempotency-Key.

3. Worker TypeScript de production avec Idempotency-Key et contrôle des variables

Placez votre glossaire de 50 000 tokens de façon statique en tête du message system pour maximiser le cache préfixe et joignez un Idempotency-Key par lot :

import OpenAI from 'openai';

const bf16 = new OpenAI({
  baseURL: 'https://api.bf16.ai/v1',
  apiKey: process.env.BF16_BATCH_KEY,
});

// Shared 50,000-token lore glossary stays identical across all chunks -> hits $0.06-$0.30/1M prefix cache!
export async function localizeDialogueBatch(
  frozenGlossaryXml: string,
  batchId: string,
  rows: Array<{ id: string; speaker: string; sourceEn: string; tier: 'bulk_ui' | 'cinematic' }>
) {
  const isCinematic = rows.some((r) => r.tier === 'cinematic');
  const model = isCinematic
    ? 'zai-org/glm-5.3-abliterated'            // $1.40 in / $0.30 cached / $4.40 out
    : 'zai-org/glm-5.3-flash-abliterated';     // $0.24 in / $0.06 cached / $1.00 out

  const completion = await bf16.chat.completions.create(
    {
      model,
      temperature: isCinematic ? 0.35 : 0.1,
      response_format: {
        type: 'json_schema',
        json_schema: {
          name: 'localized_rows',
          strict: true,
          schema: {
            type: 'object',
            additionalProperties: false,
            required: ['items'],
            properties: {
              items: {
                type: 'array',
                items: {
                  type: 'object',
                  additionalProperties: false,
                  required: ['id', 'ja', 'zh_CN', 'de'],
                  properties: {
                    id: { type: 'string' },
                    ja: { type: 'string' },
                    zh_CN: { type: 'string' },
                    de: { type: 'string' },
                  },
                },
              },
            },
          },
        },
      },
      messages: [
        {
          role: 'system',
          content:
            'You are a senior game & legal corpus localization engine. Preserve speaker register, ' +
            'slang, dark-fantasy conflict, and placeholders ({player_name}, %d) 1:1 without sanitizing ' +
            'tone or appending commentary.\n\n' + frozenGlossaryXml,
        },
        { role: 'user', content: JSON.stringify(rows) },
      ],
    },
    {
      // Safe worker retries: exact replay returns cached response with zero double-charging
      headers: { 'Idempotency-Key': `loc-batch-${batchId}` },
    }
  );

  return JSON.parse(completion.choices[0]?.message?.content ?? '{"items":[]}');
}

Vérifiez systématiquement les variables ({player_name}, %d) avant insertion. Consultez les 3 modèles actifs directement sur bf16.ai/models :

// Post-generation placeholder & refusal-leakage assertion before writing to Postgres/Parquet
export function assertCleanLocalizationRow(source: string, translated: string) {
  const placeholders = source.match(/\{[a-z0-9_]+\}|%[sd]/gi) ?? [];
  for (const token of placeholders) {
    if (!translated.includes(token)) {
      throw new Error(`Missing placeholder ${token} in localized output`);
    }
  }
  if (/cannot translate|申し訳ありませんが|无法翻译|als KI-Modell/i.test(translated)) {
    throw new Error('Refusal leakage detected in localized string');
  }
}

4. Chiffrer une localisation de 10 000 lignes avec le cache préfixe

Prenons 10 000 répliques RPG (20 lignes × 500 lots partageant une bible de 50K tokens, soit 25M tokens de glossaire cumulés). Aux tarifs publics, ces 25M tokens de glossaire coûtent $6.00 sans cache sur GLM-5.3 Flash, mais $1.50 servis depuis le cache préfixe — et $35.00 contre $7.50 sur GLM-5.3. Les tokens de sortie sont facturés de la même façon dans les deux cas :

Configuration PipelineBilan SLA
API filtrée stricte (3x retry)Reprise manuelle requise
Appels unitaires sans cacheLent, avec le glossaire facturé au plein tarif d’entrée à chaque appel
bf16.ai avec Cache (GLM-5.3 Flash + GLM-5.3)Prêt à livrer

5. 4 règles d’ingénierie avant de lancer plus de 100 000 lignes

  1. Gardez le préfixe system strictement identique au bit près (aucun timestamp dynamique en tête).
  2. Groupez 15 à 25 entrées par requête JSON.
  3. Envoyez toujours Idempotency-Key depuis vos workers de file.
  4. Contrôlez les variables de formatage et l’expansion de longueur UI (allemand/russe).