AIワークフロー設計をはじめよう！

# はじめに
この記事では、AIワークフローの設計を説明します。

**いいね・ストックをよろしくお願いします！**

巷では、AIで一発でこんなアプリができた！ みたいなインパクトのある事例が騒がれがちですが、一発で生成できることと、実際の仕事で役立つことの間には大きな差があります。

この記事では、そんな泥臭いAIを含んだシステムの設計で考慮するべき点を解説し、**本当に役立つシステム**を作るためのヒントを書きたいと思っています。


# AIワークフロー設計
## AIワークフローとは

:::note
**本記事におけるAIワークフロー**
- 業務を開発者が事前に複数ステップに分解し、事前に決定しておいた手順に従って処理を行う
- 業務はグラフ構造として展開され、AIはノードの実行や、どのエッジに移動するかの判断を行う
:::

開発者目線からすると、単に処理の単位が関数呼び出しからAI呼び出しに変わったようなもので、変わらず要件定義や設計が必要です。
ユーザー目線からすると、AIが内蔵されたシステムが自動的に知的な作業を代替してくれたように見えます。

現時点でのLLMはテキストの入出力以外では現実的な性能を持っていないため、次のようなユースケースを想定します。

- プログラミング支援
    - コード生成
    - 設定ファイル生成
    - テスト生成
- 業務上の文書生成
    - 一般的な意味での文章の生成
    - 業務上の決定内容を表すなんらかのデータ構造を入出力する

## AIワークフロー設計とは

:::note
**AIワークフロー設計**
システムのユーザー体験の設計、AIを組み込んだ新しい業務の設計をすること。
:::

具体的な設計内容は次のようになります。

- AIに解かせる問題の決定
    - どんな情報を読ませる？
    - 何を出力させ、出力結果をどうやって処理する？
- プロンプトエンジニアリング
    - どのようなプロンプトが効果的か？
    - どこまで事前にプロンプトのテンプレートを作る？
    - 個別のケースの人間の指示はどこまで対応する？

従来のシステムでも、システム設計をしないと正しくユーザー価値を実現できなかった。AIが新しく登場したところで設計の重要性は変わりません。
AIはこれまでのシステム設計のコンポーネントとは異なる新しい性質を持っていので、システムに組み込む場合、新しい設計方針が必要なのです。

:::note
**設計の重要性**
- 設計次第でユーザー体験が大きく変わってくる。例えば、GPT単体とそれを活かしたAIエディタでは大きく異なる
- 良い設計だと、安いモデルでも十分精度が高まり、コストパフォーマンスが改善する
:::


# 極限までAIを回避する

AIを使ったシステムを作成するにあたって、最も重要な点を最初に提言します。

:::note
**AIシステムの設計原則**
AIは銀の弾丸ではない
AIをなるべく排除し、通常のシステム化では不可能・困難・高価になるタスクに絞ってAIを導入するべきである
:::

LLMは動作に通常のアルゴリズムでは考えらないほどの金額・時間がかかり、不安定で予測ができない結果を引き起こします。対して、通常のシステム化は動作が決定的で高速で安価です。

AIに丸投げしてもいい結果はでてきません。これは人間に仕事を丸投げするのと同じです。システム設計者側が業務内容を整理し、なるべく機械的に処理して、判断のガイドラインを示すことにより、AIの精度を高められます。

:::note alert
**「AIで一発解決」の罠**
現実の業務は、基本的にAIで一発解決できない。泥臭い設計をして、AIのタスクの難易度を下げ、エラーを回避し、UXを改善することが重要である。
:::

この論点については、すごくいい記事が書かれています。

https://zenn.dev/pharmax/articles/d1d3695e4114c0

この章では、具体的なAIを回避する手法を説明します。しかし、このような手法は基本的に「要件定義」と呼ばれる作業と大差ありません。**従来のスキルが無駄になることはないです**。AIワークフローを設計するのに重要な部分に絞って説明します。

## 情報構造の整理
AIワークフローでは、次のような情報を整理することが非常に重要です。

- 誰に聞けば必要な情報が得られるか
- どの情報がどこに保存されているか
- どのタイミングでユーザー・管理者から確認を取るべきか

