以前のアーキテクチャの投稿](/ja/blog/how-plugin-guardrail-and-pipeline-systems-work/)では、プラグイン、アクションガード、パイプラインシステムを取り上げました。今回は、Rasepiを他のdocsプラットフォームと根本的に違うものにしていると私が考えている部分である、翻訳エンジンについて深く掘り下げます。
ページの代わりに段落を翻訳するという売り文句ではありません。実際のコードです。用語集がテナントごとにどのように解決されるのか、DeepLのスタイルルールとカスタム命令がどのようにすべての翻訳を形成するのか、コンテンツハッシュがどのように陳腐化を検出するのか、オーケストレータがどのブロックを再翻訳するかをどのように決定するのか。
翻訳エンジン: 用語集、スタイルルール、スマートな再翻訳](/ja/blog/img/translation-engine-deep-dive.svg)
翻訳パイプライン
ユーザーが文書を保存するとき、システムは単にすべてを再翻訳するわけではありません。かなり特殊なシーケンスを実行します:
1.TipTap JSONを個々のブロックに解析します。 2.どのブロックが実際に変更されたかを検出するために、コンテンツのハッシュを比較します。 3.変更されたブロックについて、テナントの用語集と言語ペアのスタイルルールリストを解決します。 4.テナント構成からスタイルルール、カスタム命令、形式を適用します。 5.変更されたブロックのみを DeepL に送信します。 6.翻訳ブロックの更新とコンテンツ・ハッシュの同期
各ステップは、独自のインタフェースを持つ独自のサービスです。どのステップも、別の翻訳プロバイダ、別のハッシュ・アルゴリズム、別の用語集ソースなど、別のものに交換で きるため、これは重要です。
グロッサリ解決: テナントをスコープし、DeepLを同期します。
DeepL用語集には、ほとんどの人が知らない制約があります:**DeepL用語集は編集できません。DeepL 用語集を編集することはできません。変更すると、古い用語集が削除され、新しい用語集が作成されます。
Rasepiは、データベースを真実のソースとして扱い、DeepL用語集を使い捨ての実行時成果物として扱うことで、これを処理します。CODEBLOCK_16__エンティティはすべてをローカルに格納します:
コードブロック_0__
ユーザが用語集エントリを追加すると、例えば EN→DE の "Sprint Review" を "Sprint-Überprüfung" にマッピングすると、データベース・レコードが直ちに更新され、IsDirty が true に設定されます。DeepL 用語集はその時点では再作成されません。次に翻訳で実際に必要になったときに、DeepL 用語集は遅延的に再作成されます。
同期フロー
翻訳が呼び出されるたびに、用語集が解決されます:
コードブロック_1__
ここで注目すべきことが3つあります:
1.**翻訳が実際に必要な場合にのみ DeepL API を呼び出します。用語集エントリを一括編集しても、数十回の API 呼び出しがトリガされることはありません。
2.テナントの分離. クエリは EF グローバル・クエリ・フィルタを介して実行されるため、TenantGlossaries は自動的にスコープされます。テナントAの用語集エントリがテナントBの翻訳に漏れることはありません。
3.**DeepL は、とにかくこれを強制します。EN→DE 用語集を 1 つ、EN→FR 用語集を 1 つ、といった具合です。CODEBLOCK_20__ ペアはテナントごとに一意です。
用語集エントリ
個々のエントリは単なる用語のマッピングです:
コードブロック2
APIは完全なCRUDと一括管理のためのCSVインポート/エクスポートを提供します:
コードブロック_3__
CSVインポートは、既存の翻訳メモリシステムから移行するチームにとって非常に便利です。用語集をエクスポートしてクリーンアップし、Rasepiにインポートすると、次の翻訳実行時に新しい用語集が自動的に使用されます。
スタイルルール、カスタム命令、形式
用語集は専門用語を扱います。しかし、用語集はその半分に過ぎません。正しい言葉を使っていても、翻訳が誤って聞こえることがあります。語調の間違い、日付書式の間違い、句読点の打ち方の間違い。
DeepL の スタイル・ルール API (v3) は、これを解決します。2 種類のコントロールを組み合わせた再利用可能なスタイル・ルール・リストを作成できます:
1.構成ルール、日付、時刻、句読点、数値などの事前定義されたフォーマット規則 2.カスタム命令、トーン、言い回し、およびドメイン固有の慣習を形成するフリーテキスト命令
ラセピは、テナントごと、ターゲット言語ごとにこれらを作成し、管理します。CODEBLOCK_21__エンティティは、DeepLのstyle_idを、テナントの設定されたルールとカスタム命令と一緒に保存します:
コードブロック_4__。
スタイル・ルール・リストの作成
管理者がドイツ語の翻訳ルールを設定すると、RasepiはDeepLのv3 APIを呼び出してスタイルルールリストを作成します。以下はその様子です:
コードブロック_5__
用語集とは異なり、DeepLのスタイルルールリストは変更可能です。構成されたルールは PUT /v3/style_rules/{style_id}/configured_rules で置換でき、カスタム命令は個別に追加、更新、または削除できます。反復的な絞り込みに最適です。
構成されたルールはどのように見えますか?
設定されたルールは、言語や会社の好みによって異なる書式規則をカバーします。以下のようなものです:
__codeblock_6__のようなものです。
これらは些細なことのように聞こえますが、すぐに複雑になります。AM/PM時間フォーマットとピリオドで区切られた小数を使うドイツ語の文書は、ドイツ語の読者には「英語から翻訳された」としか読めません。すべてのドイツ語翻訳でuse_24_hour_clockとuse_commaを小数点区切り文字に設定すると、そのようなことはすぐになくなります。
カスタム命令: これが本当の力です
カスタム命令は、スタイル・ルール・リストごとに最大 200 個、それぞれ最大 300 文字のフリーテキスト命令です。基本的に、DeepL に翻訳をどのように作成するかを平易な言葉で指示します:
codeblock_7__
テナントからの実際の例:
- CODEBLOCK_26__親しみやすいドキュメントを求める新興企業向け
- CODEBLOCK_27__ドイツの法律事務所向け
"Translate 'deployment' as 'Bereitstellung', never 'Deployment'"単純な用語集のマッピングだけでなく、文脈に依存した取り扱いが必要な用語のための__CODEBLOCK_28__。- CODEBLOCK_29__ 英国を拠点とする企業で英語の異言語間の翻訳を行う場合
- ヨーロッパの慣例に合わせるための
"Put currency symbols after the numeric amount"。
カスタム命令は、用語集エントリに収まらないドメイン固有の慣習に対して非常に強力です。用語集は1つの用語を別の用語にマッピングします。カスタム命令では、"APIドキュメントを翻訳するときは、受動態の代わりに命令形を使いなさい "と言うことができます。これはまったく異なる種類のコントロールです。
形式
DeepLのformalityパラメータ (default、_more、less、prefer_more、prefer_less) は、スタイル・ルールと並んで、別のコントロールとして引き続き使用できます。ドイツ語の "du "と "Sie"、フランス語の "tu "と "vous"、日本語のポライトネス・レベル。これらはTenantLanguageConfigによってテナント言語ごとに設定されます:
コードブロック8
形式、スタイルルール、用語集はすべて合成されます。1つの翻訳呼び出しで3つすべてを実行できます:
コードブロック_9__
ここで注目すべきことが2つあります:
1.1. context パラメータ。 翻訳品質を向上させるために、隣接するブロックをコンテキストとして渡します。DeepL は、曖昧さを解決するためにこれを使用しますが、翻訳や請求は行いません。周囲のコンテキストが生物学のドキュメントである場合とスプレッドシートのマニュアルである場合では、"セル" に関する段落の翻訳が異なります。
2.2. モデルの選択. style_id または custom_instructions を含む要求はすべて、自動的に DeepL の quality_optimized モデルを使用します。これは最高品質の階層です。これらを latency_optimized と組み合わせることはできませんが、これは DeepL による意図的な制約です。スタイルのカスタマイズにはフル・モデルが必要です。
これが想像以上に重要な理由
ドイツ語で社内文書を作成する企業が、非公式な「du」を使用していて、翻訳されたセクションで突然正式な「Sie」に切り替わるとします。よく言えば一貫性がなく、悪く言えばプロらしくありません。形式がそれを処理します。しかし、ドイツのオフィスでは24時間制を採用しているのに、AM/PMのタイムスタンプを使っていたり、通貨記号を数字の後ではなく、前に置いていたりする文書は、形式だけでは捕らえられません。
これら(スタイルルール、カスタム命令、形式、用語集)を重ねることで、あなたのチームの誰かが書いたような翻訳ができあがります。あなたの会社の存在を知らない機械が出力したようなものではありません。
DeepLサービス層
すべてのDeepL通信は、IDeepLServiceを経由します。これは、公式の DeepL .NET SDK をラップし、スタイル・ルールの v3 API 呼び出しを処理します:
コードブロック_10__
この実装は、言語コードの正規化を処理します。DeepL では、en の代わりに EN-US または EN-GB が、pt の代わりに PT-PT または PT-BR が必要です:
CODEBLOCK_11__
バッチ翻訳では、50 項目のチャンキングを使用して、DeepL の API の制限内でスループットを最大化します:
コードブロック_12__
ドキュメント全体ではなく、古いブロックのみを送信するため、1つの編集の典型的な翻訳バッチには、40以上のブロックではなく、1~3ブロックが含まれます。94%のコスト削減はここから生まれています。
翻訳オーケストレーター
CODEBLOCK_50__は、ソース文書が変更されたときに各ブロックをどうするかを決定します。決定ツリーを見てみましょう:
コードブロック_13__
重要なビットです:**人が編集したブロックが自動的に上書きされることはありません。**翻訳者が手動でブロックを調整した場合、たとえば文化的な背景を追加したり、わかりやすく言い換えたりした場合、システムはその作業を尊重します。翻訳者が手作業でブロックを調整した場合、例えば文化的な文脈を追加したり、わかりやすい表現に変更したりした場合、システムはその作業を尊重します。
CODEBLOCK_51__が有効な機械翻訳ブロックは直ちに再翻訳されます。CODEBLOCK_52__が有効な機械翻訳ブロックは、staleとマークされ、誰かが実際にその言語でドキュメントを開いたときに翻訳されます。
翻訳トリガー: 翻訳が行われるタイミング
各言語にはタイミングを制御するTranslationTriggerがあります:
コードブロック_14__
CODEBLOCK_54__は、翻訳をすぐに最新の状態にしたい優先度の高い言語に便利です。例えば、パリに大きなオフィスがある会社のフランス語。ミュンヘンに本社がある会社のドイツ語。
TranslateOnFirstVisitは、時々必要になるけれども、APIコストをかけてまで常に最新の状態に保つ価値がない言語に便利です。誰かがその言語でドキュメントを開くと、古くなったブロックがその場で翻訳されます。
どちらのモードでも、同じ用語集解像度、同じ形式設定、同じコンテンツハッシュを使用します。唯一の違いはタイミングです。
独自のコンテンツと構造の適応
このアーキテクチャは、単なる翻訳にとどまりません。
ドイツ語の翻訳者が英語には存在しないDSGVOコンプライアンスセクションを追加する場合、ドイツ語版では新しいブロックとして追加されます。そのブロックにはSourceBlockIdはなく、独自のコンテンツとしてフラグが立てられます。翻訳元がないため、システムが再翻訳に回すことはありません。ドイツ語にしか存在しないからです。
日本語の翻訳者が箇条書きリストを番号付きリストに変更した場合(日本語のテクニカルライティングでは一般的な慣習です)、このブロックのIsStructureAdaptedフラグによって、将来の再翻訳サイクルでもこのフラグが維持されます:
コードブロック_15__
CODEBLOCK_58__フラグは、コードブロック、URL、製品名、数学的表記など、そのままコピーされるべきコンテンツを扱います。翻訳プロバイダはこれらを完全にスキップします。
まとめ
それでは、全体の流れを見ていきましょう。ロンドンのユーザが英語のソース文書の段落を編集し、ミュンヘンのオフィスではドイツ語がAlwaysTranslateに設定されています:
1.User saves. TipTapがAPIにJSONを送信します。
2.**ブロック抽出と変更検出。CreateBlocksFromDocumentAsyncはJSONを解析し、コンテンツ・ハッシュを再計算し、新旧のハッシュを比較して、実際に変更されたブロックを特定します。
3.**ドイツ語のEntryTranslationを見つけ、ドイツ語のブロックをチェックします。機械翻訳であり、ロックされておらず、人間が編集したものでもないので、再翻訳の対象です。
4.**CODEBLOCK_62__で用語集IDを解決し、GetOrSyncStyleRuleListAsync("de")でスタイル規則を解決し、形式を "more"(正式には "Sie")に設定し、曖昧性解消の文脈として隣接するブロックを渡します。
5.**単一ブロックは、用語集ID、スタイルID、形式、およびコンテキストで送信されます。
6.**翻訳されたコンテンツが格納され、SourceContentHashが同期され、ステータスがUpToDateに設定されます。40以上のブロックの代わりに1つのブロックが翻訳されました。残りの39ブロックは?そのままです。
一方、あなたの東京オフィスは日本語がTranslateOnFirstVisitに設定されています。同じ編集で、日本語の翻訳ブロックはStaleとマークされます。東京の誰かが文書を開くと、ステップ5~9がその場で行われます。その構造適応(番号付きリスト)は保存されます。そのユニークなブロックはそのままです。
翻訳エンジンはラセピの中で最も目に見える価値を提供する部分だと思います。あなたの専門用語を使い、あなたの書式規則に従い、あなたのカスタム指示に従い、あなたの語調に合わせ、あなたの翻訳者の仕事を尊重し、全文再翻訳の何分の一かのコストで翻訳します。このアーキテクチャは、そのすべてを自動化し、人間が翻訳を引き継ぎたいときには邪魔になりません。
文書による翻訳と同じDeepLエンジンは、DeepL Voiceが音声インタラクションを処理する会話型ドキュメントインターフェイスであるTalk to Docsにも対応しています。同じ用語集、同じスタイルルール、同じ形式、同じ一貫性。チームがドキュメントを読む場合も、ドキュメントと会話する場合も、言語の品質は同じです。