はじめに
Claude Codeのプラグインという仕組みを使って、営業プロセス(のうちリード獲得)を自動化しました。
作り始めた動機は実用目的だったのですが、作っていく中で面白い気づきがありました。従来のソフトウェア開発とは、設計思想が根本的に逆転しているということです。
この記事では、プラグインの設計と実装を通じて感じた「LLMが処理の主体になる世界」について書きます。
作ったもの
LeadAce という営業自動化プラグインです。以下の一連のプロセスをAIが自律的に実行します。
| コマンド | 処理内容 |
|---|---|
/setup <project> |
SQLite DB初期化 + プロジェクト作成 |
/strategy <project> |
AIと対話して営業戦略を策定 |
/build-list <project> |
Webを巡回して営業先リストを自動生成 |
/outbound <project> |
1社1社にパーソナライズしたメールを自動送信 |
/check-results <project> |
返信・反応を確認してDBに記録 |
/evaluate <project> |
反応率を分析して戦略を自動改善 |
/daily-cycle <project> |
上記をまとめて1日分の営業サイクルを自動実行 |
ソースコードはGitHubで公開しています。
https://github.com/aitit-inc/claude-plugins
概念の逆転:CPUからLLMへ
従来のソフトウェア
従来のシステム開発では、決定的なロジックがシステムの基盤です。
[コード] ──処理の流れを制御──> [コード] ──> [コード]
↓
[AI/MLモデル] ← 部分的にパーツとして使う
メインの処理はプログラムが行い、AIは画像認識や自然言語処理など、特定のタスクに「パーツ」として組み込まれます。
Claude Codeプラグイン
Claude Codeプラグインでは、これが完全に逆転します。
[LLM] ──判断・実行──> [LLM] ──判断・実行──> [LLM]
↓ ↓ ↓
[スクリプト] [スクリプト] [スクリプト]
← 道具として使う ← 道具として使う ← 道具として使う
処理を進めるのはLLMです。スクリプトは、LLMが使う「道具」として存在します。
たとえば「営業先リストを作って」という指示に対して、LLMが「まずWebを検索して候補を集めよう → 重複チェックのスクリプトを呼ぼう → DBに登録するスクリプトを呼ぼう」と判断しながら処理を進めます。
ソフトウェアの処理環境がCPUからLLMに移った、という感覚がありました(だいぶ語弊はありますが)。
プラグイン開発における「システム設計」とは、コードの設計ではなく「AIにどう指示するか」の設計です。主要な設計ドキュメントはコードではなく、各スキルの SKILL.md(マークダウン)です。
スキル自体はすでにたくさん便利なものがありますが、今回真面目にプラグインを作ってみて思ったのは、DBやスクリプトとうまく組み合わせれば、結構大きな「システム」を作れるのでは、という感覚でした。
全体アーキテクチャ
この構成でgithubにおいてパブリックにすれば、Claude Codeのマーケットプレイスに簡単に追加できます。
workspace-root/
├── data.db # SQLiteデータベース(全プロジェクト共有)
├── project-a/ # プロジェクトごとのサブディレクトリ
│ ├── BUSINESS.md # 事業情報
│ ├── SALES_STRATEGY.md # 営業戦略
│ └── DAILY_CYCLE_REPORT.md # 日次レポート
└── project-b/
└── ...
プラグイン自体のファイル構成:
lead-ace/
├── .claude-plugin/
│ └── plugin.json # プラグインマニフェスト
├── scripts/ # Pythonスクリプト群(LLMの「道具」)
│ ├── init_db.py # DB初期化
│ ├── sales_queries.py # 定型クエリ
│ ├── add_prospects.py # 営業先登録(重複チェック付き)
│ ├── send_and_log.py # メール送信 + DB記録(アトミック)
│ ├── license.py # ライセンス管理
│ └── ...
├── skills/ # 各コマンドの指示書(LLMへの指示)
│ ├── setup/SKILL.md
│ ├── strategy/SKILL.md
│ ├── build-list/SKILL.md
│ ├── outbound/SKILL.md
│ ├── check-results/SKILL.md
│ ├── evaluate/SKILL.md
│ └── daily-cycle/SKILL.md
└── references/ # スキル横断の共通ルール
└── workspace-conventions.md
ポイントは、skills/ 配下のマークダウンがシステムの本体だということです。Pythonスクリプトはあくまで「LLMでは効率的に処理できない部分」を補完する道具です。
技術的なポイント
1. なぜSQLiteか
営業プロセスでは数千〜数万件のデータを扱います。営業先、送信履歴、返信、評価メトリクス…。
最初はマークダウンやCSVで管理することも考えましたが、到底無理でした。
- 重複チェック(同じ会社に二度送らない)
- ステータス管理(new → contacted → responded → converted)
- 集計クエリ(反応率の算出、チャネル別分析)
- プロジェクト横断の共有(
do_not_contactフラグなど)
これらを安全に行うにはRDBが必要です。SQLiteなら依存関係がほぼなく、ファイル1つで完結します。
DBスキーマは6テーブル構成です:
-- 営業先マスタ(全プロジェクト共有)
CREATE TABLE prospects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
company_name TEXT NOT NULL,
website_url TEXT NOT NULL,
email TEXT,
contact_form_url TEXT,
sns_accounts TEXT, -- JSON: {"twitter": "...", "linkedin": "..."}
do_not_contact INTEGER DEFAULT 0, -- グローバルなブロックリスト
...
);
-- プロジェクトごとの営業先メタデータ
CREATE TABLE project_prospects (
project_id TEXT,
prospect_id INTEGER,
status TEXT DEFAULT 'new', -- new/contacted/responded/converted/rejected
priority INTEGER DEFAULT 3, -- 1=最高, 5=最低
match_reason TEXT, -- なぜこの営業先をターゲットにしたか
UNIQUE(project_id, prospect_id)
);
-- 送信ログ(監査証跡)
CREATE TABLE outreach_logs (...);
-- 返信記録
CREATE TABLE responses (...);
-- 評価履歴
CREATE TABLE evaluations (...);
prospects テーブルをグローバルにして、project_prospects でプロジェクトごとのメタデータを分離する設計にしました。同じ営業先に別プロジェクトから別のアプローチをかけることもできますし、do_not_contact は全プロジェクトで効きます。
2. なぜPythonか
スクリプトの言語選定では、ShellスクリプトとPythonで迷いました。
Shellスクリプトのほうが環境依存が少なく、Claude Codeのbashツールとの相性も良さそうです。ただ、実際にSQLiteの操作やJSONの処理を書き始めると、Shellでは辛い場面が多くありました。
# こういう処理がPythonだと楽に書ける
def check_duplicate(db_path, company_name, email, website_url):
"""メール、法人番号、ドメイン、社名で多段階の重複チェック"""
conn = sqlite3.connect(db_path)
# ... 複数条件での照合ロジック
結果、Pythonを選びましたが、標準ライブラリだけで完結することを徹底しました。pip install が必要なパッケージは一切使っていません。sqlite3、json、hashlib、os、sys などだけで全スクリプトを書いています。
(ただし、SQLite自体は brew install sqlite3 が必要な場合があります。macOS標準で入っていることが多いですが…)
3. daily-cycleとコンテキスト管理
/daily-cycle は、check-results → evaluate → outbound + build-list を順番に実行する統合コマンドです。
100件、200件と指定してアウトバウンドを実行すると、かなりの長時間処理になります。ここで問題になるのがコンテキストウィンドウの圧迫です。
Claude Codeでは、opus(1Mトークン)を使えばコンテキスト自体は溢れないかもしれません。しかし、コンテキストが膨らむと指示追従性が下がっていくという問題があります。200件目の処理が1件目と同じクオリティで実行される保証がありません。
そこで、daily-cycle内の各ステップはすべてサブエージェント(Agent tool)で実行する設計にしました。
なので、daily-cycleは他のスキル全てを束ねるオーケストレーター的な立ち位置です。
# daily-cycle/SKILL.md の一部(イメージ)
## 5. check-results(サブエージェント)
Agent toolで新しいサブエージェントを起動し、check-resultsの処理を実行させる。
結果は $0/.tmp/check-results-summary.md に書き出させ、
メインコンテキストには3行の要約だけを受け取る。
## 8. outbound(バッチ実行)
10件ずつのバッチに分割し、各バッチをサブエージェントで実行する。
こうすることで、200件処理した後でもメインのコンテキスト使用量は10〜20%程度に抑えられます。サブエージェントのコンテキストはメインに影響しないので、各バッチは常にクリーンな状態で処理が始まります。
ちなみに、sonnet(200kトークン)だとこの方式でも厳しいかもしれません。opusの1Mコンテキストだからこそ成立する設計です。
4. オレオレライセンス認証
このプラグインは無料版(1プロジェクトまで)と有料版(無制限)を設けています。
が、Claude Codeプラグインのマーケットプレイスには、そんな課金の仕組みは用意されていません。ソースコードはGitHubでパブリックに公開されています。
さて、どうするか。
結論から言うと、SHA-512ハッシュによるオレオレ認証です。
# license.py
VALID_KEY_HASH = "44192c18c9d9c4d4195f77a204036fc..." # ← ソースコードにベタ書き
def validate_key(key: str) -> bool:
h = hashlib.sha512(key.strip().encode()).hexdigest()
return h == VALID_KEY_HASH
仕組みとしては:
- ライセンスキー(平文)は購入者にメールで送付
- プラグイン内にはSHA-512ハッシュだけを保持
-
/setup時にキーを入力 → ハッシュ比較 →~/.leadace/pkeyに保存 - 各スキル実行時にライセンスチェック
もちろん、これは全然堅牢ではありません。
- サーバー通信はないので、オフラインで完結します
- ハッシュ値はソースコードに書いてあります
- キーは1つしかありません(全購入者共通)
- ソースコードを読んで
validate_keyをバイパスすれば突破できます
コードの難読化をするか、認証サーバーを立ててプラグイン内から通信させるか…とも考えましたが、正直そこまでやるモチベーションがありませんでした。
ライセンスで法的に縛った上で、あとは性善説。「みんな、コード読まないでね」という仕組みです。
この辺、Claude Codeプラグインのエコシステムとして、マーケットプレイスに課金の仕組みが整備されるといいなと思っています。そのうちそうなるでしょうけど。
おわりに
Claude Codeプラグインを作ってみて、一番印象的だったのは 「コードを書く量が驚くほど少ない」 ことです。
このプラグインのPythonスクリプトは全部合わせても数百行です。処理の大部分はSKILL.md(マークダウン)に書かれた「AIへの指示」が担っています。
従来なら、Webスクレイピング、メール作成、送信制御、反応分析、戦略更新…これらすべてにコードが必要でした。それが今は「AIにやらせる」の一言で済む部分が大半です。
残るのは、AIでは効率的に処理できない部分だけ。SQLiteへの確実なデータ操作、アトミックなメール送信+ログ記録、ライセンス管理。こういう「決定的であるべき処理」だけをスクリプトとして切り出す。
これは、ソフトウェアの作り方そのものが変わり始めている兆しだと感じています。
LeadAce — Autonomous Lead Generation Plugin for Claude Code
https://github.com/aitit-inc/claude-plugins