AIはプロンプトを介して作業します。逆に言えば、AIはプロンプトからしか情報を得られません。したがって、情報が散らばっていると、プロンプトを作成するときに色々な場所から情報を集めて回る必要があります。これは、ワークフローのアルゴリズムを複雑にします。

:::note
**情報構造の整理の重要性**
- 必要な情報を必要な場所に一元管理することで、プロンプトの精度を高められる
- 情報が散らばっていると、AIが知らないコンテキストが増え、タスクに失敗する
:::

情報不足を解決する手法として、RAGが提案されています。これは、必要な情報をその場で検索エンジンを使って収集する手法です。この手法を使えば、情報構造がある程度散らかっていても、検索によって補うことができます。しかし、**散らばった情報を検索するより、元から整理された情報を取得するほうが精度が高いです**。

:::note
**AIにいい感じに情報抽出をさせる前に、人間が厳密に情報を設計しよう！**
:::

## 手順の整理
AIに最終結果だけを聞いても精度が低いです。人間の業務のマニュアルでも「これをしたらこれをする」というある程度のガイドラインが提供されているはずです。

人間がAIのやっていることを理解しやすくなります。さらに、プロンプトを詳細に作成することができ、安定した作業をさせることができます。

:::note
**AIの業務内容をステップごとに整理し、それぞれのステップでのタスク内容と生成物を整理しよう！**
:::

## 機械的に処理
AIは大量の文章に弱いです。入力が大きすぎれば途中で書かれていた指示の一部を忘れることがありますし、出力が大きければ出力内で不整合が発生することがあります。

AIワークフローでは、AIに生成させるのは欲しいデータ全部ではなく、欲しいデータを生成するために必要な本質的な情報だけにしましょう。

例えば、ソースコード全てを生成させるのではなく、ソースコードジェネレータの設定ファイルだけを生成させることが考えられます。

AIへの入力についても、全ての情報が入った雑多な非構造テキストではなく、機械的に分かりやすくフォーマットしたMarkdownで指示しましょう。

:::note
**AIには本質的に重要な少量の文章を生成させ、残りは機械的に計算しよう！**
:::

# ユーザー体験重視

AIを用いたシステムでは、本質的ではないAI固有の面倒さが発生してしまいがちです。例えば、次のような作業が発生します。

- わざわざプロンプトエンジニアリングをしないといけない
- わざわざ出力を確認して必要な部分を抽出・コピペしないといけない

AIシステムでは、このような問題を排除したスムーズなユーザー体験を得られるような工夫が必要です。

:::note
**AIの利用体験**
できるだけユーザーにAI固有の煩雑さを感じさせないように設計するべきである。
ユーザーがAIの存在に気づかないのが理想的
:::

## 自然なコメントをプロンプト扱いする

業務上の様々な項目には「コメント」「補足」「特記事項」を含めることがあります。これは、例外的な内容や理解が難しい内容を補足する効果を持った文章です。このようなコメントは、AIにも必ず与えるようにしましょう。

ソースコードであれば、関数の先頭に記述するコメントはドキュメンテーションコメントとして扱われています。AIにコード生成を実行させる際に、ドキュメンテーションコメントを自動的に読みこませることで、**AIへの指示と人間への補足を共有できます。**

:::note
**プロンプトをプロンプトとして意識させないようにしよう！**
:::

## 設定ファイルとしてプロンプトを扱う

前節で解説したコメントは、単一の項目に対する一回限りの補足説明でした。そのほかにも、複数の項目に対する補足説明を記述することがあります。例えば、次のような場合です。

- 基本的な業務の流れは同じだが、顧客ごとに領収証の記述フォーマットが異なる
- 基本的なコード生成の内容は同じだが、関数の命名規則などはプロジェクトごとに異なる

このような**ある程度共有されているが、必要に応じて変更する必要がある**業務の特性については、設定ファイルを利用して対応することができます。

:::note
**プロンプトの再利用性ごとの取得元**
- 業務の基本的な流れ・確実に必要なデータ：システム側で事前に用意しておく
- 事例ごとにある程度変わる部分：設定ファイル
- 例外的な部分：コメント
:::

