Microsoft の AI 技術 をベースに構成される Craft Functions Copilot

はじめに

ごきげんよう、@tik-son です。

プレイドは KARTE Craft という PaaS を提供しているのですが、KARTE Craft で作成する汎用プログラムを Craft Functions と呼びます。

そして、 Craft Functions Copilot (以下、Functions Copilot) はその中の一機能で、Craft Functions の活用を支援してくれる AI アシスタントです。

以下の動画のように、KARTE Craft の使い方や機能開発に関する質問をすると、リアルタイムでその回答やコードの改善提案をしてくれる機能になっています。

s-1280x720_c546f5ab-3a81-47b7-b3bf-3fc04f6defd3.gif

プレイドの今までの技術スタックではあまりなかったのですが、Functions Copilot は Microsoft Azure (以下、Azure) の AI 技術を中心に構成されています。

(AI 周りの技術の進化は早すぎるのですが) 現時点での Azure の最新の情報/技術を駆使して作っているはずなので、
どのような仕組みで動いてるのか、その中で各 Azure サービスはどのような役目を果たしているのかを、
冒頭の動画でも紹介した コードの改善 の機能を例にしてお話しようと思います。

全体構成

まず、全体の構成は以下の通りになります。

copilot-overview.png

Functions Copilot はいわゆる RAG(Retrieval-Augmented Generation) の構成を取っていて、
主に以下の3種類のAzure のサービスを中心に構成されています。

  1. Azure OpenAI Service : OpenAI の AI Model が エンタープライズグレードな機能性で利用できるサービス
  2. Azure AI Search : AI 機能が統合された、RAG 構成の中心になる 強力な検索エンジン
  3. Azure API Management : Azure の各サービスとシームレスな接続性があり、APIの統合管理が出来るサービス

処理の流れ

エンドユーザからのリクエスト

  1. エンドユーザが管理画面から、Functions Copilotにリクエストを投げます。この中身としては 対象のコード箇所それに対するリクエスト になります。

  2. エンドユーザからリクエストを受けて回答を返す機能を持つのが Craft Functions Copilot RPC Server (以下、RPC Server ) です。この RPC Server がオーケストレータ的に、各 Azure サービスに情報を投げて、最終回答まで整えていきます。
    エンドユーザからリクエストを受けると、まずそのリクエストの内容を Azure OpenAI Service のGPT3.5 turbo (以下、GPT3.5 turbo) に渡し、次のステップに適したフォーマットに整えます。
    例えば、 「kvsにkeyがvisitor_idとしてdateを保存したい。その後その内容をloggerで出力したい。最後に実行完了時間をlogger.logしたい。」 という質問があった時、 以下のようなシステムで 活用しやすい JSON ドキュメントで出力します。

    {
      "questions": [
        "visitor_id を使って、日付を kvs に保存する方法は?",
        "logger を使用して、kvs に保存された内容を出力する方法は?",
        "logger を使用して、完了時間を記録する方法は?"
      ]
    }
    

    リクエスト内容が不明瞭であれば、ここでエンドユーザに返信して再度リクエストを促す形になります。

  3. 2 で整えた質問内容 を Azure OpenAI Service の text-embedding-3 でそれぞれ、ベクトル化します。

  4. 3 でベクトル化したデータ と、エンドユーザのもともとの テキストインプット をつかって、Azure AI Search にセマンティックハイブリッド検索をかけます。

  5. 検索結果のスコアを見て最終処理を判斷します。スコアが一定以上であれば、 スコアの高いドキュメントと、対象のコード箇所それに対するリクエストそして Craft Functions の基礎的な情報 を利用し、GPT3.5 turbo で 回答内容を生成します。
    この際にトークン数が GPT3.5 turbo のトークンのリミットである 16k を超えるようであれば、利用するドキュメントを減らしたり、最終リクエストの前に更に GPT3.5 turbo で情報を整理します。
    もし、スコアが一定数ない場合は、対象のコード箇所それに対するリクエストそして Craft Functions の基礎的な情報 をベースに回答を生成します。

  6. 生成された回答を使って、RPC Server がエンドユーザに回答を返信します。

