0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

未経験エンジニアがAI業務自動化を作って痛感した「AIより先に設計すること」

0
Posted at

AI業務自動化ツールのイメージ

はじめに

こんばんは!
最近、プライベートでMacbook proを購入し、遊んでる新人です!

AIエージェントやAI駆動開発の記事がかなり増えています。

自分はまだ未経験エンジニア寄りで、最初からきれいな設計ができたわけではありません。

むしろ最初は「AIに投げればなんとかなるのでは?」くらいの感覚でした。

でも業務向けのAI自動化ツールをいくつか作ってみると、AIそのものよりも、その前後の設計の方がずっと大事だと分かってきました。

作っていたのは、たとえばこういうものです。

  • 案件収集・評価・Discord通知ツール

    • Gmail、案件ポータル、紹介サイトから案件情報を取得
    • Geminiで案件を評価
    • 通知済み案件を重複排除してDiscordへ通知
  • 外部業務サービス / Gmail同期パイプライン

    • 外部サービスやGmailから案件・人材情報を取得
    • 共通フォーマットへ正規化してDB保存
    • マッチ候補を確認してDiscord通知
  • 動画チャプター編集ツール

    • 動画をアップロード
    • シーン検出、OCR、音声文字起こし
    • Geminiで画面内容を解析して章立て補助
  • Cloudflare Workerベースの定期実行ツール

    • Cronで外部データを取得
    • D1 / R2 / KV に状態を保存
    • AI評価後に必要なものだけ通知

どれも見た目は違いますが、作って失敗して直しているうちに、共通点が見えてきました。

この記事では、未経験寄りの自分がAI業務自動化ツールを作りながら、「ここを先に考えておけば後で苦しまなかった」と感じたことをまとめます。

1. AIに渡す前の「入力整形」がほぼ本体

AIを入れると、つい最初にプロンプトを考えたくなります。

自分も最初はプロンプトをこね回していました。

でも実際に効いたのは、プロンプト以前に、AIへ渡す情報をどこまで整えるかでした。

案件評価ツールでは、入力元が複数あります。

Source 入口 重複判定
Gmail通知 Gmail message ID / 案件ID message ID / matching ID
案件サイト 一覧API / 詳細API matching ID
紹介ポータル referral project ID project ID
案件媒体 matching ID matching ID

それぞれHTMLだったり、メール本文だったり、GraphQLのレスポンスだったりします。

このままAIに投げると、毎回入力の形が違いすぎて評価が安定しません。

そこで、まず人間が読みたい形に寄せました。

type ProjectForEvaluation = {
  source: "gmail" | "project_portal" | "referral_site" | "media";
  sourceId: string;
  title: string;
  companyName?: string;
  budget?: string;
  schedule?: string;
  description: string;
  requirements: string[];
  rawUrl?: string;
};

AIに「この情報を判断して」と言う前に、まず全ソースをこの形へ正規化します。

これをやると、プロンプトが短くなります。

以下の案件情報を S/A/B/C で評価してください。
評価理由、懸念点、次に人間が確認すべきことも出してください。

AIに頑張らせるのではなく、AIが迷わない入力にする。

初心者ほどここを飛ばしがちですが、ここが一番効きました。

2. 「全件通知」ではなく「人間が見るべきものだけ通知」する

業務自動化で最初にやりがちなのが、全部DiscordやSlackに流すことです。

自分も最初は「通知されれば便利」と思っていました。

でも全部流すと、すぐ誰も見なくなります。

案件収集ツールでは、最初に複数の入力元から案件を集めます。

  • Gmail通知
  • 案件ポータル
  • 紹介サイト
  • 外部サービスの一覧ページ
  • 手動で保存したJSON

ここで取れたものを全部通知すると、通知チャンネルがただのログ置き場になります。

なので、通知対象を絞りました。

function shouldNotifyProject(evaluation: ProjectEvaluation) {
  if (evaluation.rank === "S") return true;
  if (evaluation.rank === "A" && evaluation.needsHumanReview) return true;
  if (evaluation.hasUnknownBudget && evaluation.deadlineIsNear) return true;
  return false;
}

通知の目的は「処理結果を全部流すこと」ではなく、「人間の次の一手を作ること」です。

これを決めてから、画面もAPIもかなり作りやすくなりました。

  • DBには全件保存する
  • Discordには人間が反応すべきものだけ流す
  • Run logsでは取得件数、スキップ件数、失敗理由を見る
  • Raw payloadは後から再解析できるように残す

AIが処理する範囲と、人間が判断する範囲を分けるのが大事でした。

