LangChainで業務アプリの機能を作ろうとして学んだこと(失敗談)

初めまして。人事業界のSaaS企業でバックエンドエンジニアをしている蔭山です。

この度、私は業務の一環として3か月ほどlangChanを活用した新規機能開発を行っていました。
最終的に精度の問題で開発はストップになったのですが、初心者ながら業務で生成系AIに向き合った中で学んだことを記事にしたいと思います。

私は初心者であり生成系AIに詳しいわけではないので、あくまで新卒やインターンに生成系AI機能開発をさせる場合のイチ参考にして頂けると幸いです。
また、内容に間違いや勘違いが含まれている可能性があります。その際はコメントでご指摘くださると幸いです。

## 作成しようとした機能
弊社のサービスでは、顧客側で自由にエクセル関数のような計算式を組める機能を提供しています。
非常に便利な機能である反面、顧客にとっては使いこなすまでが難しく、必然的に社内でもお問い合わせ対応に時間を割かれてしまう状態でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2605179/ecaa29f7-93c0-f0d6-51a9-4f1a0ef13757.png)

そこで流行りの生成系AIを活用することで、顧客の要望を満たす計算式を自動生成出来れば、顧客はすぐさま計算式を使いこなせてお問い合わせ負担も軽減されるだろうと考え、計算式自動生成機能を開発することにしました。

業務ではGoを使用しており、今回はLangChain Goという非公式パッケージを開発に用いました。

https://github.com/tmc/langchaingo

### システムの流れ
当初考えていた自動生成計算式機能の流れを以下に示します。
計算式の仕様と情報をLLMに与えつつ生成させるといった一般的な構成です。
これを基本として、機能修正を行っていきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2605179/39cbd6de-9cd0-a248-4257-15b2db2e29dd.png)

## 何に躓いたか
自動で計算式を生成させるうえで、一番の問題であったのは**精度が出ない**ことでした。

計算式を組む上で考えないといけないことは多岐にわたります。
- 顧客の要望の分析
- 使用できる関数の確認
- 計算式に用いる項目の型や値の分析
- 計算式機能の細かな仕様の把握
- 生成した計算式が正しいかどうか

また、1つの要望を満たす計算式は何通りもあるため、ただ参考例を与えるだけではなく、なぜそう組んでいるのかまでLLMに考えさせる必要がありました。

### 要望とは異なる計算式が出力されてしまう
プロダクトの機能として提供する以上、顧客企業の給与計算や昇給判定にも絡んでくることから、間違った計算式を提供してしまうとお客様へ被害が出てしまいます。
また、せっかく開発したにも関わらず精度が低いために、結局お問い合わせ件数が増えるようでは意味がありません。

そのため、生成した計算式の正答率を上げるのは必須課題でした。
とはいえども、LLMの構造上あくまで出現確率に基づいた回答しか過ぎず、RAGを与えようともなかなか精度は上がりません。

ステップ・バイ・ステップや聞き返し手法、段階的推論も試しましたが、やはり上記観点がネックとなり、満足のいく精度は出ませんでした。

### 正しい計算式が出力されない
もし動かない計算式を生成してしまうと、顧客は計算式の仕様を深く把握できていないため、なぜ動かないかを理解できず、修正も難しくなります。

これを解決するためにOpenInterpreterのように、作成した計算式を仮実行させる機能を用意することにしました。

早速手元で動かしてみますが思うように精度が出ません。その理由として以下が考えられました。
* LLMの既存知識不足
  * プログラムの生成であれば、LLMはすでにある程度学習済であるため、エラー内容を踏まえて修正が可能です。
  * しかし、「弊社独自の計算式仕様」をLLMは学習していません。そのため、修正を実行する際にはエラー内容だけでなく、参考にすべき計算式仕様も与える必要がありました。
  * また、ただ与えるだけではLLMの構造上仕様を理解出来ず、精度向上に苦しみました。
* エラー内容を計算式仕様に上手く変換できなかった
  * 計算式の実行時に構文木解析が行われていたのですが、そのエラー内容をLLMに与える方法に悩みました。
  * エラーの種類も、項目の型比較エラーや数学的エラー、計算式構文エラー等複数あり、それぞれに適切な計算式仕様や参考例を選択する必要が有りました。
  * また、理想的な参考例を与えれたとしても、LLMはなぜその式になっているのかを理解出来ていないため、ハレーションを起こしてしまいました。

