Upgrade to Pro — share decks privately, control downloads, hide ads and more …

フルスタックTS製SaaSを AIエージェント化する

Avatar for Yuma Ito Yuma Ito
June 10, 2026
23

フルスタックTS製SaaSを AIエージェント化する

すでに動いているフルスタックTypeScript製SaaS「プロワン」に、AIがブラウザのUIを操作する仕組みを後付けして、AIエージェント化した話です。新しいAIプロダクトを作ったのではなく、既存のプロダクトに後から組み込みました。

AIはツールの引数に何でも送ってくるため、入口でZodによる検証を行い、複雑な操作は専用ツールに型を定義し、型で表現しきれない汎用ツールは元々ある既存の検証に委ねました。弾かれてもAIはエラーを読んで自分で修正します。本番3ヶ月のデータと合わせて、「既存プロダクトをAIエージェント化するなら、普段から検証を整えておくことが効く」という話をします。

Avatar for Yuma Ito

Yuma Ito

June 10, 2026

Transcript

  1. 仕組み AIがReactアプリを操作する仕組みを作った AIが返すのは tool_call、「この関数を呼べ」という⽂字列だけ。 { name: "addRows", args: { quantity:

    1, unitPrice: 7207 } } これを、Reactアプリの操作に変える。 そのために作ったのが、use-ai というライブラリ Reactのコンポーネントを、AIが操作できるようにする 2 ©プロワン
  2. 発想の転換 各画⾯のコンポーネントで、AIに状態とツールを渡す // マウントで「今の画面の状態」と「使えるツール」を登録 useEffect(() => { register(id, { state:

    { 見積もり: estimate }, // AIが見る、画面の情報 tools: { addRows, editRows }, // AIが使えるツール }) return () => unregister(id) // アンマウントで解除 }, [deps]) 3 ©プロワン Reactの描画ツリーが、AIに渡す状態とツールを決める。 ダイアログを閉じれば、その状態もツールも、AIから⾒えなくなる。
  3. 仕組み ツールがUIを動かす ツールはブラウザで実行され、結果と更新後の状態がAIに返る。AI側は結果が返るまで待つ。 4 ©プロワン ブラウザ (React) サーバー LLM API

    状態とツール定義を登録 ツール定義(JSON Schema)+ 状態 tool_call + 引数(JSON) 実行を依頼 UIを操作して実行 結果 + 更新後の状態 Type JSON
  4. 型検証 ツールの⼊⼒をZodで検証する const addRows = defineTool( '明細に行を追加する ', z.object({ quantity:

    z.number(), unitPrice: z.number() }), (input) => { /*inputの型は { quantity: number; unitPrice: number } */ }, ) Type‧JSON Schema‧AIが返すJSON。この3つが⼀致する保証はない。 Zodスキーマ1枚から全部導出すれば、ズレない。 z.toJSONSchema(s) AIに渡る「⼊⼒の取り決め」になる z.infer<typeof s> handler の引数の型になる s.parse(args) 実⾏する前に、値を検証する 6 ©プロワン
  5. 専用ツール ツールの⼊⼒を型で定義する // 明細行スキーマ(addRows の入力・抜粋) z.object({ label: z.string().nullable().optional().describe('品名(例: "エアコン")'), quantity:

    z.number().nullable().optional().describe('数量(例: 10, 1, 0)'), unitName: z.string().nullable().optional().describe('単位(例: "個", "台")'), unitPrice: z.number().nullable().optional().describe('単価(例: 75000)'), unitCost: z.number().nullable().optional().describe('原価(例: 50000)'), taxCode: z.string().nullable().optional().describe('税区分(例: "001")'), isTextLine: z.boolean().nullable().optional().describe('見出し行なら true'), }) 型で値を縛り、.describe() の例がAIへの指⽰になる。1枚のZodが、型とAIへの説明を兼ねる。 型を定義した所では、AIはこの型どおりに送ってくる。本番3ヶ⽉、数量‧単価が型からズレたことは0件。 7 ©プロワン
  6. 自己修正 弾かれたAIは⾃分で直す 例えば、AIがAPIの boolean の欄に、⽂字列の "true" を送ってしまう。 サーバーの検証が「boolean で送ってください」と差し戻す。 AIはそのメッセージを読んで、true

    に直して送り直す。 本番では、こうして弾かれた型ミスの8割超 を、AIが⾃分で直して完遂した。 ⼈間向けに検証を作り込んでおけば、AIエージェント化でも効く。 10 ©プロワン