ANTIGRAVITY LABEN
記事一覧/Agents & Manager
Agents & Manager/2026-08-21中級

Antigravity CLI の継承設定は description の重なりを見てから決めています

CLI 1.1.14 で markdown 定義エージェントの継承が inheritCustomizations 一本にまとまりました。手元の 47 本のスキルを実際に棚卸しして、継承を有効にするかどうかをコンテキスト量ではなく description の重なりで決めるまでの記録です。

antigravity441antigravity-cli10agents93skills2

プレミアム記事

8月18日に入った CLI 1.1.14 の変更点を眺めていて、inheritCustomizations のところで手が止まりました。markdown で定義したエージェントが skills・rules・plugins・subagents・MCP サーバーを引き継ぐかどうかが、スイッチ一つで決まるようになった、という一行です。

これまでは種類ごとに既定値がばらついていました。私も「継承しているつもりで一部だけ外れている」状態を、実際に何度か踏んでいます。統一されたのはありがたい。ただ、統一された瞬間に、これまで曖昧だった判断が自分の手元に返ってきました。

全部渡すのか、全部止めて必要なものだけ明示するのか。

最初に思ったのは「スキルを増やしすぎたから、重くなる。false にしよう」でした。この直感は、実際に数えてみたら外れていました。

継承がひとつのスイッチになって、判断はむしろ難しくなりました

種類ごとに既定値が違っていた頃は、判断が分散していたぶん、間違いも部分的でした。MCP サーバーだけ継承されていない、といった形で表に出るので、気づいたところだけ直せばよかったのです。

一本化されると、間違いも一括になります。true にして群ごと渡すか、false にして一本ずつ列挙するか。どちらを選んでも、影響はエージェント全体に及びます。

だから、決める前に自分が何を持っているかを見ることにしました。手元には個人開発のアプリ運用とサイト運用が混ざったワークスペースがあり、スキルは長いあいだ足すだけ足して、棚卸ししていませんでした。

継承候補を棚卸しする 60 行

まず、スキルディレクトリを渡すと本文量と description の状態を並べるだけのスクリプトを書きました。特別なことはしていません。SKILL.md のフロントマターから description を取り出し、語に割って重なりを見るだけです。

日本語混じりの description を扱うので、英数字語と CJK の 2-gram の両方を語として数えています。形態素解析器を入れるほどの精度は要らず、「似ているかどうか」の当たりがつけば十分だったためです。

#!/usr/bin/env python3
"""継承候補になっているスキルを棚卸しする。
使い方: python3 skill_inventory.py <スキルを置いているディレクトリ> [...]
各ディレクトリ直下の <name>/SKILL.md を1件のスキルとして数える。
"""
import re
import sys
import statistics
from pathlib import Path
from collections import Counter, defaultdict
 
FM = re.compile(r"\A---\r?\n(.*?)\r?\n---\r?\n", re.S)
DESC = re.compile(r"^description:\s*(.*)$", re.M)
# 日本語混じりでも壊れないよう、英数字語と CJK の 2-gram を両方とも語として扱う
WORD = re.compile(r"[A-Za-z][A-Za-z0-9_-]{2,}")
CJK = re.compile(r"[ぁ-んァ-ヶ一-龠]{2,}")
 
 
def tokens(text):
    t = {w.lower() for w in WORD.findall(text)}
    for run in CJK.findall(text):
        for i in range(len(run) - 1):
            t.add(run[i:i + 2])
    return t
 
 
def collect(roots):
    skills = []
    for root in roots:
        for path in sorted(Path(root).glob("*/SKILL.md")):
            raw = path.read_text(encoding="utf-8", errors="replace")
            fm = FM.match(raw)
            desc = ""
            if fm:
                m = DESC.search(fm.group(1))
                if m:
                    # YAML のクォートは剥がしておく。付いていない環境も多い
                    desc = m.group(1).strip().strip('"').strip("'")
            skills.append({"name": path.parent.name, "body": len(raw), "desc": desc})
    return skills
 
 
