Engineering content-gen 12 min read

Brand rule の機械チェック化 — 自己言及 vs 他社言及の文脈判別パターン

Yakumo を『会社』と書かないルールを gate に組み込む設計。proximity 解析・allow-list・正規表現パターンを組み合わせた context-aware 検出の実装例を解説する。

公開 2026-05-24 森本拓見

pre-publish gate に brand rule のチェックを組み込むとき、最初に出てくるアイデアは禁則語を配列に追加することだ。だがこのアプローチは「自己言及と他社言及を区別できない」という壁に当たる。brand.tsforbiddenWords に「会社」を追加した瞬間、株式会社マーケットエンタープライズ(他社の正式名)や 自称 AI 開発会社(他社批判)まで block し始める。context-aware な検出が必要な理由がここにある。

→ gate の全体構造は owned media 運用エンジン mcluhan の構造設計 を先に読んでほしい。本記事は G10(brand rule の自己言及チェック)の設計に特化する。


なぜ flat な禁則語リストでは機能しないか

false positive が発生する 4 つの legitimate パターン

flat な string match で「会社」「企業」を禁則語として検出すると、以下の記述が全部 fail になる。

  1. 他社の正式名称: 「株式会社マーケットエンタープライズで AI DX 推進責任者を務めた」
  2. 他社カテゴリ対比: 「Web 制作会社とは違い、八雲は業務再設計を主軸に置く」
  3. 他社批判: 「自称 AI 開発会社の多くは API を叩くだけで業務設計をしていない」
  4. 過去経歴: 「以前共同創業した会社での経験をベースに」

これら 4 パターンはいずれも BRAND.md のルール例外条項(Yakumo 自身を会社と呼ばない。他社への言及は問題ない)に明示的に該当する。gate がこれらを block するのは、ルールの意図と実装が乖離している証拠だ。

brand rule の対象は「Yakumo 自身への言及」のみ

ルールを正確に言い直すと「Yakumo を主語または近接した位置で 会社 / 企業 / firm / company と表記しない」だ。主語が他の組織であれば問題ない。

この区別を実装するには、文字列のマッチだけでなく「誰を指しているか」の文脈判定が必要になる。完全な NLP 解析(形態素解析・dependency parsing)は実装コストが高すぎる。現実解は proximity 解析 + allow-list + 正規表現パターンの組み合わせだ。


proximity 解析による自己言及の検出

前後 N 文字以内に Yakumo / 八雲 が登場するかの判定ロジック

自己言及の判定に使う proximity ウィンドウの考え方は単純だ。会社 というワードが見つかったとき、その前後 N 文字以内に Yakumo / 八雲 / 私たち が存在すれば self-reference と判定する。

function isProximateSelfReference(body: string, match: RegExpMatchArray): boolean {
  const PROXIMITY_WINDOW = 50; // 文字数(前後それぞれ)
  const SELF_IDENTIFIERS = ['Yakumo', '八雲', '私たち'];

  const start = Math.max(0, match.index! - PROXIMITY_WINDOW);
  const end = Math.min(body.length, match.index! + match[0].length + PROXIMITY_WINDOW);
  const window = body.slice(start, end);

  return SELF_IDENTIFIERS.some(id => window.includes(id));
}

前後それぞれ 50 文字のウィンドウ内に自己識別子が含まれる場合のみ fail にする。Yakumo は AI 開発の会社です のような直接的な自己言及は、識別子と禁則語が 20 文字以内に収まるため確実に検出できる。

proximity window の適切なサイズ設計

ウィンドウサイズの選択はトレードオフだ。

ウィンドウ特性
20 文字未満false negative が増える(八雲会社 が少し離れた文で引っかからない)
50 文字実用的な中間点。1 センテンス以内の言及を概ねカバーできる
100 文字超false positive が増える(前の文の 会社 が次の文の 八雲 に引きずられる)

scripts/blog-gate.ts の G10 実装では前後 50 文字のウィンドウを採用している。Yakumo は AI エージェントを事業の中心に据えた会社です のような 1 文が概ね 50 文字以内に収まることを根拠にした。


allow-list を SSOT に持つ設計

brand.ts に externalOrgAllowlist を定義する

allow-list の置き場所として scripts/blog-gate.ts にハードコードする方法は避ける。gate スクリプト内に exception を埋め込むと、新しい他組織名が記事に登場するたびに gate を修正する必要が生じる。

正しい設計は src/config/brand.ts に SSOT として定義することだ。

// src/config/brand.ts
externalOrgAllowlist: [
  '株式会社マーケットエンタープライズ',
  'Market Enterprise',
] as const,

型定義はシンプルなリテラル文字列の配列で十分だ。as const を付けることで TypeScript が型を正確に推論し、誤記を早期に検出できる。

現時点の externalOrgAllowlist には 2 件の組織名が登録されている。

gate スクリプトからの参照方法

gate スクリプトは brand.ts を import して allow-list を参照する。

// scripts/blog-gate.ts
import { brand } from '../src/config/brand';

function isInAllowList(text: string): boolean {
  return brand.externalOrgAllowlist.some(org => text.includes(org));
}

allow-list 内のフレーズを含む文字列は、内部に「会社」「企業」が含まれていても pass とする。株式会社マーケットエンタープライズで〜 という文は allow-list に完全一致する部分文字列を含むため、proximity 解析の前に除外される。


