LINEヤフー Tech Blog

LINEヤフー株式会社のサービスを支える、技術・開発文化を発信しています。

AI Readyな開発環境の整え方

Orchestration Guildメンバーの迫川です。普段はAgent platformの開発をしています。

この記事は、社内ワークショップOrchestration Development Workshop(以下ODW)#12の内容を紹介します。ODWは、AI活用の実践知を組織横断で共有するコミュニティで、各回のワークショップは実践知を「見る」だけでなく「手を動かして試す」場として続けています。#12では、AIにコードを書かせる前の"下ごしらえ"、つまりAIが力を発揮できる環境の整え方をテーマにしました。

コーディング支援AIが広まって、コードを「書く」体験はここ数年で大きく変わりました。その一方で、現場ではこんな声もよく聞きます。

  • 「AGENTS.md(AIへの指示を書いておくファイル)を置いてみたけれど、正直、効いている実感がない」
  • 「"AI Readyな環境を整えよう" と言われても、具体的に何を整えれば成果物が良くなるのか分からない」

この記事で持ち帰ってほしいのは、「AIの成果物が変わるかどうかは、AIの賢さよりも渡した情報で決まることが多い」という感覚です。ワークショップでは、参加者に同じ依頼を2つの環境でAIに投げてもらい、出てくる成果物そのものが変わる様子を見てもらいました。その様子と考え方を、以下で紹介していきます。実際に使った題材やコード例も交えますが、まず受け取ってほしいのは「どこを整えると効くのか」という勘所です。

※本記事のサンプル(乗換案内の運賃計算)は、ワークショップ用に作成した架空の教材です。運賃などの数値も含め、実在するプロダクトのコードやデータではありません。

AIは、コードを見れば分かることはもう知っている

「AIに情報を渡そう」と言われて最初に思いつくのは、リポジトリ(プログラムのファイル一式をまとめて置いてある場所)の中身をAGENTS.mdに書き写すことかもしれません。フォルダ構成、使っている言語、主要な関数の名前などです。ただ、これはあまり効きません。

AIは、コードを見れば分かることなら自分で読み取れるからです。フォルダ構成も、関数の形も、既存のテストも、リポジトリを開けばAI自身が確認できます。人がわざわざ書き写しても情報が二重になるだけですし、コードを直したときに説明文の更新を忘れると、古くなった説明が逆にAIを迷わせることもあります。

渡して効くのは、コードを見ても分からないことのほうです。

たとえば、新しくチームに入った人に仕事をお願いする場面を思い浮かべてください。ソースコードを見れば分かること(どんな関数があるか)は、いちいち説明しなくても本人が読めます。一方で、「うちではこの書き方が決まり」「この機能は特別な事情でこうしている」といった書かれていない決まりごとは、伝えないと分かりません。AIも同じで、次のようなことは渡さないと届きません。

  • なぜこの作りになっているのか(背景や制約)
  • ビジネスとして正しい動きは何か(コードには「今こう動く」しか書かれていない)
  • チームの決まりごと(名前の付け方、やってはいけないこと)
  • 言葉の意味(同じ単語を、人によって違う意味で使っていないか)

この「コードからは分からないこと」をどうAIに届けるか。それが、環境を整えるということの中身です。

AI Readyな環境、大づかみに3つ

ワークショップでは、AI Readyな環境を大きく3つの方向から考えました。細かい定義は脇に置いて、大づかみに書くとこうなります。

  1. 意図やルールを渡す。コードからは読み取れない「正しい動き」や「決まりごと」を、AIが読める形で置いておく。
  2. AIが扱いやすい単位に分ける。一度に触る範囲が大きすぎるとトラブルが起きやすいので、機能を適切な大きさに切り分けておく。
  3. 工程どうしの受け渡しをそろえる。前の工程の成果物を、次の工程がそのまま使える形にしておく。

この記事では、ワークショップで実際に手を動かした1つ目(意図やルールを渡す)と3つ目(受け渡しをそろえる)を紹介します。

その前に、AIと人はどう協力するのか