ここを分けないと、未経験の自分でも一見すごいものは作れるのですが、運用するとすぐ通知疲れします。

3. 重複排除はAIより先にやる

AIを使った自動化で地味に大事なのが、重複排除です。

これは作る前はあまり意識していませんでした。

同じ案件が複数経路から入ってくることがあります。

  • Gmail通知で来る
  • 案件サイトの一覧にも出る
  • 詳細ページのURLが違う
  • メールIDは違うが案件IDは同じ
  • 本文は少し違うが実体は同じ

これをAI評価の後に処理すると、無駄にトークンを使います。

なので、AIに渡す前にできるだけ落とします。

type SourceState = {
  source: string;
  sourceId: string;
  canonicalUrl?: string;
  contentHash?: string;
  firstSeenAt: string;
  lastSeenAt: string;
  notifiedAt?: string;
};

重複判定に使ったものは、だいたいこのあたりです。

  • Gmail message ID
  • 外部サービス側の案件ID
  • URLの正規化結果
  • 詳細本文のhash
  • 既通知のstate

AIは便利ですが、同じものを何度も読ませると普通にお金と時間が溶けます。

「AIで賢く判断する」前に、「そもそも判断しなくていいものを渡さない」方が効きました。

これはAIというより、普通のシステム設計の話でした。作ってから気づきました。

4. Webhookや外部APIは成功前提で作らない

AI連携や外部API連携では、失敗を前提にした設計がかなり重要になります。

最初は成功パターンだけ見て作っていました。

実際に作っていると、こういうことが起きます。

  • 外部APIが一時的に落ちる
  • HTML構造が変わる
  • Discord通知だけ失敗する
  • AI評価だけタイムアウトする
  • 同じデータが二重に取得される
  • 後から同じpayloadを再処理したくなる

なので、処理結果だけでなく、取得元のraw payloadや実行ログを残すようにしました。

type RunLog = {
  id: string;
  source: string;
  status: "running" | "succeeded" | "partial" | "failed";
  fetchedCount: number;
  skippedDuplicateCount: number;
  evaluatedCount: number;
  notifiedCount: number;
  errorMessage?: string;
  startedAt: string;
  finishedAt?: string;
};

重要なのは、処理を分けることです。

  • 取得
  • 正規化
  • 重複排除
  • AI評価
  • 保存
  • 通知

このうち通知だけ失敗したなら、取得やAI評価まで失敗扱いにしない。

逆にAI評価が失敗しても、raw payloadが残っていれば後から再実行できます。

地味ですが、ここを作ると運用時の安心感がかなり変わります。

未経験のうちはエラー画面を見るだけで焦りますが、ログがあると「どこまでは成功して、どこで失敗したか」を追えるようになります。

5. AIの設定はコードに埋め込まない

AIの評価基準や出力フォーマットは、運用していると必ず変えたくなります。

たとえば案件評価なら、

  • 何を高評価にするか
  • 予算を重視するか
  • 納期を重視するか
  • 既存事業との相性を重視するか
  • 通知するrankの閾値を変えるか
  • 出力に「次に確認すること」を含めるか

最初はコードにベタ書きでも動きます。

自分も最初はコードに直接書いていました。

でも、少し運用するとすぐ調整したくなります。

なので、AIの評価設定はコードから分離した方が楽でした。

type EvaluationConfig = {
  model: string;
  rankThreshold: "S" | "A" | "B" | "C";
  scoringPolicy: string;
  mustCheckFields: string[];
  notificationPolicy: string;
};

プロンプトも、役割ごとに分けておくと扱いやすいです。

PromptTemplate
- system
- extraction
- evaluation
- notification_summary

AIの振る舞いは、コードではなく運用設定として変えられる方がいいです。

特に業務自動化は、人間の反応を見ながら改善するものなので、コードデプロイなしで調整できる境界を作った方が楽でした。

ここも、作る前は分かっていませんでした。実際に使ってみて「これ毎回デプロイするの面倒だな」となってから分かりました。

6. Cloudflareに寄せると「運用しない設計」ができる

案件収集や通知のような処理は、ずっとローカルPCやVPSで動かすより、Cloudflare Workersに寄せた方が運用が軽くなりました。

定期実行パイプラインのイメージ

最初はローカルで動けば満足していたのですが、自分のPCが閉じていると動かない時点で業務自動化としては弱いです。

たとえば案件評価ツールでは、Cloudflare Workerを30分おきに実行します。

  • Gmail APIからメール確認
  • 案件サイトから未処理案件取得
  • KVで処理済みstateを確認
  • Geminiで評価
  • Discord通知

Macが閉じていても動きます。