外部言及パターンを正規表現で構造化する

他社カテゴリ対比・他社批判・過去経歴の 3 パターン

allow-list は既知の組織名に対応するが、「他の AI 開発会社との違い」のような汎用的な他社言及には対応できない。このケースには正規表現パターンを使う。

// src/config/brand.ts
externalReferencePatterns: [
  // 他社カテゴリ対比: 「他の〜会社」「Web 制作会社とは」
  /他の[^。]{0,30}?(会社|企業)/g,
  // 他社批判: 「自称〜会社」
  /自称[^。]{0,30}?(会社)/g,
  // カテゴリ + 会社: 「開発会社」「制作会社」の一般的な言及
  /(他|別の)[^。]{0,30}?(事業|開発)[^。]{0,30}?会社/g,
  // 過去経歴: 「創業した会社」「共同創業した会社」
  /(共同|過去に)?創業(した|していた)?会社/g,
  // Web 制作会社(他社カテゴリ言及として頻出)
  /(Web|web)\s*制作会社/g,
  // English patterns
  /other\s+(AI\s+)?(development\s+)?(firms|companies)/gi,
  /self-described\s+["「]?AI\s+(companies|firms)["」]?/gi,
] as const,

externalReferencePatterns の定義と管理

各パターンは「外部の組織カテゴリを指す文脈」を正規表現で捉えている。[^。]{0,30} は「句点までの最大 30 文字以内」を意味し、文をまたいで誤検出するのを防ぐ。

現時点の externalReferencePatterns には 7 件のパターンが定義されている。

gate スクリプトはこれらのパターンに対して match された部分文字列を allow として扱う:

function isExternalReferenceContext(text: string, matchStart: number, matchEnd: number): boolean {
  const matchedText = text.slice(matchStart, matchEnd);
  return brand.externalReferencePatterns.some(pattern => {
    // pattern.exec が matchedText を含む部分文字列を対象にする
    const searchWindow = text.slice(
      Math.max(0, matchStart - 60),
      Math.min(text.length, matchEnd + 60)
    );
    return pattern.test(searchWindow);
  });
}

G10 実装前の flat grep で出た false positive は 4 件だ。blog-ops/retros/2026-05-18-context-aware-company-gate.md の根本原因セクションに列挙されている:「株式会社マーケットエンタープライズ」「共同創業した会社」「自称 AI 開発会社」「Web 制作会社とは違う」。

G10 の実装日は 2026-05-18、commit d1e3ae7feat(brand/gate): G10 catches Yakumo self-reference as 会社 / firm、06:12:34 +0900)だ。


テストケースで検証する 4 象限

self-reference NG / external-known OK / external-pattern OK / unknown WARN の 4 象限

context-aware gate の検証は「何が通り、何が通らないか」を明確にすることから始まる。4 象限に整理してテストケースを設計した。

象限入力文期待結果根拠
self-reference NGYakumo は AI 駆動の会社ですfailproximity 内に「Yakumo」あり
external-known OK株式会社マーケットエンタープライズで AI 推進責任者を務めたpassexternalOrgAllowlist に一致
external-pattern OK他の AI 開発会社との違いは業務設計にあるpassexternalReferencePatterns に一致
unknown WARNその会社は優秀だwarningproximity なし、allow-list なし、パターンなし。Yakumo 言及ではないが文脈が不明

実際の fixture ファイルには次のような実例を混在させる:

<!-- fail が期待される例 -->
Yakumo は AI で動く会社として認知される。

<!-- pass が期待される例: 他社の正式名を含む -->
株式会社マーケットエンタープライズで過去に勤務していた経験がある。

<!-- pass が期待される例: 他社カテゴリ言及 -->
他の AI 開発会社は API を叩くだけのアプローチが多い。

<!-- warning が期待される例: 文脈不明 -->
その会社の判断を尊重している。

G10 のテスト fixture ファイルは未作成だ。blog-gate.test.ts は存在せず、corporate-site の *.test.ts は e2e ディレクトリのみだ。


まとめ — 読者が持ち帰る原則

context-aware な brand rule 検出は 3 層で構成する。

  1. allow-list(完全一致 → 除外): 既知の他組織名を brand.ts に SSOT として列挙する。gate スクリプトは import して参照するだけ。
  2. externalReferencePatterns(正規表現 → 除外): 他社カテゴリ言及・他社批判・過去経歴のパターンを正規表現で構造化する。文をまたいで誤検出しないよう [^。]{0,30} でウィンドウを制限する。
  3. proximity 解析(残存マッチ → 判定): allow-list と pattern で除外された後に残ったマッチだけに proximity ウィンドウを適用する。前後 50 文字以内に自己識別子が存在すれば fail、なければ warning とする。

3 層を順に適用することで、完全な NLP 解析なしに実用的な精度を達成できる。brand rule の SSOT(brand.ts)と gate の実装(blog-gate.ts)を分離した設計が、allow-list の追加を gate 側の修正なしで完結させる。

allow-list 設計の詳細は Allow-list 駆動の brand check — 他組織名 SSOT 化による exception 設計 を参照してほしい。gate 全体の false positive / false negative トレードオフは Pre-publish gate 設計 — false positive と false negative のバランス で扱う。

SHARE X でシェア B! はてブ