例えば、AIエディタであるCursorは、`cursorrules`というAIに対するルールを説明する設定ファイルを含んでいます。これにより、いちいちルールを記述することなく、適切に再利用ができています。

## 普段使うツールにさりげなく組み込む

AI専用の画面を新しく作ることは、AIを使っていることを強烈に意識させてしまいます。できるだけAIはAPIとして呼び出し、基本的な業務体験を変更しないようにしましょう。**特に、AIツールと非AIツールでコピペが発生するようなシステムは避けるべきです。**

- ツールならプラグインとして作成する
- WebサービスならBotやブラウザ拡張として作成する
- CLIならタスクランナーから起動できるようにする

:::note
**AIの存在を明示的に示さず、チャットのUIを見せず、背後でさりげなく気の利いた動作をさせるべき**
:::

# 不安定性を克服する
AIの最大の欠点は、不安定性・不完全性です。通常の計算と違い、計算結果を無批判に受け入れてはいけません。その結果、次のような制約が生まれます。

- 人間が処理内容を理解し、AIよりも「上」に立たなければならない
- 通常のシステムエラーに加えて、AIの失敗を考慮した処理をしなければならない

この問題は、AIワークフロー上の最大の課題であり、システム設計者の腕の見せ所です。基本的には、次の3つのレイヤーにわたって、多層的に対処します。

:::note
**AIの不安定性への対処**
1. そもそもエラーを発生させない
1. エラーを自動的に検出・修正する
1. 人間がAIの失敗を修正しやすくする
:::

## エラーを発生させない
### 過不足のない入力
AIの精度を高めるには矛盾のない明確なプロンプトを用意することが重要です。
**AIワークフローの最大の利点は、事前のプロンプトエンジニアリングが効果的に行えることです。**

ワークフローとして業務を分割しているので、個別の業務ごとにプロンプトを事前に作成しておくことができます。このようなプロンプトは基本的に再利用されるので、プロンプトエンジニアリングの費用対効果が高いです。

プロンプトエンジニアリングの基礎的なテクニックについては詳しく説明しませんが、明確かつ簡潔な指示をすればよいです。

:::note
**適切なAIへの入力の性質**
- 指示に矛盾が含まれないこと
- AIが推測しないといけない情報が少ないこと（プロンプト内に必要な情報がなるべく全て存在すること）
- 入出力の形式が適切に構造化されていること
- タスクを理解するための効果的な具体例が含まれていること
:::

### プロンプトを揮発させない
なるべくコメント・設定ファイル・システム側の事前定義プロンプトだけでプロンプトを構成させ、その場その場でユーザーが作成するプロンプトを使うのを止めてください。

一回限りの揮発性のプロンプトを利用すると、リトライが難しくなります。
これは、失敗時のやり直しが難しくなり、不安定性につながります。

さらに、AIは進歩が速い領域です。今失敗するタスクでも、より高性能なモデルであれば正解できる可能性があります。リトライが容易であれば、**指示が失敗した場合に自動的に高性能モデルに切り替えて実行する**ことができるので、必要最低限のコストで必要に応じて高い知性を利用できます。

:::note
**ユーザーのボタン一つで常に再生成ができるようにしよう！**
:::

### 安定した出力指定
出力を安定させる最大の方法は、JSONを利用することです。

:::note
**形式は明確に指定したJSONで出力させよう**

- 各JSONのプロパティを説明する
- プロパティのデータ型を含める
- 具体的な出力例を含める
:::

モデルがまだJSON出力に対応していない場合は、結果の生成を行った後、JSONに対応したモデルを使って整形処理を行うことで対応することができます。
### 中間生成物を作成する
これは、**そもそもエラーを発生させない**ための手法です。

AIの性能向上手法として、Chain-of-Thoughtと呼ばれるものがあります。推論を繰り返し発展させることでより賢く振舞うというものです。

AIワークフローでは、事前に仕事の流れをある程度定義できるため、Chain-of-Thoughtを活用しやすいです。

例えば、QA対応をする際に、直接ユーザーへの返答を生成せず、最も近い用例・解答パターンを答えさせ、次にその用例に従って自然な解答を生成させる、というように業務を分析して刻むことでより品質を向上させられます。この例における中間生成物は、「最も近い用例」になります。

