/** * 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'] ); } } } } } } } } View_common Ai purposeテンプレート – Raqqa

View_common Ai purposeテンプレート

Domain_guide全体のテンプレ

# ============================================================
# DOMAIN_GUIDE v1.2 TEMPLATE
# Purpose:
#   Natural Language → Semantic Interpretation → Safe SQL Generation
#
# DeciKG Contract:
#   - DeciKG MUST read ONLY the block between:
#       # --- DeciKG ONLY BEGIN ---
#       ...
#       # --- DeciKG ONLY END ---
#   - Everything outside that block is OPTIONAL and MUST be ignored by DeciKG
# ============================================================

domain: <DOMAIN_KEY>         # e.g. opportunity_pipeline / customer_360 / ...
module: <MODULE_KEY>         # sales | manufacturing | accounting | inventory | purchase | crm
locale: <LOCALE>             # ja_JP | en_US | ...

# ------------------------------------------------------------
# OPTIONAL (Dev-Portal / Human / LLM)
# -----------------------------------------------------------

下記別掲

# ------------------------------------------------------------
# --- DeciKG ONLY BEGIN ---
# ------------------------------------------------------------


          enabled: true
          value: <BASE_GRAIN_KEY>
        - key: FORCE_DISTINCT_ON_MIXED_GRAIN
          enabled: true
          forbidden: mixed_without_distinct
        - key: DISTINCT_FOR_BASE_GRAIN_COUNT
          enabled: <true_or_false>
          metric: <METRIC_KEY_FOR_COUNT>
          sql_expr: "COUNT(DISTINCT <BASE_ALIAS>.<BASE_PK>)"
        - key: REQUIRE_TENANT_FILTER
          enabled: <true_or_false>
          column: "<TENANT_COL_SQL_EXPR>"     # e.g. header.company_id
      notes:
        - "<HUMAN_NOTE_1>"
        - "<HUMAN_NOTE_2>"

  # DeciKGが「グラフとして扱う」ノードとエッジ(KG側の意味)
  graph:
    nodes:
      - name: <NODE_NAME>
        key: <NODE_KEY_COL>
        source_table: <SOURCE_TABLE>
    edges:
      - name: <EDGE_NAME>
        from: <FROM_NODE>
        to: <TO_NODE>
        fk: { from_col: <FROM_COL>, to_col: <TO_COL> }
        cardinality: "<CARDINALITY>"

  # DeciKGが推論/説明に使う「根拠」
  evidence:
    sources:
      - entity: <ENTITY_NAME>
        from_table: <TABLE_NAME>
        evidence_type: <EVIDENCE_TYPE>         # self_assessment|interaction|language_signal|ground_truth|...
        fields: [<FIELD_1>, <FIELD_2>]

  # DeciKGがSQL側に要求する「最小安全契約」
  sql_safety:
    base_grain: <BASE_GRAIN_KEY>
    distinct_key: <DISTINCT_KEY_SQL_EXPR>      # e.g. opp.opp_id
    forbid: ["COUNT(*)"]
    when_join_multiplying_tables: [<TABLE_ROLE_1>, <TABLE_ROLE_2>]
    required_patterns:
      - "<REQUIRED_PATTERN_1>"
      - "<REQUIRED_PATTERN_2>"

  # 変更ポリシー(DeciKGは運用上参照するだけでOK)
  dev_metadata:
    created_by: "<AUTHOR>"
    created_at: "<YYYY-MM-DD>"
    change_policy:
      - "decikg.sql_contract.* は破壊的変更禁止(追加のみ)"
      - "metrics/dimensions には semantic_type を必須"
# ------------------------------------------------------------
# --- DeciKG ONLY END ---
# ------------------------------------------------------------

# OPTIONAL: LLM向け補助(Dev-Portalで使うなら)
llm:
  notes:
    - "<LLM_NOTE_1>"
    - "<LLM_NOTE_2>"
# ------------------------------------------------------------
# --- DeciKG ONLY BEGIN ---
# ------------------------------------------------------------
decikg:
  contract_version: 21  # v2.1 (JOIN重複を廃止し、View入口を強化)

  # 0) 入口(View=action_xmlid)としての意味:将来NLQのルーティングに使う
  entry:
    action_xmlid: "<ACTION_XMLID>"          # e.g. opportunity_detail
    query_keys: ["<QUERY_KEY_1>"]           # e.g. ["detail"]
    description:
      ja_JP: "<このViewが返す内容を1-3行>"
      en_US: "<optional>"
    intents: ["<INTENT_TAG_1>", "<INTENT_TAG_2>"]  # e.g. ["detail","evidence","why"]
    examples:
      - utterance_ja_JP: "<自然文例>"
        query_key: "<QUERY_KEY>"
        slots: { company_id: "c001", opp_id: "op0104" }
        params: { evidence_limit: 10 }

  # 1) slots(必須ID)と、ID補完の仕方(Neo4j固定Cypherでの補完を想定)
  slots:
    required: ["<SLOT_1>", "<SLOT_2>"]  # e.g. ["company_id","opp_id"]
    optional: ["<SLOT_OPT_1>"]
    resolvers:
      # 入力が曖昧な時に、候補を返すための “検索Cypher”
      - slot: "<SLOT_NAME>"             # e.g. opp_id
        when_missing: true
        cypher: |-
          MATCH (o:Opportunity {company_id:$company_id})
          WHERE o.name CONTAINS $q
          RETURN o.opp_id AS id, o.name AS label
          ORDER BY o.updated_at DESC
          LIMIT 10

  # 2) params(件数/期間/証拠など)を機械的に制御する
  params_schema:
    evidence_limit: { type: int, default: 10, min: 0, max: 50 }
    limit:          { type: int, default: 10, min: 0, max: 50 }
    months_back:    { type: int, default: 6,  min: 0, max: 36 }   # 任意

  # 3) 出力shape契約(UI/後段が壊れないための“約束”)
  shape_contract:
    shape_normalized: "<SHAPE_NAME>"  # e.g. opportunity_detail
    fields:
      - key: "<FIELD_KEY>"
        type: "<TYPE>"
        required: true
    evidence:
      keys: ["signals", "outcomes", "email_events", "phrase_stats"]
      digest:
        required: true
        keys: ["counts", "signals_top", "email_events_top", "phrase_stats_top", "outcomes_top"]

  # 4) LLMなし実行(固定Cypher)— いまの工程3で使う
  graph_query:
    engine: "neo4j"
    registry_key:
      action_xmlid: "<ACTION_XMLID>"
      query_key: "<QUERY_KEY>"
    # ※ここにCypher本文を二重に置くかは好み。
    #    “Registryが唯一の真実”にするなら、ここは参照だけでOK。
    postprocess: "<POSTPROCESS_FN_NAME>"   # e.g. pp_opportunity_detail

  # 5) SQL生成フェーズに備える「最小機械契約」(JOINは参照にする)
  sql_contract:
    aliases:
      # Role -> Alias の強制(rewrite/guard用)
      tenant: "<ALIAS>"
      header: "<ALIAS>"
      line: "<ALIAS>"          # optional
      event: "<ALIAS>"         # optional

    quoting:
      must_quote_if_contains: ["/"]
      example: '<ALIAS>."<RAW_IDENTIFIER_WITH_SLASH>"'

    granularity:
      base: "<BASE_GRAIN_KEY>"           # e.g. opp
      allowed: ["<GRAIN_1>", "<GRAIN_2>"]
      forbidden: ["mixed_without_distinct"]

    # JOINの実体はここに書かず “参照” する(重複排除)
    join_profile_ref:
      catalog: "rpc_view_catalog"
      key: "<JOIN_PROFILE_KEY>"          # e.g. star_opp

    # metrics/dimensions は “意味定義” として残すのはOK(ただしJOIN詳細は参照)
    metrics:
      <METRIC_KEY>:
        label: { ja_JP: "<JP>", en_US: "<EN>" }
        semantic_type: "<count|amount|ratio|...>"
        grain: "<GRAIN_KEY>"
        sql_expr: "<SQL_EXPR>"
        forbid: ["COUNT(*)"]

    dimensions:
      <DIM_KEY>:
        label: { ja_JP: "<JP>", en_US: "<EN>" }
        semantic_type: "<time|category|id|...>"
        grain: "<GRAIN_KEY>"
        sql_expr: "<SQL_EXPR>"

    guardrails:
      enforce:
        - { key: NO_COUNT_STAR, enabled: true }
        - { key: QUOTE_IDENTIFIER_WITH_SLASH, enabled: true }
        - { key: BASE_GRAIN_IF_UNSPECIFIED, enabled: true, value: "<BASE_GRAIN_KEY>" }
        - { key: FORCE_DISTINCT_ON_MIXED_GRAIN, enabled: true, forbidden: "mixed_without_distinct" }
        - { key: REQUIRE_TENANT_FILTER, enabled: true, column: "<TENANT_COL_SQL_EXPR>" }
      notes: ["<HUMAN_NOTE_1>"]

  # 6) 根拠の取り扱い(Graphのevidenceと、SQLのevidenceを分けて管理できる)
  evidence:
    sources:
      - entity: "ProposalSignal"
        evidence_type: "self_assessment"
        fields: ["fit_grade","self_probability","pain_severity","created_at"]
      - entity: "EmailEvent"
        evidence_type: "interaction"
        fields: ["direction","subject","created_at"]
      - entity: "PhraseStat"
        evidence_type: "language_signal"
        fields: ["phrase","count","created_at"]

  dev_metadata:
    created_by: "<AUTHOR>"
    created_at: "<YYYY-MM-DD>"
    change_policy:
      - "decikg.* は破壊的変更禁止(追加のみ)"
# ------------------------------------------------------------
# --- DeciKG ONLY END ---
# ------------------------------------------------------------
decikg:
  contract_version: 21

  entry:
    action_xmlid: "opportunity_detail"
    query_keys: ["detail"]
    description:
      ja_JP: "案件1件の詳細と根拠(signals/email/phrase/outcome)を返す。"
    intents: ["detail","evidence","why"]
    examples:
      - utterance_ja_JP: "op0104の状況と根拠を見せて"
        query_key: "detail"
        slots: { company_id: "c001", opp_id: "op0104" }
        params: { evidence_limit: 10 }

  slots:
    required: ["company_id","opp_id"]
    resolvers:
      - slot: "opp_id"
        when_missing: true
        cypher: |-
          MATCH (o:Opportunity {company_id:$company_id})
          WHERE o.name CONTAINS $q
          RETURN o.opp_id AS id, o.name AS label
          ORDER BY o.updated_at DESC
          LIMIT 10

  params_schema:
    evidence_limit: { type: int, default: 10, min: 0, max: 50 }

  shape_contract:
    shape_normalized: "opportunity_detail"
    fields:
      - { key: "opp_id", type: "string", required: true }
      - { key: "partner_id", type: "string", required: true }
      - { key: "rep_id", type: "string", required: true }
      - { key: "name", type: "string", required: true }
      - { key: "stage", type: "string", required: true }
      - { key: "amount", type: "number", required: true }
    evidence:
      keys: ["signals","outcomes","email_events","phrase_stats"]
      digest:
        required: true
        keys: ["counts","signals_top","email_events_top","phrase_stats_top","outcomes_top"]

  graph_query:
    engine: "neo4j"
    registry_key: { action_xmlid: "opportunity_detail", query_key: "detail" }
    postprocess: "pp_opportunity_detail"

  sql_contract:
    aliases:
      tenant: "t"
      header: "opp"
      event: "ev"
    quoting:
      must_quote_if_contains: ["/"]
      example: 'opp."<x/y>"'
    granularity:
      base: "opp"
      allowed: ["opp"]
      forbidden: ["mixed_without_distinct"]
    join_profile_ref:
      catalog: "rpc_view_catalog"
      key: "star_opp"
    guardrails:
      enforce:
        - { key: NO_COUNT_STAR, enabled: true }
        - { key: REQUIRE_TENANT_FILTER, enabled: true, column: "opp.company_id" }

  evidence:
    sources:
      - entity: "ProposalSignal"
        evidence_type: "self_assessment"
        fields: ["fit_grade","self_probability","pain_severity","created_at"]
      - entity: "EmailEvent"
        evidence_type: "interaction"
        fields: ["direction","subject","created_at"]
      - entity: "PhraseStat"
        evidence_type: "language_signal"
        fields: ["phrase","count","created_at"]

DeciKG 側の「取り出し仕様」も固定できます

DeciKG はパース後にこれだけ見ればOKです:

  • decikg.sql_contract.granularity
  • decikg.sql_contract.tables
  • decikg.sql_contract.joins
  • decikg.sql_contract.metrics
  • decikg.sql_contract.dimensions
  • decikg.sql_contract.guardrails.enforce
  • decikg.sql_contract.quoting

それ以外(human, llm)は 完全に無視


なぜこの形が強いか

  • DeciKG用の情報が “一箇所” にまとまる(decikg)
  • ルールが “文字列だけ” じゃなく、guardrails.enforce機械キーで固定される
    → 実装が揺れない / 判定が簡単
  • 既存 v1.0 の意味(粒度、DISTINCT、COUNT(*)禁止、/クォート)を 一切変えずに構造化できる

このテンプレを採用する前提で確認したいのは1点だけ:
DeciKG(SQL生成/rewriter)側は どのキーを最小セットとして必須にしたいですか?

  • 例:まずは granularity / tables / joins / metrics(order_count) / quoting / guardrails だけ必須
  • dimensions や accounting/manufacturing は任意

Comments

コメントを残す

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