Azure AI Search へのデータ更新

  1. Azure AI Search に格納されているデータは、Craft Develop Portal のコンテンツをベースに作られています。このコンテンツのソースは Github のレポジトリで管理されており、Markdown フォーマットの構造化された情報となっています。
  2. プレイドのエンジニアが新たに機能を開発/改修する際に Markdown ファイル を更新します。更新後、Github で Pull Request をマージします。
  3. マージすると、まずGithub Actions で Craft Develop Portal が更新されます。
  4. その後、Github の Webhook 経由で Craft Functions の Endpoint に更新情報が投げられます。
  5. 対象のドキュメントに更新がある際にはそのドキュメントデータを text-embedding-3 でベクトル化します
  6. ベクトル化された情報とそのドキュメントデータを Azure AI Search に Upsert します。

設計ポイント

全部はこのブログ内で書ききれないのですが、この構成における設計ポイントの一部分をピックアップして書いてみます。

GPT4 でなく、 GPT3.5 turbo を選択している理由

一言でいうと、レスポンス速度コストになります。

GPT3.5 turbo は精度の部分は課題がありつつも、Azure AI Search が 優秀なので、RAG 構成の中でいい感じに補完してくれています。ただ、エンドユーザのユースケースにおいて、GPT3.5 turbo で厳しいシナリオが増えてきたら考え直すかもしれません。
また、GPT4 でないと厳しい Creativity が求められるような機能は、今回の記事のターゲットになっている コード改善 機能の外で作る想定ではあります。

Microsoft の Tech Community Blog に Modelを選択する際の Decision Tree があったので参考にしてみてください。

WinnieNwanne_0-1710767355334.png

GPT Model でのプロンプトや API リクエストでの工夫点

プロンプト

まずは奇をてらわず、ベストプラクティスに従うことが重要です、Microsoftが提供してくれてるガイドラインに従えば一定以上のクオリティのプロンプトはすぐに作れると思います。
Functions Copilot でも System MessageFew-shot Learning を真面目に取り組んでいます。
ただし、インプットを増やせば増やすほどいいというわけでなく、トークン数がリミットを超えてエラーを返したり、逆に ハルシネーション が起きやすくなったり、狙った回答がえにくくなったりすることがあるので、そのバランスを取る事が重要です。

(Claude3 や Gemini もそうなり始めていますが、今の10倍や100倍のトークン数対応が当たり前になる時代がそこまで見えているので、あまり作り込み過ぎない事が重要かもしれません)

JSON Mode

Azure OpenAI Service の GPT Model には JSON Mode という機能があります。AI からの返信が、フリーフォーマットなテキストで返ってきた場合、プログラム内で活用するための前処理が面倒なことがあります。この JSON Mode を使えば、狙った通りのスキーマの JSON データを返信させることができるので、開発効率をあげることができます。

Functions Copilot では前述した、エンドユーザからのリクエスト内容を整理して、次の処理にわたす部分に JSON Mode を利用しています。

Stream Response

Azure OpenAI Service の GPT Model では、利用可能になったトークンから、チャンクでレスポンスを返してくれる Stream Option があります。このオプションを、最後の回答生成の部分 で使ってるのですが、13秒程度待って返信されていたレスポンスが、3秒後から返信が始まるようになり、エンドユーザ体験が格段に良くなりました。

Azure API Management を利用する理由

Azure Open AI Service は クォータ がそれなりに厳しく存在します。
リージョンごとにそれぞれ分間トークン数などに制限がかかるのですが、このクォータはそんなに簡単に上げれるわけではないようです。公式ドキュメントにもこのような書きっぷりになっています。

クォータの増加要求は、Azure OpenAI Studio の [クォータ] ページから送信できます。 需要が殺到しているため、受け付け中のクォータ引き上げの要求は、受領した順に処理されます。 既存のクォータ割り当てを消費するトラフィックを生成するお客様が優先され、この条件を満たしていない場合は要求が拒否される可能性があります。

一方で、production 環境で機能をリリースするとなるとそれなりに負荷がかかることが予想されます。

そこで Functions Copilot では各リージョンごとにそれぞれの Model の Deployment を作成し、それぞれに対して分散させるという方法を取っています。

それを実現しているのが、Azure API Management です。このサービスを使えば、一つのAPI Endpointの裏側に、複数の Model の Deployment を紐づけることが簡単にできます。さらに分散のロジックも色々と作り込むことも可能です。

api-management.png

また、Azure API Management に、Azure OpenAI Service の Cognitive Services OpenAI User の 権限を与えると、Azure OpenAI Service と API Key 無しに通信することも可能で、メンテナンス性、セキュリティ性が高い構成を取ることもできます。

Azure AI Serach のセマンティックハイブリッド検索