:::note
**タスクの分割と中間生成物の作成**
タスクを一発でAIにやらせてはならない。タスクを細かく分割し、ステップごとに明確にプロンプトを作成し、ステップ間の中間生成物を作成させるべきである。
:::

品質向上以外にも、中間生成物を作成することは、ワークフローを途中から人間が引き継ぐ場合にも効果があります。
単に「できませんでした」より「ここまでやって、これがうまくいかない」と報告された方が役立つためです。

## エラーを検出する
### 静的解析
静的解析とは、コード解析の技術において、コードを実行せずに問題を検出する技術です。AIの生成結果を静的解析することで、問題を検知できます。人間のミスを検出するために発展してきた手法ですが、AIの登場によってさらに重要度が増しています。

- Linterを実行する
- Formatterを実行する
- 静的型付き言語を利用する

:::note
**なるべく厳密に静的解析を行おう！**
:::

### テスト（動的解析）
テストの実行も大切な品質保証の手段です。
人間が事前にテストを記述しておく方法以外にも、AIにテスト内容を生成させ、それを満たすかを検証させることも可能です。
コード生成以外でも、チェックリストを作成し、それを満たすかをAIを使って一つずつ判定することで、疑似的にAIを実行環境として扱い、テストを行うことができます。

:::note
**生成結果を無批判に採用せず、必ずテストを行おう**
:::

## エラーを自動修正する
エラーを検出できれば、AIによって修正を行うことができます。
Linterの警告、テストの実行ログなどを収集し、その情報を含めて修正を依頼することで、高い精度で問題を認識し、エラーを自動的に修正することができます。

:::note
**エラーを検知したら、まずはAIにエラーログとともに自動的に修正をさせよう**
:::

## 人間がAIの失敗を修正しやすくする
### 「創造性が不要になる最後の点」をレビューさせる
ワークフローでは、様々なデータが変換・処理されます。そのうち、システムが機械的に制御する部分は、アルゴリズムによって厳密に処理されるため、レビューは不要です。
AIが処理する部分のレビューが必要になります。とはいえ、AI制御と言っても、簡単なフォーマットの変換程度のタスクならほぼほぼ間違えないように進化していくはずです。

- 人間のレビューの手間を最小化する
- 人間のレビューの効果を最大化する

これらを達成するため、「創造性が不要になる最後の点」という概念を考えます。

:::note
**コスパの良い人間のレビュー**
- データ変換のフローを創造性の観点から捉え、創造性が必要な難しい場所を特定する
- その箇所の直後だけをレビューさせる
:::

この方針は、創造性が必要な前段階をレビューしても、それ以降の段階で壊れてしまう可能性があることに対処しています。本質的に難しい場所だけをレビューさせることで、効果的に人的リソースを活用できます。

### 問題を説明させる

AIの生成物を人間が全て理解するのは面倒です。そのため、必要に応じて適宜、どうしてそのような生成をしたのかをAIに質問できるようにするべきです。つまり、生成されたものに対して人間がいちいちチャットにコピペせずともコンテキストに含めて説明できるような仕組みを整備してください。

:::note
**生成物の出力**
生成物に対して人間が質問できるようにする。特にコード生成では、通常のプロジェクトのソースファイルとして出力し、AIエディタを使って質問できるようにする。
:::

### 中間生成物を作成する

これは、精度を高めるためテクニックでも説明しました。中間生成物を用意して、中間生成物の単位でレビューを複数回行うことで、早期のエラー検出が可能になります。

例えば、SQLクエリだけを別に生成させ、そのクエリを使ったコード生成は別のレイヤーで行うことが考えられます。
クエリ由来の問題と、コード由来の問題を分離して、クエリの問題を先に発見することができます。

:::note
**中間生成物のレビュー**
問題をステップごとに分解し、論理的な区切りごとに中間生成物を作成し、レビューさせよう
:::

# まとめ
この記事では、AIワークフロー設計の重要なポイントを網羅的に解説しました。実際のツール設計では、AI自体よりも、このような泥臭い設計、ユーザー体験への執念が重要になってきます。