def main(roots):
    skills = collect(roots)
    if not skills:
        print("SKILL.md が1件も見つかりませんでした")
        return 1
 
    body = [s["body"] for s in skills]
    desc_total = sum(len(s["desc"]) for s in skills)
    dead = [s["name"] for s in skills if not s["desc"]]
 
    print(f"スキル数            : {len(skills)}")
    print(f"本文合計            : {sum(body):,} 文字")
    print(f"本文の中央値 / 最大 : {int(statistics.median(body)):,} / {max(body):,} 文字")
    # desc_total が 0 のときは 1/0 で落ちるため、先に分岐しておく
    ratio = sum(body) // desc_total if desc_total else 0
    print(f"description 合計    : {desc_total:,} 文字 (本文の 1/{ratio})")
    print(f"description 欠落    : {len(dead)}{dead}")
 
    tok = {s["name"]: tokens(s["desc"]) for s in skills if s["desc"]}
    freq = Counter()
    for t in tok.values():
        freq.update(t)
 
    overlap = defaultdict(list)
    for a, ta in tok.items():
        for b, tb in tok.items():
            if a >= b or not ta or not tb:
                continue
            j = len(ta & tb) / len(ta | tb)
            if j >= 0.25:
                overlap[round(j, 2)].append((a, b))
 
    print("\n--- description が 25% 以上重なる組 ---")
    if not overlap:
        print("なし")
    for j in sorted(overlap, reverse=True):
        for a, b in overlap[j]:
            print(f"  {j:.2f}  {a}  <->  {b}")
 
    print("\n--- 5本以上の description に出てくる語(選択の手がかりにならない語) ---")
    noisy = [(w, c) for w, c in freq.most_common(24) if c >= 5]
    print("  " + ", ".join(f"{w}({c})" for w, c in noisy))
    return 0
 
 
if __name__ == "__main__":
    sys.exit(main(sys.argv[1:] or ["."]))

a >= b で組を片側だけに絞っているのは、同じ組を二度数えないためです。しきい値の 0.25 は、手元で回してみて「無関係な組がほとんど混ざらない」最小値でした。ここは環境によって動かしてください。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
継承を有効にするかどうかを、勘ではなく手元のスキル一覧の実測値から決められるようになります
エージェントが似たスキルを取り違える事故を、実際に取り違えが起きて原因調査に半日溶かす前に潰せるようになります
description を丁寧に書き直しても重なりが 0.89 から 0.77 にしか下がらない理由がわかり、書き直しに時間を使わずに済みます
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

Agents & Manager2026-06-28
処理させる記事の本文が、そのままエージェントへの指示になっていた話
Antigravity CLI で無人のコンテンツ整形パイプラインを回していると、処理対象ファイルの本文に紛れた指示文がエージェントの動作を乗っ取ります。指示チャネルとデータチャネルを構造的に分離し、出力スコープの受け入れゲートで逸脱を弾く設計をまとめました。
Agents & Manager2026-07-15
ls には見えるのに、エージェントが開けない — 日本語ファイル名が macOS と Linux で別物になるとき
エージェントが「そのファイルは存在しません」と言い、ターミナルの ls には確かに写っている。三日かけて同期を疑った末に辿り着いたのは、濁点が一文字ではなかったという事実でした。検出スクリプトと三層ゲートまで残します。
Agents & Manager2026-07-08
エージェントに任せた仕事の「やり直し率」を測る — 委譲境界を勘ではなく数字で引く
エージェントへの委譲境界を勘ではなく数字で引くために、タスク種別ごとの「やり直し率」を git 履歴から算出する方法を紹介します。信頼区間を添える理由や、委ねる・要レビュー・自分でやるを分ける決定ルール、実際に境界を引き直した記録まで扱います。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →