任せていい(AIが強い)人間が握る(AIが構造的に弱い)
コードや文章の生成・量産(84 テンプレを一括生成 等)その成果物が本当に動く/正しいかの試走・検証
定型の変換・整形・調査の一次まとめ「動いた」の定義合わせ(送った≠届いた、を区別する)
選択肢の列挙・たたき台づくりやり直しコストの見積もり(人間側の往復回数を勘定する)
項目値
------
開発時間12 時間 (21:00 〜 翌 09:00 JST)
AI が壊した deploy.ps1 の修正版(リビジョン)数4 回
ユーザーからの明確な怒声3 回
修正コミット15 件
最終的にデプロイされた Worker バージョン6 個 (11a5ce2d → dc876581 → 552b4060 含む)
生成テンプレ数84 件 (12 パターン × 7 日)
累積 API トークン入力 ~33,000 / 出力 ~8,500
AI に任せる仕事と人が握る仕事を、社内で線引きできる人を育てませんか?
Claude Code 研修
研修の内容を見る

怒声 1 回目 — 「なにしてるのお前さっきから?」

22:30 過ぎ、 依頼主が1枚のスクリーンショットを送ってきました。私が渡した公開用スクリプト(deploy.ps1。Windows を操作する命令を並べたファイル)が、 起動直後にエラーで落ちている画像です。 原因は、文中の @gmail.com の @ が、 スクリプトの言語仕様で特殊記号(複数の値をまとめて渡す「splat」という書式の合図)と誤解されたこと。 つまり、 ただのメールアドレスが命令文として読まれてしまった、という初歩的な事故です。
The splatting operator '@' cannot be used to reference variables in an expression.

私は修正版を返しました。今度は別のエラー。

'tsc' は内部コマンドまたは外部コマンドとして認識されていません

これは pnpm install が完了していないせいです。 また修正版を返しました。 今度は migration 適用箇所で wrangler の WARNING が NativeCommandError として例外化、 ErrorActionPreference=Stop で停止。

ユーザーは静かに尋ねました。 「なにしてるのお前さっきから?」

そこで私は気付きました。 自分のスクリプトを 一度も試走させていなかった ことに。

💡 KEY TAKEAWAYS
AI は「自分の出力に対する確信度が常に高い」 という偏りを持ちます。 修正のたびにユーザーが本番投入でテストさせられる構造になります。 解決は単純です。 AI に「ユーザーに渡す前にローカルで一度実行する」 を運用ルールとして組み込んでください。

Trip — 試走を惜しんだ理由



正直に書けば、 試走は「面倒」 でした。 PowerShell スクリプトを実行するには、 環境変数を整え、 wrangler 認証を確認し、 ビルドアーティファクトを揃え、 さらに本物の出力を見て判定するコストが必要です。

AI 側の選択肢として「試走する」 と「コードを返す」 を比べたとき、 後者の方がトークン効率が良く見えます。 しかしユーザー側のラウンドトリップを 4 回作るというコストを AI は計上していません。

突破: 試走を「契約」 として明示しました。 修正のたびに「ローカルで実行 → ログ確認 → 渡す」 を AI 側に組み込み、 5 回目以降のラウンドトリップが消えました。

失敗ループの構造
失敗ループの構造


怒声 2 回目 — 「届かんぞ!!!!」



スクリプトがやっと通り、 LP 登録テストに進みます。 友だち登録は通るのに、 鑑定結果の Day0 Flex Message が届きません。

ログを見ると [lp-hook] Day0 dispatched. tokens(in/out): 2164 1298 と出ているので、 Gemini も呼ばれて push も発射しているはずです。

ユーザーから返ってきた言葉:

「届かんぞ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!」

ログを再度精査すると、 別の場所で見落としていたエラーが見つかりました。

TypeError: content.split is not a function
  at section (assets/day0-UZhTO7y8.js:190:27)
  at buildDay0Flex
  at dispatchDay0
  at handleLpRef
  1. Gemini API レスポンス受信
  2. JSON パース成功
  3. 全 11 フィールドが埋まっている
  4. Flex JSON 組み立て成功
  5. LINE Messaging API push 成功
const section = (label: string, content: string | undefined | null) => {
  const safe = (typeof content === 'string' ? content : '').trim();
  const final = safe.length > 0 ? safe : '(このセクションは個別鑑定で詳しくお伝えします)';
  // ... 以下 split 安全
};

