
AIにどこまで任せる?複雑なForecast計算をスプレッドシート関数1セルに固定化した話
事業部の利益目標管理で必要になるForecast計算を、AIに毎回計算させるのではなく、AIに設計させたスプレッドシート関数に固定化した事例を紹介します。「AIを使うべきかどうか」ではなく「AIにどこまで任せるか」を考えるきっかけになれば幸いです。
やりたいこと:年度末の着地見込みを日々把握する
今期から、事業部の利益目標の管理を担当することになりました。見たいものは、月ごとの収支の実態と、年度末にどこへ着地するのかの見込み(Forecast)です。目標未達の兆候があれば、売上拡大やコスト削減の意思決定を早い段階で打てるようにすることが目的です。
数字の元になるデータは、経理部門から財務会計の数字として共有されています。ただ、これは会計ルールと締め処理に基づく確定実績なので、計上タイミングが事業管理として見たい月と一致しない場合があり、将来の着地見込みも別途補う必要があります。どちらが間違っているという話ではなく、見方の違いです。そこで、意思決定のための管理会計として、管理用の利益目標データを独自に作ることにしました。
意思決定に使う数字だからこそ、精度と再現性が求められます。数字がズレれば、判断もズレるからです。
収集から計算まで、すべてAIに任せる運用でよいか
最初に考えたのは、必要なデータをすべてAIに渡してしまう運用です。会計データ・管理台帳・CSV / Excelなど、いろんな場所に散らばった数字を収集・統合するところは、AIを使うととても便利です。
一方で、その後の計算までAIに任せると、確認作業が毎回残ります。
- 複雑な条件分岐では、条件の読み違いや適用漏れが起こり得る
- 指示の解釈によって、計算手順や出力形式が変わる可能性がある
- そのため、元データ・計算条件・合計値を毎回確認する必要がある
さらに、定型処理のたびにトークンを消費するのももったいない点です。5時間ごとの利用上限に当たった経験のある方も多いのではないでしょうか。AIによる収集・整理は有効ですが、確定した計算まで毎回AIに任せる必要はあるのか。ここが今回の出発点でした。
Skill化しても「計算の固定」にはならない
同じことの繰り返しならSkill化すればよいのでは、という考え方もあります。Skill化は有効ですが、手順の一貫性と計算結果の決定論性は分けて考える必要があります。
| Skill(指示の固定) | 関数・コード(計算の固定) | |
|---|---|---|
| 効果 | 手順が安定しやすくなる | 結果を安定させられる |
| できること | 毎回同じルールを参照、指示漏れ・表現揺れの削減、出力形式の統一 | 同じ入力に同じ計算を適用、分岐条件の固定、テストケースでの検証 |
| 限界 | AIがルールを解釈して計算する限り、結果の完全一致までは保証されない | ルールが明確でないと固定化できない |
Structured Outputsを使えば出力形式は揃えやすくなりますが、数式の途中や値そのものの誤りまでは防ぎきれないとされています(出典:OpenAI「Introducing Structured Outputs in the API」)。
つまり、Skillは「手順」を固定し、関数やコードは「計算」を固定する。この2つは別のレイヤーの話です。
実行手段にスプレッドシート関数を選んだ理由
確定したルールを動かす仕組みには、BIツール、業務システム / SaaS、SQL / スクリプトなど、いろんな選択肢があります。今回は次の前提から、スプレッドシート関数を選びました。
- 元になる数字の多くが、もともとスプレッドシートで管理されていた(インプットもアウトプットもスプレッドシート)
- 条件分岐は多く複雑だが、数字の世界なのでルールは明確
- 同じ処理を定期的に繰り返す
- 非エンジニアが日々運用・修正する
- データ量・権限要件がスプレッドシートの範囲に収まる
逆に、大量データ・高頻度処理、厳格な権限管理や監査、多人数による同時更新が必要な業務であれば、別の仕組みを検討すべきです。簡単だから関数を使うのではなく、ルールが明確だから関数として固定できる、という順序です。
AIと関数の役割分担
AIの役割は、数字を毎回計算することではなく、データを整えて計算ルールを設計することです。実際には次のプロセスで進めました。
- 条件を洗い出す(人+AI): やりたいことをAIに伝え、入力・例外・判断基準を整理する
- ルール化する(人+AI): 標準処理と例外処理に分ける
- 関数を設計する(AI支援): BYROW、LET、IF、MAPで構造化した関数をAIに設計させる
- 検証する(人+関数): 実際のインプットでテストケースを確認する
- 運用する(関数): 日々のデータに同じルールを適用する
関数化してしまえば、あとは同じ入力に対して同じ結果が返ります。「AIの計算は本当に合っているのか」という確認はなくなり、数字そのものの確認と判断に注力できるようになりました。
Forecastの月次按分ロジック
今回関数に固定化したのは、Forecastの月次按分です。標準処理を中心に置き、例外を2つに分けてルール化しました。
| 区分 | 内容 | 処理 |
|---|---|---|
| 標準 | 対象期間で按分 | 確度や開始月をもとに、対象期間へ均等按分する |
| 例外A | 期ズレ・月ズレ | 会計側の計上タイミングのずれを、あくまで元の月で計上するように調整する |
| 例外B | 想定外の金額 | 当初見込んでいなかった金額が事業部に割り当てられた場合、発生月から年度末までで均等按分する |
たとえば、本来6月末に計上されるお金が会計側の都合で7月になっている、といったズレをそのまま見てしまうと、プロジェクトの実態と合いません。こうしたルールを本部内で決め、条件を分解してから関数に落とし込みました。複雑な業務ほど、AIに毎回判断させるのではなく、標準処理と例外処理に分解してから仕組み化するのがポイントです。
実装した関数(全文)
実際の関数はGoogleスプレッドシートでの実装例です。BYROWで行ごとに処理し、LETで変数を定義、IFで標準・例外を分岐、MAPで月ごとに計算しています。1セルに入れれば、1行分の元データから管理区分・確度・開始月を取得し、標準 / 例外A / 例外Bを判定して、月別Forecastを出力します。
=BYROW(FILTER({F8:F, H8:S, W8:W, U8:U}, A8:A<>""), LAMBDA(r, LET( prob, INDEX(r, 1, 1), start_m, INDEX(r, 1, 14), manage_type, INDEX(r, 1, 15), months, CHOOSECOLS(r, SEQUENCE(1, 12, 2)), IF(manage_type = "A", IF(SEQUENCE(1, 12), ""), IF(manage_type = "B", LET( num_indices, IFERROR(FILTER(SEQUENCE(1, 12), MAP(SEQUENCE(1, 12), LAMBDA(idx, ISNUMBER(INDEX(months, 1, idx))))), "NONE"), IF(INDEX(num_indices, 1, 1) = "NONE", IF(SEQUENCE(1, 12), ""), LET( start_idx, INDEX(num_indices, 1, 1), val, INDEX(months, 1, start_idx), duration, 12 - start_idx + 1, distributed_val, val / duration, MAP(SEQUENCE(1, 12), LAMBDA(m, IF(m >= start_idx, distributed_val, "") )) ) ) ), IF(prob >= 1, MAP(SEQUENCE(1, 12), LAMBDA(m, LET( future_indices, IFERROR(FILTER(SEQUENCE(1, 12), (SEQUENCE(1, 12) >= m) * MAP(SEQUENCE(1, 12), LAMBDA(idx, ISNUMBER(INDEX(months, 1, idx))))), "NONE"), IF(INDEX(future_indices, 1, 1) = "NONE", "", LET( m_target, MIN(future_indices), target_val, INDEX(months, 1, m_target), prev_indices, IFERROR(FILTER(SEQUENCE(1, 12), (SEQUENCE(1, 12) < m_target) * MAP(SEQUENCE(1, 12), LAMBDA(idx, ISNUMBER(INDEX(months, 1, idx))))), "NONE"), calc_start_m, IF(INDEX(prev_indices, 1, 1) = "NONE", start_m, MAX(prev_indices) + 1), duration, m_target - calc_start_m + 1, IF((m >= calc_start_m) * (m <= m_target), IF(duration > 0, target_val / duration, target_val), "" ) ) ) ) )), CHOOSECOLS(r, SEQUENCE(1, 12, 2)) ) ) ) )))長く見えますが、読み解いていけば「1月ずつデータを取り出して、条件で分岐させて計算していく」だけの構造です。自力でゼロから書こうとするとなかなか大変ですが、条件を整理して渡せばAIがここまで作ってくれます。
固定化しているのは次の5点です。
- どの列を参照するか
- どの条件で標準・例外を分けるか
- 対象期間をどう判定するか
- 按分額をどう計算するか
- どの月に出力するか
関数は一度作って終わりではなく、育てる
この関数も、最初からこの形だったわけではありません。最初は単純な按分だけのもっとシンプルなものでした。「こういうパターンもあるな」と新しい条件が見つかるたびにAIと壁打ちして、要件に応じて関数を更新し続けています。
これはWebサービス開発と同じプロセスです。要件があり、コード化して運用し、新しい要件が出てきたらAIと一緒にコードを更新していく。それをスプレッドシート関数でやっているだけ、と捉えています。
仕組み化して変わったこと
| 見ているもの | |
|---|---|
| Before | 条件適用・計算手順・出力形式を毎回確認する |
| After | データの抜け漏れ・例外・前提条件に確認を集中する |
| Goal | 月次の実態と年度末Forecastを見て、早めに手を打つ |
計算を毎回作り直すのではなく、判断に使える数字を安定して出す。これが仕組み化の効果です。
まとめ
- AIには、データの整理と計算ルールの設計を任せる
- 確定した計算ルールの反復実行は、関数・コード・SQLなど、同じ入力から同じ結果を返す仕組みに任せる
- Skillは「手順」を固定するもので、「計算」の固定は別レイヤーで考える
- 複雑な業務は、標準処理と例外処理に分解してから仕組み化する
AIに考えさせ、仕組みに繰り返させる。当たり前の話ではありますが、AIの活用が広がっているいまだからこそ、切り分けを見直す価値がありそうです。みなさんも、毎回AIに任せている定型計算があれば、関数やコードへの固定化を試してみましょう!