### その他多くの壁が立ちはだかった
- トークン量制限が厳しい
  - 関数一覧や項目名一覧を一括で与えることは出来ず、分割問い合わせを行うように処理を変更しました
  - しかし、LLMが計算式仕様の前提知識を持ち得ていないため、正しい関数や項目の抽出を行うことが出来ませんでした。
  - GPT-4-turboの登場によりトークン量制限は一旦考えなくて良くなったのはありがたかったです。
- RAGや段階的推論、エラー修正に用いる参考例が揃わない
  - 精度向上のためには上質な参考データが必須では有るのですが、いかんせん一企業のサービスであるため十分なデータセットが揃いませんでした
  - また、自分でデータセットを考えようにもLLMの出現確率に基づいた回答に苦しめられました

## 学んだことと反省
### 得た知見
- LLMは与えた情報に基づいた事実を答えることは出来ない
  - LLMはその仕組み上、膨大なデータを学習した結果その出現確率に基づいて結果を返しているに過ぎません。
  - 今回の計算式自動生成では、「〇〇の場合~出来ない」といった仕様書のとある1文のみでは計算式修正を正しく行えませんでした。
- 専門的な領域を扱うにはRAGのみでは厳しく、チューニング等が必要
  - 専門的な仕様書を与えただけではその事実に基づいた回答をLLMにさせることは難しく、Fine-tuningや独自のモデルを作成する必要があると感じました。
- LLMには厳密さを要求するよりも、既存知識や創造性を活かすべき
  - 開発を行っている３ヶ月の間でもっとも革新的な伸びを見せたのは画像や動画の分野だと感じています。
    - [顔を固定した画像生成も可能。写真、画像何でもござれ Stable Diffusion](https://ja.stability.ai/stable-diffusion)
    - [AIによる音楽生成&共有プラットフォーム Suno AI](https://app.suno.ai/)
    - [AIによる服装着せ替えが可能 Outfit Anyone](https://github.com/HumanAIGC/OutfitAnyone?tab=readme-ov-file), [Hagging Face](https://huggingface.co/spaces/HumanAIGC/OutfitAnyone)

### 検討すべきだったこと
> 機械学習の知見を持って独自モデルを作成する

上記に尽きます。
もっと食わず嫌いせず、積極的に機械学習を取りに行くべきであったと反省しています。持ちうるデータセットが少ないという明確な問題があるからこそ、LLMを教師とした特化モデル作成に踏み切っても良かったのではと思っています。

自分は終始3ヶ月間、精度上げに関しては、RAGや段階的推論、プロンプト言い換えといったOpenAIのGPTを対象にした手法しか取り組んできませんでした。
もちろん、GPTは優秀ですのである程度のそれっぽい計算式は作成できます。しかし、それ以上を求めようとすると途端にLLMの出現確率に基づく回答がネックになりました。

https://www.itmedia.co.jp/news/articles/2312/15/news158.html

最近、ニュースで話題にもなっていた「自治体のゴミ出しに関する問い合わせを自動回答させるシステム」でも、間違った回答が出来ないという都合上断念されていました。おそらくチューニングも行っていたでしょうが、LLMで100%に近い精度を求めることの難しさを表しているニュースだと感じます。

LLaMaといったオープンソースのモデルも公開されており、実際にチューニングを行った事例も多く出ています。
やはり今後は、いかに専門的なローカルデータを用いながら、GPTといった大規模LLMモデルを教師としつつ、チューニングを行うかが肝になるのではないでしょうか。

https://zenn.dev/tokyotech_lm/articles/d6cb3a8fdfc907

https://tech.preferred.jp/ja/blog/knowledge-distillation-using-intermediate-representations/
## まとめ
LLMを業務アプリに組み込む際、一番重要な点は「業務の専門的な知識をいかにしてLLMに教えるか」だと思っています。
とはいえ、日々LLMの精度を向上させる論文や[OpenAI公式のプロンプトガイド](https://platform.openai.com/docs/guides/prompt-engineering)が発表されていますので、一辺倒にならず、まだまだ自分自身改善の余地があるのかも知れません。精進します。

最後に、精度向上のための手法をいくつか載せておしまいにします。稚拙な文ですが最後まで読んでくださり、ありがとうございました。

**参考になる精度向上のための手法**
- LLMへ与えるプロンプトを圧縮させる

https://aiboom.net/archives/60431

- LLMに与えるデータをLLMによって生成させ、自己学習させる

https://x.com/ai_database/status/1734563358475682097?s=20

- ユーザーの入力をLLMが理解しやすいようにLLMに言い換えさせる

https://x.com/ai_database/status/1722437222966923363?s=20