/** * WPML compatibility functions * * @global array $duplicated_posts Array to store the posts being duplicated. * * @package Yoast\WP\Duplicate_Post * @since 3.2 */ add_action( 'admin_init', 'duplicate_post_wpml_init' ); /** * Add handlers for WPML compatibility. */ function duplicate_post_wpml_init() { if ( defined( 'ICL_SITEPRESS_VERSION' ) ) { add_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'shutdown', 'duplicate_wpml_string_packages', 11 ); } } global $duplicated_posts; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: Renaming a global variable is a BC break. $duplicated_posts = []; /** * Copy post translations. * * @global SitePress $sitepress Instance of the Main WPML class. * @global array $duplicated_posts Array of duplicated posts. * * @param int $post_id ID of the copy. * @param WP_Post $post Original post object. * @param string $status Status of the new post. */ function duplicate_post_wpml_copy_translations( $post_id, $post, $status = '' ) { global $sitepress; global $duplicated_posts; remove_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10 ); remove_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10 ); $current_language = $sitepress->get_current_language(); $trid = $sitepress->get_element_trid( $post->ID ); if ( ! empty( $trid ) ) { $translations = $sitepress->get_element_translations( $trid ); $new_trid = $sitepress->get_element_trid( $post_id ); foreach ( $translations as $code => $details ) { if ( $code !== $current_language ) { if ( $details->element_id ) { $translation = get_post( $details->element_id ); if ( ! $translation ) { continue; } $new_post_id = duplicate_post_create_duplicate( $translation, $status ); if ( ! is_wp_error( $new_post_id ) ) { $sitepress->set_element_language_details( $new_post_id, 'post_' . $translation->post_type, $new_trid, $code, $current_language ); } } } } // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: see above. $duplicated_posts[ $post->ID ] = $post_id; } } /** * Duplicate string packages. * * @global array() $duplicated_posts Array of duplicated posts. */ function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: renaming the function would be a BC-break. global $duplicated_posts; foreach ( $duplicated_posts as $original_post_id => $duplicate_post_id ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $original_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $original_post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $new_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $duplicate_post_id ); if ( is_array( $original_string_packages ) ) { foreach ( $original_string_packages as $original_string_package ) { $translated_original_strings = $original_string_package->get_translated_strings( [] ); foreach ( $new_string_packages as $new_string_package ) { $cache = new WPML_WP_Cache( 'WPML_Package' ); $cache->flush_group_cache(); $new_strings = $new_string_package->get_package_strings(); foreach ( $new_strings as $new_string ) { if ( isset( $translated_original_strings[ $new_string->name ] ) ) { foreach ( $translated_original_strings[ $new_string->name ] as $language => $translated_string ) { do_action( // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. 'wpml_add_string_translation', $new_string->id, $language, $translated_string['value'], $translated_string['status'] ); } } } } } } } } Re:翻訳とchromadb設計 – Raqqa

Re:翻訳とchromadb設計

  • 埋め込みテキスト=日本語で作る
  • メタデータに厳密な model 等を持たせて後段(SQL組立)で使う
  • (英訳は保持してOKだが)検索の主軸は日本語にする

以下、「何を doc に入れて」「何を metadata に入れるか」の最小・実用設計です。

コレクション & ID

  • コレクション(日本語用)
    • portal_field_ja
    • portal_view_common_jaai_purposehelp は別ドキュメント)
  • idnatural_key + "::ja"
    • 例)field::sale.order::partner_id::ja
    • 例)view_common::sale.action_quotations::help::ja

既に英訳インデックスを作る場合は ::en で並存可能(任意)。

埋め込みテキスト(doc)テンプレ(日本語)

できるだけ短く・決め打ちで。

Field 用(doc)

モデル: 受注 (sale.order)
フィールド: 顧客 (partner_id)
型: many2one → 取引先 (res.partner)
用途: 受注と顧客を関連付ける。

