/** * 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'] ); } } } } } } } } Dev-portal ver.3 – Raqqa

Dev-portal ver.3

開発事項①

Dev-Portal 側で ai_purpose を Chroma upsert するときに “DeciKG 部分だけ抜く” オプション仕様です。
(将来の追加開発でそのまま使えるように、実装方針・設定・アルゴリズム・DoDまで固定します)


Spec: ai_purpose から DeciKG セクションを除去して Chroma に upsert できるようにする

0. 背景と目的

  • portal_view_common.ai_purpose は DomainGuide(YAML)を含み、Chroma に upsert される。
  • DomainGuide の中に decikg: ブロック(DeciKG専用の機械可読情報)を含める予定。
  • しかし Chroma は SQL生成/検索用途のため、DeciKG ブロックはノイズになり得る
  • よって Dev-Portal の Chroma upsert 時に、DeciKG ブロックを除去したテキストを upsert できる仕様を追加する。

1. 対象

  • 対象エンティティ:portal_view_common の Chroma ドキュメント化
  • 対象フィールド:ai_purpose(= domain_guide本文を含む可能性)
  • 前提:ai_purpose の本文は YAML であり、decikg: ブロックが存在する場合がある

2. 要求仕様(Functional Requirements)

FR-1: DeciKG ブロック除去のスイッチ

  • ai_purpose を Chroma upsert 用に doc_text 化する際に、
    DeciKG ブロックを除去するオプションを提供する。

FR-2: 除去対象の定義

  • 除去対象は YAML の decikg: キー配下(ネスト全体)。
  • decikg: が存在しない場合は何もしない(NOP)。
  • YAML として parse できない場合は、フェイルオープン
    • 既定は「削除しない(そのまま upsert)」
    • ただし設定で「正規表現で削除を試みる」fallback を有効化できる。

FR-3: 既存挙動互換

  • デフォルトは 現状互換(除去しない)
  • オプションON時のみ除去。

FR-4: 監査/観測

  • upsert された doc について、以下を diagnostics/log/metadata で追跡できること:
    • decikg_stripped: true/false
    • decikg_strip_method: yaml|regex|none|failed
    • decikg_strip_error: <error msg>(失敗時のみ)
    • doc_text_bytes_before/after(可能なら)

3. 非機能要件(Non-Functional Requirements)

  • NFR-1: Idempotency 維持
    • 同一の入力 ai_purpose と同一の strip 設定なら、生成される doc_text は決定的であること
  • NFR-2: 安全性
    • YAML parse エラーで pipeline を落とさない(fail open)
  • NFR-3: 速度
    • 文字列長が大きくても O(n) で処理できること(YAML parse は許容、regex fallback は線形)

4. インターフェース仕様(Dev-Portal)

4.1 設定(ENV)

以下の環境変数を追加する(既定は互換=無効):

  • CHROMA_STRIP_DECIKG
    • false(既定)| true
    • true の場合、ai_purposedecikg: ブロックを除去して doc_text を生成する
  • CHROMA_STRIP_DECIKG_FALLBACK
    • none(既定)| regex
    • YAML parse に失敗した場合の fallback。regex は簡易削除を試みる。
  • CHROMA_STRIP_DECIKG_REGEX_BEGIN(任意、fallback=regex の場合)
    • 既定:(?m)^\s*decikg:\s*$
  • CHROMA_STRIP_DECIKG_REGEX_END(任意)
    • 既定:(?m)^(?=\S)(次のトップレベルキー開始で止める、ただしYAML次第で誤爆あり)

注:regex fallback は YAML の厳密性がないため「ベストエフォート」。既定は none。


5. 実装仕様(アルゴリズム)

5.1 正式ルート(推奨):YAML parse → decikg 削除 → YAML dump

入力text: str(ai_purpose)
出力(stripped_text: str, diag: dict)

  1. yaml.safe_load(text) を試みる
  2. 返ったオブジェクトが dictdecikg キーを持つ場合:
    • obj.pop("decikg", None)
    • yaml.safe_dump(obj, allow_unicode=True, sort_keys=False) で再シリアライズ
  3. decikg が無い場合:
    • 入力をそのまま返す
  4. parse/dump 例外時:
    • fallback に従う

注意(重要)

  • safe_dump により YAML の見た目(コメントや改行)が変わる可能性がある。
  • 見た目の保持が重要なら、**“テキスト編集” の方針(5.2)**を推奨する。

5.2 見た目保持ルート:ブロック境界マーカー方式(将来推奨)

domain_guide v1.1 以降、decikg ブロックを明示的に囲う:

# --- DeciKG ONLY BEGIN ---
decikg:
  ...
# --- DeciKG ONLY END ---

この場合、除去は文字列操作で安全かつ決定的:

  • BEGIN〜END 行を含めて削除
  • それ以外は完全に保持

優先順位

  1. マーカーがあればマーカー方式で削除(最優先)
  2. マーカーが無ければ YAML parse
  3. parse 失敗なら fallback(regex or none)

仕様としては「マーカーがある場合は必ずそれを優先」まで固定してOK。

5.3 Regex fallback(任意)

  • decikg: 行から、次のトップレベルキー開始までを削除(ベストエフォート)
  • 誤爆の可能性があるため既定は無効

6. どこで適用するか(Dev-Portal 内の挿入ポイント)

推奨ポイント:Chroma doc_text 生成の直前

  • portal_view_common を doc 化するサービス(例:services/package または services/chroma_export)に
    sanitize_ai_purpose_for_chroma(text) を追加し、doc_text 組み立て時に適用する。

例(擬似フロー):

  1. view_common row を読み出す
  2. ai_purpose_raw = row["ai_purpose"]
  3. ai_purpose_for_chroma = sanitize(ai_purpose_raw, strip=CHROMA_STRIP_DECIKG)
  4. doc_text = render_template(..., ai_purpose=ai_purpose_for_chroma, ...)
  5. embed → upsert

7. Metadata / Diagnostics 仕様

Chroma metadata(または portal_chroma_doc.meta)に以下を追加(可能なら):

  • decikg_stripped: boolean
  • decikg_strip_method: "marker" | "yaml" | "regex" | "none" | "failed"
  • decikg_strip_error: string(failed時のみ、短く)
  • decikg_bytes_before: int
  • decikg_bytes_after: int

Chroma metadata は型制限があるので、数値/文字列/真偽値のみ。


8. テスト(DoD)

DoD-1: 互換(デフォルト)

  • CHROMA_STRIP_DECIKG=false で、生成される doc_text が現行と一致

DoD-2: strip 有効(marker)

  • マーカーありの入力で、BEGIN〜END が削除され、その他の文字列が完全一致で保持される

DoD-3: strip 有効(yaml)

  • マーカーなし・YAMLパース可能な入力で decikg: のみ削除される
  • decikg 以外のキーが残る

DoD-4: strip 有効(失敗時)

  • YAML が壊れている入力で、
    • fallback=none:そのまま返す(decikg_strip_method=failed)
    • fallback=regex:ベストエフォートで削除を試みる

DoD-5: 観測

  • processed/upserted のログまたは diagnostics に decikg_stripped が出る

9. 運用ルール(固定)

  • DomainGuide の decikg:DeciKG専用。SQL生成/LLM向けルールからは参照しない。
  • 将来、Chroma がノイズ過多になった段階で CHROMA_STRIP_DECIKG=true を有効化する。
  • その際は **“まず marker方式を入れてから”**有効化するのが推奨(見た目保持&誤爆ゼロ)。

Comments

コメントを残す

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