ハンズオンに入る前に、前提を一つだけ共有させてください。私たちはAIを、「丸投げして全部やらせる相手」とも「ちょっとした補完」とも捉えていません。人が意図を渡し、AIがたたき台をつくり、人が確かめて次へつなぐ。この小さな協力のくり返しとして考えています(こうした進め方は、AI駆動開発(AI-Driven Development, AIDD)とも呼ばれます)。

人が意図を渡し、AIがたたき台をつくり、人が判断して次の工程へつなぐループを表した図。このループが要件整理・設計・実装・レビューの各工程で繰り返し回る
人とAIの協力のくり返し

このくり返しの出発点は、いつも「人が意図を渡す」ところにあります。ところが意図の多くは、コードではなく仕様書や人の頭の中にあります。そこがAIに届いていないと、どれだけ賢いAIでも、たたき台は的を外します。だから、環境を整えることが効いてきます。

ワークショップの進め方

考え方を聞くだけだと「なるほど」で終わってしまい、翌日には何も変わりません。そこでODW#12では、次のように進めました。

  • 同じ依頼を、情報ナシと情報アリの2環境で並べて投げる。変えるのは渡した情報だけで、コードも依頼文もそろえる。
  • 見るのは、成果物そのものが変わったかどうか。AIが書いたコードを見比べ、答え合わせのテスト(正解と合っているかを自動でチェックする仕組み。合えば「緑(成功)」、外れれば「赤(失敗)」と教えてくれる)が通るか落ちるかで判定する。
  • 答えは環境の中に置かない。正解を作業フォルダの中に置くと、AIがそれを読んで正解してしまい、情報ナシで外す体験が再現できないためです。

参加者に配ったサンプルは、リポジトリを1つ取ってくれば動く、追加インストール不要のものです。フォルダはこう分かれています。

sample/
├── cold/          ← 情報ナシ。仕様も答えも無い、素のリポジトリ
├── context/       ← 情報アリ。中身のコードはcoldと同じ。AI向けの説明ファイルだけが違う
└── _answer-key/   ← 答え合わせ用(cold / contextの外に置く)

cold(情報ナシ)とcontext(情報アリ)で、中身のコードは完全に同じです。違うのは、AI向けの説明ファイルがあるかどうかだけ。この対比が、環境を整えると何が変わるかの答えになります。

ハンズオン(1):AIに意図を渡すと、実装が変わる

題材は乗換案内の運賃計算です。距離と券種(きっぷ/定期)から運賃を出すcalcFareという小さな関数(入力を渡すと答えを返す部品)があります。参加者はこの関数に、coldcontextの両方へまったく同じ依頼を投げます。

学割(学生向けの割引)に対応してほしい。calcFareに学生向けの割引を追加して。

ここでのポイントは、割引が何%か、端数(半端な金額)をどうするか、定期にも効くのか、といった本当のルールがコードのどこにも書かれていないことです。ルールは、仕様書か人の頭の中にしかありません。

同じ依頼をcold(情報ナシ)とcontext(情報アリ)に投げた結果の対比図。coldは割引率が一律10%・端数は四捨五入・定期にも割引・APIはisStudentで、答え合わせテストは失敗。contextは距離帯ごとに20%か30%・端数は10円切り上げ・定期は対象外・APIはpassengerTypeで、テストは成功。コードは同じで、違うのは渡した情報だけ
同じ依頼でも、渡す情報で実装が変わる(左:情報ナシ/右:情報アリ)

情報ナシ(cold)の場合

coldには答えがないので、AIは自分で推測して埋めるしかありません。たいていはこんな風に外します。

  • 距離に関係なく、割引率を一律にしてしまう
  • 割引率をなんとなく10%と決めてしまう
  • 端数の処理を四捨五入にしてしまう(本当は切り上げ)
  • 定期券にも割引を効かせてしまう(本当は対象外)

見た目は動くコードが出てきますし、もともとあったテストも緑(成功)のままです。ただ、それはたまたま当たっているように見えているだけです。

情報アリ(context)の場合

contextには、同じコードに加えて、AI向けの意図やルールを書いたファイルが置いてあります。たとえば、割引の本当のルールはこうです。