View(view_common, ai_purpose / help)用(doc)

アクション: 見積 (sale.action_quotations)
モデル: 受注 (sale.order)
目的: [ai_purpose の日本語文]
説明: [help_ja_text]

※ カッコ内の英字は“補助”。ユーザーは知らなくても、モデル当ての精度が上がります。行数は4〜6行に抑えるのがコツ。

メタデータ(metadata)設計

絞り込みと SQL 生成に必要な厳密情報をここに。日本語検索は doc 任せでOK。

共通

  • entity: "field" / "view_common"
  • lang: "ja"
  • model: "sale.order"必須
  • model_table: "sale_order"
  • label_ja: 日本語ラベル
  • purpose_ja: 日本語用途文(view_common の場合は ai_purpose or help_ja_text
  • source_hash: 原文+主要値のハッシュ(再埋め込み判定)
  • updated_at: ISO 文字列

Field だけ

  • field_name: "partner_id"
  • dtype: "many2one"
  • relation_model: "res.partner"(あれば)

View(view_common)だけ

  • action_xmlid: "sale.action_quotations"
  • target: "ai_purpose" or "help"
  • primary_view_type: "list"
  • view_types: ["list","form","search"]

(任意の拡張)

  • synonyms_ja: ["見積","受注","注文","SO"](長くしすぎない)
  • label_en / purpose_en: 英訳のバックアップ(使わなければ空でOK)

なぜこの分離か

  • ユーザーの入力は完全に日本語 → doc(日本語)で類似が取りやすい
  • SQL の確定は厳密情報が必要 → metadata の model / model_table / field_name / dtype でミスを防ぐ
  • (任意)英訳を metadata に持っておくと、将来の社内標準化や他言語対応に展開しやすい

2段リトリーバル(日本語のまま)

  1. ルータ(portal_view_common_ja)で「やりたいこと」→ 候補モデルを推定(Top-N)。
  2. portal_field_jametadata.model で絞り、日本語 doc でフィールド特定 → SQL 組立。

ユーザーはテーブル名を知らなくてOK。こちらでモデル当て→フィールド確定を行います。

ドキュメント例(最終イメージ)

A) フィールド field::sale.order::partner_id::ja

  • doc モデル: 受注 (sale.order) フィールド: 顧客 (partner_id) 型: many2one → 取引先 (res.partner) 用途: 受注と顧客を関連付ける。
  • metadata { "entity": "field", "lang": "ja", "model": "sale.order", "model_table": "sale_order", "field_name": "partner_id", "dtype": "many2one", "relation_model": "res.partner", "label_ja": "顧客", "purpose_ja": "受注と顧客を関連付ける。", "updated_at": "2025-09-21T10:00:00Z" }

B) ビュー(help) view_common::sale.action_quotations::help::ja

  • doc アクション: 見積 (sale.action_quotations) モデル: 受注 (sale.order) 目的: — 説明: この画面では見積の確認、条件交渉、受注への変換を行います。
  • metadata { "entity": "view_common", "lang": "ja", "target": "help", "action_xmlid": "sale.action_quotations", "model": "sale.order", "model_table": "sale_order", "primary_view_type": "list", "view_types": ["list","form","search"], "label_ja": "見積", "purpose_ja": "この画面では見積の確認、条件交渉、受注への変換を行います。", "updated_at": "2025-09-21T10:00:00Z" }

実運用メモ

  • 埋め込みモデルは日本語対応(多言語)を選択。
  • doc は短く、metadata は厳密に
  • synonyms_ja を増やしすぎるとノイズになるので数語まで
  • 既存の英訳も保持したい場合は ::en でもう一枚作れば共存できますが、検索デフォルトは lang='ja' フィルタで。

この形なら、ユーザーは完全に日本語のまま、こちら側でモデル特定と SQL 組立を安定して行えます。

