以前のアーキテクチャの投稿](/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 に翻訳をどのように作成するかを平易な言葉で指示します:
コードブロック_7__
テナントからの実際の例:
- CODEBLOCK_26__親しみやすいドキュメントを求める新興企業向け
- CODEBLOCK_27__ドイツの法律事務所向け
"Translate 'deployment' as 'Bereitstellung', never 'Deployment'"シンプルな用語集のマッピングだけでなく、文脈に依存したハンドリングが必要な用語向け- CODEBLOCK_29__ 英国を拠点とする企業で英語の異言語間の翻訳を行う場合
- CODEBLOCK_30__ ヨーロッパの慣例に合わせるため
カスタム命令は、用語集エントリに収まりきらないドメイン特有の慣習に対して本当に強力です。用語集は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.モデル選択. 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__は、翻訳をすぐに最新のものにしたい優先度の高い言語に便利です。例えば、パリに大きなオフィスがある会社のフランス語。ミュンヘンに本社がある会社のドイツ語。
CODEBLOCK_55__は、たまに必要になるけれども、APIコストをかけてまで常に最新の状態に保つ価値がない言語に便利です。誰かがその言語でドキュメントを開くと、古くなったブロックがその場で翻訳されます。
どちらのモードも、同じ用語集解像度、同じ形式設定、同じコンテンツハッシュを使用します。唯一の違いはタイミングです。
独自のコンテンツと構造の適応
ここが、単なる翻訳にとどまらない、アーキテクチャの真価が発揮されるところです。
ドイツ語の翻訳者が、英語には存在しないDSGVOコンプライアンス・セクションを追加する場合、ドイツ語版に新しいブロックとして追加する。そのブロックにはSourceBlockIdはなく、独自のコンテンツとしてフラグが立てられる。翻訳元が存在しないため、システムが再翻訳に回すことはありません。それはドイツ語にしか存在しません。
日本語の翻訳者が箇条書きリストを番号付きリストに変更した場合(日本語のテクニカルライティングでは一般的な慣習です)、このブロックのIsStructureAdaptedフラグによって、将来の再翻訳サイクルでもこのフラグが維持されます:
コードブロック_15__
CODEBLOCK_58__フラグは、コードブロック、URL、製品名、数学的表記など、そのままコピーされるべきコンテンツを扱います。翻訳プロバイダーはこれらを完全にスキップします。
すべてをまとめる
それでは、全体の流れを見ていきましょう。ロンドンのユーザーが英語のソース文書の段落を編集し、ミュンヘンのオフィスではドイツ語がAlwaysTranslateに設定されています:
1.**TipTapはAPIにJSONを送信します。
2.ブロック抽出. CreateBlocksFromDocumentAsync がJSONを解析し、コンテンツハッシュを再計算します。
3.**システムは新旧のハッシュを比較し、変更されたブロックを特定する。
4.ドイツ語のEntryTranslationを見つけ、ドイツ語のブロックをチェックする。
5.ブロックは機械翻訳されている。 ロックされておらず、人間が編集したものでもない → 再翻訳の対象
6.GetOrSyncDeepLGlossaryIdAsync("en", "de")は用語集IDを返します(ダーティな場合は同期します)。
7.スタイルルールの解決. GetOrSyncStyleRuleListAsync("de") は、設定されたフォーマットルールとカスタム命令を含む DeepL style_id を返します。
8.形式 + コンテキスト. 形式を "more" に設定 (形式は "Sie")、曖昧性解消のコンテキストとして隣接ブロックを渡し、書式を保持する
9.9. DeepLコール. 単一のブロックが、用語集ID、スタイルID、書式、およびコンテキストとともに送信される。
10.**翻訳されたコンテンツが保存され、SourceContentHashが同期され、ステータスがUpToDateに設定される。
11.**40以上のブロックの代わりに1つのブロックが翻訳された。残りの39ブロックは?そのまま。
一方、あなたの東京オフィスは日本語をTranslateOnFirstVisitに設定している。同じ編集で、日本語の翻訳ブロックはStaleとマークされます。東京の誰かがドキュメントを開くと、ステップ5-9がその場で行われる。その構造適応(番号付きリスト)は保存される。そのユニークなブロックはそのままの位置にとどまる。
翻訳エンジンはラセピの中で最も目に見える価値を提供する部分です。あなたの専門用語を使い、あなたの書式規則に従い、あなたのカスタム指示に従い、あなたの語調に合わせ、あなたの翻訳者の仕事を尊重し、全文再翻訳の何分の一かのコストで翻訳します。このアーキテクチャは、これらすべてを自動化し、人間が翻訳を引き継ぎたいときには邪魔になりません。
文書翻訳と同じDeepLエンジンは、DeepL Voiceが音声インタラクションを処理する、会話型ドキュメントインターフェイスであるTalk to Docsにも対応しています。同じ用語集、同じスタイルルール、同じ形式、同じ一貫性。チームがドキュメントを読む場合も、ドキュメントと会話する場合も、言語品質は同じです。