Functions Copilot は RAG 構成を取っていると前述しましたが、その中で最も重要と言ってもいい部分が Azure AI Serach のセマンティックハイブリッド検索 です。

GPT3.5 turbo ベースのシステムになっているので、ここのクオリティがいまいちだと実用レベルから大きく乖離した機能になってしまいます。

機能的な紹介は Microsoft の hanagasaki さんが書かれているブログ をご覧いただければと思いますが、

Azure Cognitive Search (※現Azure AI Search) の機能を総動員した検索クエリーです。ハイブリッド検索結果をセマンティック検索による Re-ranker で結果を並び変えます。さらに、セマンティックキャプションセマンティックアンサーを用いた抽出的要約を検索結果に付加します。

これらの表現や以下のグラフが示す通り、かなりハイパフォームする検索技術と言っても過言ではないと思います。

Azure AI Search では検索精度のチューニングについても多くの選択肢が用意されています。現在以下の検索モードが利用可能であり、RAG システムにおいては 1 から 4 へ行くにしたがって精度が向上していくことが Microsoft の定量分析結果で示されています。

  1. キーワード検索(既存の全文検索)
  2. ベクトル検索
  3. ハイブリッド検索
  4. セマンティックハイブリッド検索(Microsoft 独自機能)

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3637343334342f37376261643732382d363631312d653330342d386463642d6663373166653831666338332e706e67.png

【速報】Azure Cognitive Search が Azure AI Search としてリブランドしベクトル検索が GA!

また、text-emnedding-3-large の3072次元のベクトルにも対応しており、さらに高い検索精度が期待できます。

Functions Copilot では、Craft Developer Portal のデータソースになっている Markdown で書かれたドキュメントデータをほぼそのまま入れているのですが、それでもかなり精度の高い検索結果を返してくれます。(実際にただのベクトル検索だと、本当に探したいドキュメントがトップ5 に来ないことが結構ありました。)

ただし、毎回満点の結果が得れるわけではないので、意図しない結果が来た場合に人間でカバーできるUI/UX を作っておく必要がある、と考えています。現時点での Functions Copilot では、回答の最後に関連ドキュメントを AIにきく ボタンとともに表示するようにしており、このボタンにより再度、より意図に近い形で質問を投げ返す事ができる仕様としています。

copilot-ask-ai.png

Azure AI Searchは 2024/4/4 にインフラ面の大幅なアップデートが行われたり、まだまだ進化していく気がするので、一開発者としてこれからも要注目ですね。

ドキュメントの Chunk

現時点では、Functions Copilotではドキュメントの Chunk は行っていません。

情報源がCraft Developer Portalであり、1ページごとのドキュメント量がそこまで大きくなく、一定の意味に Chunk された状態になっているからです。(Developer Portalのわかりやすさとしても長くなりすぎない記事を作るのは重要)

それに加え、ドキュメントソースは、Markdown フォーマットで一定構造化された状態にもなっています。
前述した Azure AI Search のセマンティックハイブリッド検索を駆使すれば、その記事の中身を Chunk せずとも今は一定以上の精度と速度が出る状態となっています。

また別の観点では、昨今の AI 技術の進化は早いので、この Chunk や独自の作り込みを頑張るより、Model や 検索技術の進化を想定して、つど対応 (場合によっては新しい技術に合わせて全面刷新) したほうがコスパがいいとも感じています。

(実際に作り込んだLLM 周りのロジックが OpenAI のアップデートにより不要になってしまったという過去もありました 涙)

まとめ

だいぶ駆け足とはなりましたが、Microsoft の AI 技術 をベースに構成される Functions Copilot について、お話しました。

今回お話したような技術がなければ、Model の部分や検索の部分の実装だけで相当なコストなどがかかり、Functions Copilot の Beta リリースまでたどり着けていなかったかもしれません。

AI にそこまで専門性が高くないエンジニアでもドキュメントを読んだりキャッチアップすれば、一定のクオリティのものを作れるような時代になって面白さを感じると同時に、
AI をとりまく技術の進化が早過ぎて、キャッチアップをおこたると一瞬で周回遅れになるような、恐ろしさも感じています。
Functions Copilot は Beta フェーズが始まったばかりのプロダクトなので、これから利用してもらう中でユーザフィードバック と AI の進化とともに改善していく予定です。
伸びしろしかない機能だと思うので、一緒に改善していきたい人も絶賛募集中です。