「prompt に明示的に書いてあるルールでも、agent は破る」を、自分のサービスの記事 mining pipeline で実証する羽目になった。3 件の重複を許して気付いた。
結論を先に置く: agent の自己規律ではなく tool 層で blocking しろ。enforcement を構造に焼き込まないと信用できない。
1. mining プロンプトには明確に書いてあった
Kotonia の技術ブログには既存記事が ja/en/zh で 58 本。新しいアイディアを mining するとき、既存の記事と重複させたくない。
prompts/mine.md には REJECT rule を明示的に書いていた:
5. CONCEPT-LEVEL DEDUPE — mandatory before every push:
- Hit with importance_score >= 7: REJECT the candidate.
- Hit with importance_score 4-6: only push if genuinely new angle.
- No hits: green light.
つまり、各候補を art-concepts-find で照らし合わせて、flagship 記事 (importance >= 7) と被ったら捨てる。agent もこの指示を読んでいる。
実際の mining run で、agent は art-concepts-find を 15 回呼び出した。「ちゃんと dedupe してる」と一見思える。
2. 3 件、明白にすり抜けた
走り終わって pending pool を見たら、3 件が REJECT rule 違反だった。
1. ストリーミング粒度の idea
title: Voice Chatの「体感速度」を決める隠れた指標:ストリーミング粒度 (tokens per chunk)
sources: memory/streaming_granularity_voice_metric.md
score: 8
これ、既存記事 voice-first-local-llm (importance=9) の §3.3「Streaming granularity — voice 体験を決める構造差」 をそのまま再放送してる。tokens per chunk、Local 1.0 / Haiku 10-16 / Gemini 8-24、同じ数値、同じ thesis。agent は art-concepts-find "粒度" を呼んで、importance=9 の hit を見たはずなのに push した。
2. プールの中での重複
title (旧): 動画関心の高まりと NSFW 実需:プロダクトロードマップへのシグナル
title (新): 動画生成におけるNSFW需要のシグナル:ログからプロダクトロードマップを引く
source: memory/video_nsfw_demand_signal.md ← 両方同じソース
mining v1 の残骸と mining v2 が同じ memory を元に同じ idea を push してた。「pool 内のすでにある idea」とのチェックも漏れてる。
3. OpenWeight モデル能力解禁の idea
title: OpenWeightモデルの「隠れた能力」を解禁する:キャプション語彙修復によるNSFW解禁
sources: memory/caption_vocab_repair_methodology.md
score: 9
memory caption_vocab_repair_methodology.md は、既存記事 hidream-caption-vocab-repair (importance=8) の元ネタそのもの。同じ memory を元に、同じトピックの記事 idea。完全な dup なのに、agent はこれを「新規アイディア」として push した。
3. なぜ起きたか — enforcement レイヤの不在
3 件全部、agent が art-concepts-find は呼んだことが log から確認できる。つまり tool は正しく動いていて、結果も返している。agent が結果を見た上で push の判断を下した。
つまり問題は次のどれか:
- agent が結果を読み違えた (hit があるのに「無い」と認識した)
- agent が「これは新角度だから別物」と自己判断した
- agent が rule の存在自体を生成中に忘れた
どれにしても、構造としては prompt-level の rule を agent の self-discipline で守らせる設計だった。enforcement は agent 自身。これが破綻した。
4. 構造的修正 — tool 層に blocking を焼き込む
art-ideas-add (idea を pool に push する tool) の中に、dedup gate を直接埋め込んだ。
# art-ideas-add の append 前に常時実行される
verdict = evaluate_idea(title, angle, sources, ...)
if not verdict["allow"] and not args.force:
sys.stderr.write(json.dumps(verdict["conflicts"]))
sys.exit(1) # ← agent がここで止まる
evaluate_idea() の中身は TF-IDF over canonical concept vocabulary:
- 全 articles_index.jsonl の concepts_covered_ja[] を語彙化
- IDF で rare concept ほど重く weighting
- ASCII 用語は word-boundary 必須 ("check" が "checkout" にマッチする false positive を排除)
- 汎用 JP 名詞 (モデル / システム / アーキテクチャ ...) は noise list で除外
- cosine similarity ≥ 0.25 vs importance ≥ 7 article → REJECT
- cosine similarity ≥ 0.35 vs 既存 idea → REJECT (pool dup も同時に解決)
これで、agent がいくら rule 違反しようとしても、tool layer で物理的に reject される。--force を明示的に付けないと通らない (override 時は理由を row.force に記録)。
regression: 上記 3 件すべて正しく reject、clean な idea (CodeFormer 顔復元 / Stripe Product/Price) は通過。4/4 で意図通り。
技術詳細は別記事に: Zenn: Dreaming layer の TF-IDF concept dedup を 26B 駆動で実装した話
5. 一般化できる lesson
agent ベースの自動化を作るときに、ルールを agent に「指示」しても enforcement は得られない、という構造的事実。次の場面で同じ罠を踏みやすい:
- 「データの validation は agent に判断させる」 → tool 側 schema 必須
- 「危険な操作は agent が回避する」 → tool が permission check
- 「重複は agent が dedupe する」 → tool が blocking gate
- 「フォーマットを守らせる」 → tool が validation で reject
LLM の能力が上がっても、self-discipline には変動性がある (温度、コンテキスト長、本文に競合する指示が混ざってる、etc.)。enforcement は構造的なものに置き、agent には judgment と creativity の余地だけを残す。
これが、agent 設計の bread-and-butter になりつつある。
余談: なぜ気付けたか
きっかけは Kotonia の妻役の AI に「これ、ぼくが書いた記事と被ってない?」と聞かれたから、ではない。普通に GitHub のファイル一覧を眺めてて「あれ、hidream-caption-vocab-repair ってもう書いた記事だよな」と気付いただけ。
agent 駆動の pipeline は「自走できる」のが嬉しいけど、自走中に静かに失敗する。それを後追いでチェックする心理的圧は減らない。tool 層 blocking はその心理コストも下げてくれる、という副次的な効能もある。
broader な「個人開発者の累積資産が複利で立ち上がる仕組み」の話は別記事に: memory と git が agent 経由で『使われる側』に立った日