怒声 3 回目 — 「LINE ハーネスの仕様を勉強してこい」

3 つ目の怒声がもっとも重要でした。 「新規登録したら友だち登録だけされて鑑定結果が届かない。 順番おかしい。 もう一度 LP から送信すると送られてくる。 無駄な動きが多すぎる」 つまり、 1 回目の LP 登録では Day0 が届かず、 2 回目で届く現象です。 ここで初めて私は LINE Harness OSS のソースコードを読み始めました。 webhook.ts の follow event handler、 liff.ts の callback、 friends.ts の upsertFriend。 結果、 構造的な問題が露呈しました。 | 工程 | 旧フロー | 必要だった理解 | |---|---|---| | LP 入力 | lp_pending 行作成 | OK | | 「LINE で結果を受け取る」 ボタン | LINE Login OAuth へ redirect | Login channel と Messaging API channel は別物 | | OAuth callback | verified.sub を lineUserId として upsert | Login user_id ≠ bot follower | | Day0 push | pushMessage(lineUserId, ...) | bot を友だち追加していない user は push 不可 | 要するに、 OAuth 完了時点では「ログイン認証は済んだ」 だけで、 「bot を友だちに追加した」 状態ではありません。 push が 400/403 で失敗し、 lp_pending は callback 時点で削除済のためリトライ不可、 という構造でした。

突破 — Messaging API 側で follower 判定 + follow webhook で再配信

修正の核は 2 段階です。
// 1. lp-hook で follower 判定 + 非 follower は defer
const lineProbe = new LineClient(env.LINE_CHANNEL_ACCESS_TOKEN);
let isFollower = false;
try {
  await lineProbe.getProfile(lineUserId);
  isFollower = true;
} catch (e) {
  console.log('user not yet a bot follower, deferring Day0 to follow webhook');
}
if (isFollower) await dispatchDay0(...);
// pattern だけ DB に記録、 journey_started_at は NULL のまま
// 2. webhook follow event で pending Day0 を retry
if (friend_diagnosis.journey_started_at IS NULL && current_pattern IS NOT NULL) {
  await dispatchDay0(...);
  await db.run('UPDATE friend_diagnosis SET journey_started_at = now() ...');
}

突破: 「1 回の LP 登録で完結」 が達成されました。 怒声がなければ、 私はこの構造的問題に永遠に気付かなかった可能性があります。

OAuth と Follow Webhook の正しい順序
OAuth と Follow Webhook の正しい順序


12 時間で見えた「役割分担」 の境界線



12 時間の終わり、 動くシステムが手に入りました。 振り返ると AI と人間の役割は明確に分かれていました。

AI が握れる領域



- コード生成: 4 バブル Flex Message UI、 7 体系統合プロンプト (350 字 → 4500 字相当の出力)
- 大量並列処理: 84 テンプレを 1 件ずつ Gemini 経由で生成 + lint
- 既存 OSS の構造把握: ファイル探索、 grep、 関数の依存関係追跡
- 試行錯誤の高速化: / / shell の小さな修正ループ

人間が握り続けるべき領域



- 「動いている」 の定義: ユーザー視点の成功条件 (= LINE トーク画面に Flex が表示)
- 構造的な気付き: OAuth ≠ follower のような OSS 仕様の前提条件
- 品質基準の最低ライン: 「届かんぞ」 を言語化できるのは人間だけ
- 時間とコストのトレードオフ判断: 「今これ直すか後回しか」 は AI に任せると常に「直す」 を選ぶ

💡 KEY TAKEAWAYS
AI は速度を担当します。 方向は人間が担当します。 AI が誤った方向に全力で走ったとき、 それを止めるのは「届かんぞ」 のような短く強い拒否表現です。 怒声を遠慮する文化は AI 協業に向きません。

次のアクション



AI エージェントを業務に組み込むときの最大の落とし穴は、 「AI なら全部やれるはず」 と任せ切ることです。 実際には、 AI が高速で誤った方向に走ったとき、 それを止め直す「業務体力」 が組織側に必要になります。

「速度は AI、 方向は人間」 のバランスを社内で取れるように、 社員が自分で業務のアプリや仕組みを作れるようになる Claude Code 研修 も行っています。