この情報を保持するchromaテーブルを作成するためのSQLはどのようになりますか。upsertする起点と対象のベースはtranslateテーブルです。なので、traslateテーブルとchromaテーブルを作成するSQLを出してください。

了解です。portal_translate を起点に portal_chroma_doc(Chroma 送信用ステージング)へ upsert する前提で、両テーブルを作る SQL をまとめました。
(ベクタは Chroma 側で持つ想定。ここでは「埋め込む日本語doc+厳密メタ+状態管理」を保持します)

BEGIN;

-- 共通: updated_at を自動更新するトリガ関数(既存なら置換OK)
CREATE OR REPLACE FUNCTION public.tg_touch_updated_at()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
  NEW.updated_at := now();
  RETURN NEW;
END
$$;

-- =========================================================
-- 1) 翻訳キュー本体: portal_translate
--    UI/抽出エンドポイントからここに集約し、翻訳→Chroma準備の進捗を管理
-- =========================================================
DROP TABLE IF EXISTS public.portal_translate CASCADE;

CREATE TABLE public.portal_translate (
  id                   bigserial PRIMARY KEY,

  -- 自然キー(対象を一意に特定)
  -- 例: field::sale.order::partner_id / view_common::sale.action_quotations::help
  natural_key          text      NOT NULL,

  -- 対象の種別とモデル(横断検索の主キー)
  entity               text      NOT NULL,   -- 'field' | 'view_common' | 'model' | 'tab' | 'smart_button' | 'menu'
  model                text,                 -- 例: 'sale.order'(可能な限り埋める)

  -- 翻訳の方向(抽出元→翻訳先)
  source_lang          text      NOT NULL DEFAULT 'ja_JP',
  target_lang          text      NOT NULL DEFAULT 'en_US',

  -- 翻訳元(抽出時に入れる最小情報)
  label                text,                 -- 表示名など
  purpose              text,                 -- 用途/説明など
  source_payload       jsonb     NOT NULL DEFAULT '{}'::jsonb,  -- 補助メタ(任意)

  -- 翻訳結果
  translated_label     text,
  translated_purpose   text,

  -- 処理状態
  status               text      NOT NULL DEFAULT 'pending',   -- 'pending'|'translated'|'ready_for_chroma'|'done'|'failed'
  error_message        text,

  -- 監査
  created_at           timestamptz NOT NULL DEFAULT now(),
  updated_at           timestamptz NOT NULL DEFAULT now(),

  CONSTRAINT uq_portal_translate UNIQUE (natural_key, target_lang),
  CONSTRAINT ck_pt_status CHECK (status IN ('pending','translated','ready_for_chroma','done','failed'))
);

CREATE INDEX IF NOT EXISTS idx_pt_entity        ON public.portal_translate(entity);
CREATE INDEX IF NOT EXISTS idx_pt_model         ON public.portal_translate(model);
CREATE INDEX IF NOT EXISTS idx_pt_status        ON public.portal_translate(status);
CREATE INDEX IF NOT EXISTS idx_pt_created_at    ON public.portal_translate(created_at);

CREATE TRIGGER trg_touch_portal_translate
BEFORE UPDATE ON public.portal_translate
FOR EACH ROW EXECUTE FUNCTION public.tg_touch_updated_at();

-- =========================================================
-- 2) Chroma送信ステージング: portal_chroma_doc
--    portal_translate を起点に、実際に Chroma へ upsert するドキュメントを保持
--    ※ 日本語で引く前提のため lang は 'ja' を既定とし、必要なら 'en' も並存可
-- =========================================================
DROP TABLE IF EXISTS public.portal_chroma_doc CASCADE;

CREATE TABLE public.portal_chroma_doc (
  id                 bigserial PRIMARY KEY,

  -- 元翻訳行への参照(再生成・監査用)
  translate_id       bigint REFERENCES public.portal_translate(id) ON DELETE CASCADE,

  -- 識別(Chroma の doc_id としても使用)
  natural_key        text    NOT NULL,                   -- 例: field::sale.order::partner_id
  lang               text    NOT NULL DEFAULT 'ja',      -- 'ja' | 'en' 等
  collection         text    NOT NULL,                   -- 例: 'portal_field_ja' / 'portal_view_common_ja'
  doc_id             text    NOT NULL,                   -- 例: natural_key || '::' || lang

  -- 検索用の本文(埋め込み対象。日本語で短く決め打ち)
  doc_text           text    NOT NULL,

  -- 厳密情報(SQL組立・フィルタ用)
  entity             text    NOT NULL,                   -- 'field' | 'view_common' ...
  model              text    NOT NULL,                   -- 例: 'sale.order'
  model_table        text,
  field_name         text,                               -- entity='field' の場合
  action_xmlid       text,                               -- entity='view_common' の場合
  target             text,                               -- 'ai_purpose' | 'help'(view_commonのとき)

  -- 追加メタ(自由項目。view_types や synonyms 等はここへ)
  metadata           jsonb   NOT NULL DEFAULT '{}'::jsonb,

  -- 変更検知・進捗
  source_hash        text,                               -- 生成元(label/purpose等)のハッシュ
  status             text    NOT NULL DEFAULT 'queued',  -- 'queued'|'upserted'|'skipped'|'failed'
  error_message      text,
  embedded_at        timestamptz,                        -- 埋め込み計算完了(任意)
  upserted_at        timestamptz,                        -- Chroma upsert 完了

  -- 監査
  created_at         timestamptz NOT NULL DEFAULT now(),
  updated_at         timestamptz NOT NULL DEFAULT now(),

  -- 一意・検索
  CONSTRAINT uq_pcd_doc UNIQUE (doc_id),
  CONSTRAINT ck_pcd_status CHECK (status IN ('queued','upserted','skipped','failed'))
);

-- 代表インデックス(メタでの絞り込みを速く)
CREATE INDEX IF NOT EXISTS idx_pcd_collection   ON public.portal_chroma_doc(collection);
CREATE INDEX IF NOT EXISTS idx_pcd_lang         ON public.portal_chroma_doc(lang);
CREATE INDEX IF NOT EXISTS idx_pcd_entity       ON public.portal_chroma_doc(entity);
CREATE INDEX IF NOT EXISTS idx_pcd_model        ON public.portal_chroma_doc(model);
CREATE INDEX IF NOT EXISTS idx_pcd_field_name   ON public.portal_chroma_doc(field_name);
CREATE INDEX IF NOT EXISTS idx_pcd_action_xmlid ON public.portal_chroma_doc(action_xmlid);
CREATE INDEX IF NOT EXISTS idx_pcd_status       ON public.portal_chroma_doc(status);
CREATE INDEX IF NOT EXISTS idx_pcd_created_at   ON public.portal_chroma_doc(created_at);
CREATE INDEX IF NOT EXISTS idx_pcd_metadata_gin ON public.portal_chroma_doc USING GIN (metadata);

CREATE TRIGGER trg_touch_portal_chroma_doc
BEFORE UPDATE ON public.portal_chroma_doc
FOR EACH ROW EXECUTE FUNCTION public.tg_touch_updated_at();

COMMIT;

補足(運用の指針)

  • upsertの起点portal_translatestatus in ('translated','ready_for_chroma') などを対象に生成)。
  • doc_idnatural_key || '::' || lang を推奨(多言語並存に強い)。
  • collectionportal_field_ja / portal_view_common_ja(日本語検索用)。英語も必要なら _en を併設。
  • metadata には view_typessynonyms_ja を必要最小で(入れすぎはノイズ)。
  • status 運用queued → upserted(失敗時は failederror_message)。再生成時は source_hash で差分検知。

この2表があれば、「抽出→翻訳→(必要なら整形)→ Chroma upsert」の全工程を冪等に回せます。

あなた:

‘translated’|’ready_for_chroma’| この状態は同じに見えますが。何が起点でready_for_chromaに変わりますか。

ChatGPT:

思考時間: 12s

いい質問です。短くまとめると——

  • translated
    →「翻訳が終わった(テキスト素材が揃った)状態」。
    ここではまだ Chroma 用の doc(本文)と厳密メタは“未パッケージ”。
  • ready_for_chroma
    →「Chroma にそのまま upsert できる “完成パッケージ” が作れた状態」。
    具体的には doc_text / collection / doc_id / 必須メタが決定・検証済み。

何が起点で ready_for_chroma になるか(トリガ)

翻訳ワーカーの次の段階である「パッケージング & 検証」ジョブが、以下を満たしたときに ready_for_chroma に遷移させます。

共通のゲート(field / view_common)

  • entity が対象(field or view_common
  • model が非NULL(厳密なモデル名が特定できている)
  • collection を決定
    • entity=fieldportal_field_ja
    • entity=view_commonportal_view_common_ja
  • doc_id を決定(natural_key || '::ja' など)
  • doc_text日本語テンプレで組み立て(短文・改行4〜6行)
  • 必須メタが揃っている
    • field: field_name, dtype(任意で relation_model), model_table
    • view_common: action_xmlid, primary_view_type, view_types
  • source_hash を算出(doc_text+主要メタ)。既存 portal_chroma_doc と同じなら 再埋め込みをスキップできる
  • ✅ 長さ・文字種の簡易チェック(空/極端に長い/HTML混入などがない)

上をすべて満たしたら、その translate 行は ready_for_chroma
満たせない場合は:

  • リトライ可能なら translated のままにして error_message に原因を記録
  • 決定的に不足(例:field_name が存在しない)なら failed

補足:今回の方針では 埋め込みは日本語です。翻訳結果(英語)がある場合でも、doc_text は日本語素材(label/purpose の原文)から組み立てます。英訳は将来用に保持するだけ。


具体的な段階イメージ(最小ステートマシン)

  1. pending … 抽出直後
  2. translated … 翻訳完了(※英訳を作った/または「英訳不要」と判定)
  3. ready_for_chromaパッケージング & 検証を通過(doc_text+メタ確定)
  4. (別テーブル portal_chroma_docqueued で行生成)→ upsert 実行 → upserted / failed

もし段階を減らしたいなら、translated を省いて**パッケージングが終わった瞬間に ready_for_chroma**へ直行でもOKです(= pending → ready_for_chroma)。運用上の分かりやすさで決めてください。


よくある“詰まり”と対処

  • model_table が取れず stop → portal_model から補完 or 規約(._ 置換)で生成
  • field の dtype が空 → portal_fields と JOIN で補完(なければ text で一旦通す運用も可)
  • view_common の action_xmlid 不整合 → portal_view_common を再UPSERT(action-centricの前提を崩さない)

まとめ
translated は“素材が揃った済み”、ready_for_chroma は“梱包が完了し、いつでも Chroma に投げられる”状態です。
トリガは パッケージング&検証ジョブが必須フィールドを埋め、doc_text を組み立てた瞬間、と覚えておけばOKです。

あなた:

コーディングの方針を決めます。 1.translateテーブルにportalの各エンドポイントから翻訳対象を抜き出して、upsert 2.翻訳してtranslateテーブルの翻訳結果を埋める 3.各エンドポイントの日本語と対になった英語部分を埋める 4.portal_chroma_doc のフィールドに翻訳結果をupsert 5.情報がそろったところで、chromadbへinsert このような流れになると思うのですが、どうでしょうか。

方針はほぼ完璧です。実装で迷わないように、各ステップの「ゴール」「入力→出力」「遷移条件」「注意点」だけ詰めておきます。(コードは示しません)

全体像(ステートと責務)

  • portal_translate.status: pending → translated → ready_for_chroma → done(失敗は failed
  • portal_chroma_doc.status: queued → upserted(失敗は failed
  • Chroma 収容対象: fieldview_common のみ(日本語docで検索、厳密メタはmetadataに)

1) 抽出 → portal_translate へ UPSERT

  • ゴール: 翻訳候補を一意(natural_key, target_lang)でキューイング。
  • 入力: 各エンドポイント(model/field/view_common/tab/smart_button/menu…)の“ラベル/用途”.
  • 出力: portal_translate 行を UPSERT(status='pending')。
  • 注意:
    • natural_key 規約で一意化(例:field::<model>::<field_name> / view_common::<action_xmlid>::help
    • 今回 Chroma 対象は field・view_common だが、translate は他も受け付けてOK(将来拡張の足場)。
    • 「日本語を含む & 英語未入力」などの抽出条件はエンドポイント側で適用。

2) 翻訳 → portal_translate を更新

  • ゴール: translated_label / translated_purpose を埋め、status='translated'
  • 入力: status='pending' の translate 行。
  • 出力: 同行の翻訳結果を保存。
  • 注意:
    • 品質チェック(空/機械訳のゴミ/極端な長文化)を入れて failed へ弾く基準を用意。
    • 冪等性: 同じ原文は同一結果になるよう source_hash を使った二度訳防止を推奨。

3) 元テーブル(portal_*)へ英語を書き戻し(推奨・任意)

  • ゴール: ポータル本体にも対訳を反映(閲覧UIや将来の整合性に効く)。
  • 対象:
    • view_common: ai_purpose_i18n.en_UShelp_en_text
    • field: 必要なら label_i18n.en_US(運用方針次第)
  • 出力: 対象行の英語列を更新。
  • 注意:
    • このステップは Chroma 収容の前提ではない(スキップ可)。ただし組織内の表記統一に有益。
    • 上書きポリシー(手で書かれた英語を守るか)を明確に。

4) パッケージング → portal_chroma_doc へ UPSERT

  • ゴール: 日本語 doc_text + 厳密メタの“完成パッケージ”を作り、queued で用意。
  • 入力: portal_translateentity in ('field','view_common') かつ status='translated'
  • 出力: portal_chroma_doc を UPSERT(doc_id=natural_key||'::ja', collection='portal_field_ja' / 'portal_view_common_ja', status='queued'
  • 遷移: パッケージング成功時に 該当 translate 行は status='ready_for_chroma'
  • 必須メタ:
    • 共通: entity, model, model_table, source_hash
    • field: field_name, dtype(あれば relation_model
    • view_common: action_xmlid, primary_view_type, view_types
  • 注意:
    • doc_text は日本語(短く、決め打ちテンプレで4–6行)。
    • source_hash が既存 portal_chroma_doc と同じなら doc差分なしとして queued にしない運用も可。

5) Chroma へ upsert

  • ゴール: ベクトルDBに投入し、検索可能化。
  • 入力: portal_chroma_doc.status='queued'
  • 出力: 成功で status='upserted', upserted_at 記録。失敗は failed+error_message
  • 検索運用:
    • 1段目(モデル当て): portal_view_common_ja を検索 → 候補モデルを抽出(metadata.model)
    • 2段目(フィールド確定): 候補モデルで portal_field_ja を metadata filter して検索 → SQL組立

ステートまとめ(何がトリガで変わる?)

  • pending → translated : 翻訳ワーカーが結果を書けたら
  • translated → ready_for_chroma : パッケージング(doc_text/collection/doc_id/必須メタ 完了)
  • ready_for_chroma(translate側)と queued(chroma_doc側)同時に作る/更新のが分かりやすい
  • queued → upserted : Chroma upsert 成功

実装のコツ(非コード)

  • 冪等性: すべて UPSERT + source_hash 比較で無駄処理を避ける
  • 分離: 「翻訳」「パッケージング」「Chroma upsert」を独立ジョブ化(再実行OK)
  • 監査: それぞれ updated_aterror_message を必ず更新
  • 対象の限定: Chroma 収容は確実に entity in ('field','view_common') のみにフィルタ

最終コメント

ご提案の 1→2→3→4→5 の流れでOKです。
運用上は 3(書き戻し)は任意4(パッケージング)で“日本語doc+厳密メタ”を作るのが肝—ここさえブレなければ検索とSQL生成が安定します。

あなた:

OpenAPIだとどのような実装になりますか。コードではなく、エンドポイントはこうなって、、のような説明でお願いします。

ChatGPT:

思考時間: 18s

いいですね。OpenAPI(REST)で切るなら、「抽出 → 翻訳 → 書き戻し → パッケージング → Chroma upsert」の5段をそのままタグエンドポイントに落とし込むのが分かりやすいです。以下は「エンドポイントはこうなって…」の形で、入出力の最小項目レスポンス例だけを示します(コードなし)。


認証・共通

  • 認証: Bearer(JWT など)
  • 冪等化: X-Idempotency-Key ヘッダ(POST/PUT系は任意で受ける)
  • エラー: 標準化(problem+json 推奨){ type, title, detail, status }

① 抽出(translate へ UPSERT)— Extract

目的: ポータル各エンドポイントから“翻訳対象(日本語ラベル/用途)”だけをtranslateテーブルに集約

POST /extract/view_common

  • 説明: portal_view_common から ai_purpose / help_ja_text を抽出して portal_translate にUPSERT
  • リクエスト(例)
{
  "action_xmlids": ["sale.action_quotations", "sale.action_orders"],
  "targets": ["ai_purpose", "help"],   // 省略時は両方
  "mode": "upsert_if_changed"          // upsert | skip_existing | upsert_if_changed
}
  • レスポンス(例)
{
  "picked": 2,
  "inserted": 2,
  "updated": 0,
  "skipped_no_ja": 0,
  "skipped_has_en": 0,
  "details": [
    {"action_xmlid": "sale.action_quotations", "target": "help", "natural_key": "view_common::sale.action_quotations::help", "status": "pending"}
  ]
}

POST /extract/field

  • 説明: portal_fields から label_i18n / notes(日本語) を抽出して portal_translate にUPSERT
  • リクエスト(例)
{
  "models": ["sale.order", "account.move"],  // 指定なしは全モデルから抽出
  "fields": ["partner_id","amount_total"],   // 任意; 未指定で全フィールド
  "mode": "upsert_if_changed"
}
  • レスポンスは /extract/view_common と同型

※ 将来拡張で /extract/menu など追加可(translate は受け皿広め、Chroma対象は field / view_common のみ)。


② 翻訳(translate を更新)— Translate

目的: portal_translatetranslated_label / translated_purpose を埋め、status='translated'

POST /translate/run

  • 説明: status='pending' を対象に翻訳ジョブを実行(件数制限・タイムアウト対応)
  • リクエスト(例)
{
  "limit": 200,
  "source_lang": "ja_JP",
  "target_lang": "en_US",
  "entities": ["field","view_common"]   // 任意フィルタ
}
  • レスポンス(例)
{
  "picked": 180,
  "translated": 175,
  "failed": 5,
  "samples": [
    {
      "natural_key": "field::sale.order::partner_id",
      "entity": "field",
      "translated_label": "Customer",
      "translated_purpose": "Links a sales order to its customer.",
      "status": "translated"
    }
  ]
}

GET /translate

  • 説明: translate 行の一覧(フィルタ/ページング)
  • クエリ例: ?status=pending&entity=view_common&limit=50&cursor=...

③ ポータル側へ英語を書き戻し(任意・推奨)— Writeback

目的: 元テーブルにも対訳を反映(組織内の整合・UI多言語化に)

POST /writeback/view_common

  • 説明: ai_purpose_i18n.en_US / help_en_text を埋める
  • リクエスト(例)
{
  "action_xmlids": ["sale.action_quotations"],
  "targets": ["ai_purpose","help"],
  "mode": "skip_if_exists"   // 上書きポリシー: overwrite | skip_if_exists
}
  • レスポンス(例)
{"updated": {"ai_purpose": 1, "help": 1}, "skipped": 0}

POST /writeback/field

  • 説明: 必要に応じ label_i18n.en_US を更新
  • リク/レスは上と同型

書き戻しは Chroma投入の前提ではありません。スキップ可。


④ パッケージング(chroma_doc へ UPSERT)— Package

目的: 日本語docテキスト+厳密メタの“完成パッケージ”を portal_chroma_doc に用意し、translate を ready_for_chroma へ更新

POST /chroma/package

  • 説明: portal_translate.status='translated' から field / view_common だけ拾い、テンプレに沿って doc_text を生成し、portal_chroma_doc にUPSERT(status='queued'
  • リクエスト(例)
{
  "entities": ["field","view_common"],
  "lang": "ja",
  "collections": {
    "field": "portal_field_ja",
    "view_common": "portal_view_common_ja"
  },
  "limit": 500
}
  • レスポンス(例)
{
  "queued": 320,
  "skipped_no_change": 45,
  "failed": 3,
  "samples": [
    {
      "doc_id": "field::sale.order::partner_id::ja",
      "collection": "portal_field_ja",
      "model": "sale.order",
      "status": "queued"
    }
  ]
}

GET /chroma/docs

  • 説明: portal_chroma_doc の一覧。?status=queued&entity=field&model=sale.order

⑤ Chroma upsert — Chroma

目的: portal_chroma_doc.status='queued' を Chroma に upsert し、upserted

POST /chroma/upsert

  • 説明: 指定のコレクション/件数を Chroma に upsert
  • リクエスト(例)
{
  "collections": ["portal_field_ja","portal_view_common_ja"],
  "limit": 1000,
  "dry_run": false
}
  • レスポンス(例)
{
  "processed": 900,
  "upserted": 896,
  "skipped": 2,
  "failed": 2,
  "errors": [
    {"doc_id": "view_common::sale.action_quotations::help::ja", "reason": "Chroma timeout"}
  ]
}

付随:参照系・運用補助

GET /status/summary

  • 説明: ステージごとの件数サマリ
  • レスポンス(例)
{
  "translate": {"pending": 120, "translated": 450, "ready_for_chroma": 430, "failed": 3},
  "chroma_doc": {"queued": 420, "upserted": 390, "failed": 5}
}

GET /samples/trace

  • 説明: natural_key を渡すと、抽出→翻訳→パッケージ→upsert の通し状態を返す
  • クエリ例: ?natural_key=view_common::sale.action_quotations::help

スキーマ(OpenAPI の components で定義しておくと便利)

  • NaturalKey(string; 規約: entity::...
  • Entity(enum: field, view_common, …)
  • TranslateRow(入出力で共有)
    • natural_key, entity, model, source_lang, target_lang, label, purpose, translated_label, translated_purpose, status, updated_at
  • ChromaDoc
    • doc_id, natural_key, lang, collection, doc_text, entity, model, model_table, field_name?, action_xmlid?, target?, metadata, status, updated_at

フローの期待遷移(状態機械)

  • translate: pending → translated → ready_for_chroma → done | failed
  • chroma_doc: queued → upserted | failed
  • 遷移トリガ
    • /translate/run 実行 → translated
    • /chroma/package 実行 → translate=ready_for_chroma & chroma_doc=queued
    • /chroma/upsert 実行 → chroma_doc=upserted

実装の要点(API設計上)

  • すべての POST は冪等X-Idempotency-Key
  • 部分実行に強い(limitentitiescollections で絞る)
  • 結果はサマリ+サンプル(大量配列は返さない)
  • 書き戻しは任意(組織運用に合わせて on/off)

Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です