条件割引
学生・きっぷ・5km以下割引なし(近すぎるので対象外)
学生・きっぷ・6〜15km20%引き(端数は10円単位で切り上げ)
学生・きっぷ・16km以上30%引き(端数は10円単位で切り上げ)
学生・定期割引なし(通学定期は別の制度)

長い距離ほど割引を手厚くする制度で、距離によって割引率が変わるのが特徴です。これはコードを見ても分かりません。ほかにも「学割と子ども料金は別物なので混ぜない」といった決まりが書いてあります。

同じ依頼をcontextに投げると、AIはこれらを読んで、距離帯ごとの割引・10円単位で切り上げ・定期は対象外と、意図どおりに実装します。

どう変わったかを確かめる

「なんとなく良くなった気がする」で終わらせないために、目で見て確かめられるようにしてあります。

一つは、2つの実装を並べて見比べる方法です。割引率、距離の条件、端数の処理、名前の付け方が変わっているのが分かります。

もう一つは、答え合わせ用のテストを両方に当てる方法です。正解の値をあらかじめ書いておいたテストを走らせると、coldは間違っているので失敗(赤)、contextは成功(緑)になります。

// 8kmの学生・きっぷ → 180円(220円の20%引き=176円を、10円単位で切り上げ)
calcFare({ distanceKm: 8, passengerType: "student" }) // → 180
// 25kmなら30%引き → 280円
calcFare({ distanceKm: 25, passengerType: "student" }) // → 280

coldは一律で推測しているので、距離帯の境目(たとえば5kmは割引なし、6kmなら20%引き)でところどころ間違えます。contextは全部通ります。AIが質問し返してこなくなるのではなく、出てくるコードそのものが正しくなる。ここが、意図を渡すことの効果です。

「たまたま成功する」問題

こういう体験をやると、必ずこんな疑問が出ます。「cold(情報ナシ)でも、AIがたまたま正解して成功することがあるのでは? それは結果ありきの見せ方では?」と。

そのとおりで、AIは毎回まったく同じ答えを返すわけではないので、coldでも推測がたまたま当たって成功することがあります。私たちはこれを隠さず、むしろ体験の核心として扱いました。見せたいのは「coldは絶対に間違う」ではなく、その先の違いだからです。coldは運で、AIの解釈しだいでブレます。contextは仕組みで、誰が何回やっても同じ前提にそろいます。

coldとcontextの違い�を表した図。左のcold(運)は、同じ依頼(情報ナシ)から、あるときは180で成功し、あるときは200・190・176で失敗するというように結果がバラつく。右のcontext(仕組み)は、同じ依頼にルールを足すことで、誰が何回やっても180の成功に収束する
coldは「運」(結果がブレる)、contextは「仕組み」(毎回そろう)

coldがたまたま成功したら、こう試します。「では同じ依頼をもう一度投げてみましょう。隣の人の結果と比べてみましょう」。すると、割引率や距離の境目をAIが根拠なく推測で決めていたことが見えてきます。コードにルールがないので、やり直すたび、人が変わるたびに、簡単にブレるのです。一方contextは、共通のルールに全員がそろうので、毎回・全員で同じ結果になります。環境を整える本当の価値は、成果物が正しいことだけでなく、それが毎回そろうことにあります。

ハンズオン(2):工程どうしの受け渡しをそろえる

(1)は、一つの作業の中で意図を渡す話でした。(2)は、作業と作業のつなぎ目の話です。

ソフトウェア開発は、要件を決めて、設計して、実装して、というふうに工程をリレーのようにつないで進みます。このとき前の工程の成果物があいまいなまま次に渡ると、受け取った側(今回は実装するAI)が推測で埋めることになり、やはりブレます。

題材は、さきほどの運賃計算に「時間帯によって運賃を変える(混む時間と深夜は少し高くする)」機能を足すこと。前の工程からは、こんなざっくりしたメモだけが渡ってきます。

混雑する時間帯は運賃を少し高くしたいです。朝と夕方のラッシュアワーが対象。あと、深夜の便はコストもかかるのでもう少し高めにしたいですね。ラッシュは「ちょっと割増」くらい、深夜は「それより少し高め」のイメージです。金額は端数が出ないように、きれいな額に整えてください。

