この記事はCyberAgent Developers Advent Calendar 2023 7日目の記事です。
こんにちは、AI事業本部の徳田(@tokkuu)です。
OpenAIのChatGPTが2022年11月に公開されてから1年が経過し、その間に数多くの生成AI系のサービスが世に出てきました。これらのサービスはSaaS形式で提供されるものや、OSSとして公開されるものなど多岐にわたり、OpenAI APIと連携して利用するものも多いです。
しかし、企業やチームによっては、ガバナンスやセキュリティの観点から、生成AI系のサービスを導入する際にAzure OpenAI Serviceを利用したいと考える場合もあります。その一方で、Azure OpenAI Serviceの利用方法は本家のOpenAI APIとは異なり、そのままではAzure OpenAI Serviceをバックエンドとして活用できない場合もあります。
そこで本記事では、Azure OpenAI ServiceをOpenAI APIとして活用するための具体的な手順を解説します。一度作成すれば様々なことに応用できる他、OpenAI APIよりもチューニングの幅が広いため、ぜひ活用してみてください。
TerraformでAzure OpenAI ServiceとAzure Functionsを用意する
まずはAzure OpenAI ServiceとAzure FunctionsをTerraformを用いて構築する方法について説明します。すべてのterraformの定義を記載するわけではないので、この他に必要なリソース(例えばリソースグループなど)は適宜準備してください。
Azure OpenAI Serviceの設定
Azure OpenAI Serviceを作成するためのTerraformファイル(open_ai.tf)を作成します。
resource "azurerm_cognitive_account" "open_ai_api" {
name = "openai-account"
resource_group_name = <your resource group name>
location = <your resource group location>
kind = "OpenAI"
sku_name = "S0"
public_network_access_enabled = true
}
resource "azurerm_cognitive_deployment" "open_ai_api" {
name = "public_endpoint"
cognitive_account_id = azurerm_cognitive_account.open_ai_api.id
model {
format = "OpenAI"
name = "gpt-35-turbo"
version = "0613"
}
scale {
type = "Standard"
capacity = 30
}
}
OpenAIのAPIを利用するためのAzure Cognitive Accountを作成し、その後、GPT-3.5-turboモデルを使用するためのサービスデプロイメントを設定します。
モデルやcapacityなどは利用頻度等によって適当に調整してください。
Azure Functionsの設定
次に、Azure Functionsを作成するためのTerraformファイル(app_service.tf)を作成します。
resource "azurerm_storage_account" "storage_account" {
name = "openaistoragesample"
resource_group_name = <your resource group name>
location = <your resource group location>
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_service_plan" "app_service_plan" {
name = "openai-app-service-plan"
resource_group_name = <your resource group name>
location = <your resource group location>
os_type = "Linux"
sku_name = "Y1"
}
resource "azurerm_linux_function_app" "function_app" {
name = "openai-function-app-sample"
resource_group_name = <your resource group name>
location = <your resource group location>
service_plan_id = azurerm_service_plan.app_service_plan.id
app_settings = {
"WEBSITE_RUN_FROM_PACKAGE" = "",
"WEBSITE_MOUNT_ENABLED" = "1",
"FUNCTIONS_WORKER_RUNTIME" = "node",
}
site_config {
application_stack {
node_version = "18"
}
}
storage_account_name = azurerm_storage_account.storage_account.name
storage_account_access_key = azurerm_storage_account.storage_account.primary_access_key
lifecycle {
ignore_changes = [
app_settings["WEBSITE_RUN_FROM_PACKAGE"],
]
}
}
Azure Functionsを作成するためのTerraformコードです。ほとんどazurerm_linux_function_appのExample Usageそのままです。
azurerm_function_appはversion 3.0以降deprecatedとなっていますので注意してください。
APIのリクエスト・レスポンスを変換するAzure Functionsの関数を作る
インフラの土台が出来上がったところで、実際にFunctionsを実装していきます。
Azure FunctionsではOpenAI APIへのリクエストとして渡ってきた、Authorizationヘッダーのトークンをapi-keyに詰め直してAzure OpenAI Serviceへ送信します。
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
const httpTrigger: AzureFunction = async function (
context: Context,
req: HttpRequest
): Promise<void> {
// Baerer tokenで渡ってくるので抜き出してapi-keyに設定する
const rawToken = req.headers["Authorization"] || req.headers["authorization"];
const [tokenType, token] = rawToken.split(" ");
// Bearer tokenとして渡ってこない場合は401を返す
if (tokenType !== "Bearer") {
context.log("Unexpected token type");
context.res = {
status: 401,
body: "Unauthorized",
};
return;
}
const headers = {
"Content-Type": "application/json",
"api-key": token,
};
// bodyがない場合は400を返す
if (!req.body) {
context.log("undefined body");
context.res = {
status: 400,
body: "Bad Request",
};
return;
}
const body = JSON.stringify(req.body);
if (!process.env.DEPLOYMENT_ID) {
context.log("undefined DEPLOYMENT_ID");
context.res = {
status: 500,
body: "Internal Server Error",
};
return;
}
const url = `https://japaneast.api.cognitive.microsoft.com/openai/deployments/${process.env.DEPLOYMENT_ID}/chat/completions?api-version=2023-07-01-preview`;
const res = await fetch(url, {
method: "POST",
headers,
body,
}).then((res) => {
return res.json();
});
context.res = {
body: res,
};
};
export default httpTrigger;
Azure FunctionsのTypeScriptを使用していますが、モデルをv4にすると、正しくfunctionを認識できなかったのでv3を使用しました。
参考: コマンド ラインから TypeScript 関数を作成する – Azure Functions | Microsoft Learn
本家OpenAI API用に作られたサービスをつかってみる
前述の通り、今回はCode Rabbitを動かしてみました。
GitHub Appとして動作するサービスの方ではなく、CodeRabbitが提供するGitHub Actionsを利用しています。
coderabbitai/ai-pr-reviewer: AI-based Pull Request Summarizer and Reviewer with Chat Capabilities.
こちらのコードを見ると、中でchatgpt というnpmパッケージを利用しており、こちらは本家OpenAI APIを前提として作成されています。
利用したGitHub Actionsの実装を見ると、openai_base_urlというパラメータを渡すことで、向き先を変更できそうだったので、こちらにAzure Functionsのエンドポイントを設定します。
https://github.com/coderabbitai/ai-pr-reviewer/blob/main/action.yml#L147
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: coderabbitai/ai-pr-reviewer@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
BASE_URL: ${{ secrets.BASE_URL }}
with:
debug: true
review_simple_changes: false
review_comment_lgtm: false
language: ja-JP
openai_base_url: ${{ env.BASE_URL }} # ここにAzure Functionsのエンドポイントを設定する
「もう初回コードレビューはAIに任せる時代になった – CodeRabbit – 」という記事にあるような方法で system_message
、 summarize
、 summarize_release_notes
を追加してプロンプトをチューニングし、無事以下のように実行できました。
まとめ
使いたい生成AIのサービスが本家のOpenAI APIを前提として組まれているような場合に、今回の構成を使えばAzure OpenAI Serviceをバックエンドに据えて利用することができます。
一度用意しておけば、例えば独自にリクエストの内容を書き換えたいような場合や、今後2つのAPIの仕様に差が出てきた場合でも、柔軟に対応することが出来ます。