また、別の同期パイプラインではCloudflare Native構成も検討しました。

VPSにもメリットはあります。

  • 既存のDocker/FastAPI/Postgresを移しやすい
  • Playwrightなどのブラウザ自動化がやりやすい
  • 長時間処理やデバッグの自由度が高い

でも、小さな定期実行パイプラインなら、Cloudflare Nativeの方が運用が軽いです。

  • OS更新を見なくていい
  • Docker daemonを見なくていい
  • ディスク容量を気にしなくていい
  • Cron、DB、Object Storage、ログ、デプロイを同じ平台に寄せられる

自分の場合、「最終的に運用したい形」から逆算すると、Cloudflareに寄せる判断はかなり自然でした。

未経験だとインフラを大きく構えがちですが、小さい定期実行なら「運用しなくていい場所」に置く方が続きます。

7. 動画やドキュメント系でも考え方は同じ

動画チャプター編集ツールでは、扱うデータが案件ではなく動画です。

でも、構造はかなり似ています。

  • 動画ファイルを受け取る
  • シーンを検出する
  • フレームを抽出する
  • OCRする
  • 音声を文字起こしする
  • Geminiで画面内容を解析する
  • チャプター案として保存する
  • 人間が最後に編集する

ここでも、AIに動画全体を丸投げするより、先に素材を分解した方が安定しました。

動画
  ↓
シーン
  ↓
代表フレーム
  ↓
OCR / 音声文字起こし
  ↓
AIによる章タイトル候補
  ↓
人間が編集

AIは最後の判断補助に使う。

その前に、AIが見やすい単位へ分解する。

案件評価でも動画解析でも、結局ここは同じでした。

AIに丸投げするほど、出力が当たり外れになります。未経験の自分には、その当たり外れを直す方が大変でした。

8. AIに任せるほど、ガードレールが必要になる

AIを使うと作業は速くなります。

でも、速くなるほど怖い操作も増えます。

特に未経験のうちは、AIが出したコマンドや修正が本当に安全か判断しきれないことがあります。

特にこのあたりは、AIに雑に触らせたくありません。

  • .env
  • APIキー
  • OAuth token
  • Webhook secret
  • DB dump
  • 顧客データ
  • 本番DB
  • 本番通知
  • main branchへの直接push

なので、プロジェクト側に「AIに見せるもの」と「見せないもの」を分ける必要があります。

自分の運用では、少なくとも次を意識しています。

.env
.env.*
secrets/
backups/
*.dump
*.pem
*.key
data/*token*

さらに、AIには最初に対象範囲を絞って依頼します。

悪い例:

このプロジェクトをいい感じに直して

良い例:

案件評価ツールの重複排除ロジックだけ見てください。
目的は、同じ案件を複数経路から取得しても二重通知しないことです。
本番DB操作、環境変数の表示、外部通知の実送信はしないでください。
変更前に対象ファイルを列挙してください。

AIに全部読ませるより、必要な範囲を指定した方が、精度も安全性も上がりました。

AIが強いほど、こちら側の依頼の雑さも増幅される感じがあります。

作って分かった共通パターン

いくつか作ってみて、AI業務自動化ツールには共通パターンがあると感じました。

最初から分かっていたわけではなく、何個か作ってようやく見えてきた感じです。

AI業務自動化ツールの共通パターン

外部入力
  ↓
正規化
  ↓
重複排除
  ↓
AI評価 / AI分析 / AI要約
  ↓
保存
  ↓
人間が見るべきものだけ通知
  ↓
ログと再処理

AI部分だけ見ると派手ですが、実際に大事なのはその周辺です。

  • 入力を整える
  • stateを持つ
  • 重複を防ぐ
  • 失敗しても追える
  • 人間が判断する出口を作る
  • 設定を運用で変えられる

ここを作ると、AIはかなり実務に入れやすくなります。

まとめ

AI業務自動化で一番大事なのは、強いモデルを選ぶことだけではありませんでした。

むしろ、未経験寄りの自分が作っていて効いたのは次です。

  • AIに渡す前に入力を正規化する
  • 全件通知ではなく、人間が見るべきものだけ通知する
  • AIに渡す前に重複排除する
  • 外部APIは失敗・重複・再処理を前提にする
  • PromptやAI設定はコードから分離する
  • 定期実行はCloudflare Workersなど運用が軽い場所へ寄せる
  • AIに任せる範囲と触らせない範囲を決める

AIを業務に入れるというより、AIが迷わず働ける業務レーンを作る。

まだきれいに作れているとは言えませんが、自分の中では、これが一番しっくりきています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?