あいまいなメモのままだと

このメモ、人間ならなんとなく分かります。ただ、実装するAIからすると、決まっていないことだらけです。

  • 「朝と夕方のラッシュ」は何時から何時か
  • 「ちょっと割増」は何%か
  • 「きれいな額」は10円単位か100円単位か、切り上げか四捨五入か
  • 9時ちょうどはラッシュに入るのか、入らないのか

このメモをそのまま渡すと、実装する側(AI)ごとに違う解釈で埋めてしまい、成果物がバラバラになります。しかも元のテストは通ってしまうので、テストは成功しているのに意図とは違う、ということが起こります。

受け渡しの形をそろえる

工程の受け渡しをそろえる流れの図。前工程の成果物であるざっくりメモ(割増率・端数・境界が未確定)を、手順(1)で入力から期待値への表(時刻・割増率・端数・境界が一意に決まった仕様)に整え、手順(2)でその表だけを見て実装するとテストが成功する。話しかけ方も決まった手順にすることで、誰がやっても同じ形でつながる
ざっくりメモを「そのまま使える仕様」に整えてから次工程へ渡す

そこで、前の工程の成果物を、次の工程がそのまま使える形に整えます。あいまいなメモを、「この入力なら、この答え」という表にまで落とし込むわけです(抜粋)。

距離時刻期待する運賃理由
8km8時270円220円 ×1.2(ラッシュ +20%)= 264 → 10円単位で切り上げて270
8km2時290円220円 ×1.3(深夜 +30%)= 286 → 290
8km9時220円9時ちょうどはラッシュに含めない(割増なし)

ここまで決めてあれば、実装するAIは推測なしで作れます。時刻の範囲も割増率も端数も境目も、迷う余地がありません。

お願いのしかたもそろえる

とはいえ、「表にしよう」と言っても、人によってAIへの頼み方がバラバラだと、また結果がブレます。そこでワークショップでは、各工程で使う頼み方そのものを、決まった手順として配りました。

  • 1つ目の手順は、ざっくりメモを、さきほどのような表(決まった形の仕様)に整えること。
  • 2つ目の手順は、その表だけを見て実装すること。表にないことは推測せず、足りなければ止まって確認します。

どちらの手順も、いきなり書き始めません。やることを書き出し、人が確認してOKを出し、1つずつ進める、という流れをはさみます。こうして前の工程の成果物を次の工程がそのまま受け取れるようになり、誰がやっても受け渡しの形がブレなくなります。

確かめ方は(1)と同じで、答え合わせのテストを当てて、失敗(赤)から成功(緑)への変化を見ます。ざっくりメモだけのcold(情報ナシ)は時刻や境目の解釈でよく失敗しますが、上の手順で整えてから渡すと成功します。

まとめ

ワークショップで一番伝えたかったのは、用語の名前でも、特定のツールの使い方でもありません。AIがうまく動かないとき、まず疑うべきはAIの賢さよりも渡している情報だ、という見方です。

この見方に立つと、チームで何から手をつければいいかも見えてきます。ワークショップを通じて私たちが特に強調したのは、次の3つでした。

  • 意図を渡すことから始める。AIが同じ場所を何度も間違えるなら、たいていは、コードに書いていない意図が届いていないサインです。その一つをAIが読めるファイルに書き足すだけで、結果が変わります。
  • 書き写しではなく、コードから分からないことを渡す。フォルダ構成のような「見れば分かること」は、書いてもあまり効かないことが多いようです。効きやすいのは、背景やルール、決まりごとのほうだと感じています。
  • AIに作らせた文脈ドキュメントは、そのまま使わない。ワークショップでは、こうした文脈ドキュメントをAIに自動生成させる手順も紹介しました。

AIがうまく動かないとき、つい、プロンプト(AIへの指示文)の言い回しや、どのAIを使うかに目が行きます。でも一度、渡している情報のほうを見直してみると、打ち手が変わってきます。ODWはこれからも、それを頭で知るだけでなく、手で確かめながら試せる場でありたいと思っています。