Introduction
先日Devinが発表されました。
課題を与えると、自律的に情報収集・コーディング・デバッグ等
システム構築をやってくれるすごいAIだそうです。
ここまでやらなくても、Agent用アカウントにGihubでissueをassignしたら
自動で実装してpull requestだしてくれたら便利では、と思い
実装してみました。
今回はAmazon Bedrock(Claude3)をつかって実装してみたので
それについて解説します。
Environment
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 14.3.1
- Node : v20.8.1
- Git : 2.43.2
AWSアカウントはセットアップ済みとします。
Setup
Agent用Githubアカウントの準備
適当なメールアドレスを用意し、Githubアカウントを作成しましょう。
アカウント作成後、Access Tokenを発行します。
ここでトークンを発行しましょう。
基本的にはFine-grained personal access tokensでいいかと思うのですが、
リポジトリによってはPersonal access tokens (classic)でないとダメかもしれません。
私の環境ではFine-grainedのトークンだとPR作成時にエラーになってしまったので、
classicのトークンを使いました。
Amazon Bedrockの準備
BedrockでClaude 3を使うので、このへんを参考にモデルを設定します。
AgentProgram
では作成していきます。
概要
ざっくりとした動きはこんな感じです。
NodeでAgentを起動した後ポーリングで自分宛てのopenなissueを取得し、
必要に応じてリポジトリのclone/updateを行います。
issue用ブランチを作成してBedrockにissue内容を問い合わせ、
結果をパースしてcommit→push→pull requestとやっていきます。
使用したライブラリなど
実装はNodeでtypescriptを使って実装いました。
GithubへのアクセスはOctakitを使い、
ローカルでのGit操作はsimple-gitを使います。
あとはaws-sdkでBedrockにアクセスしています。
各実装ポイント
Githubからissueリストをもってくる処理は下記のような感じです。
Octokitで簡単にできます。
let octokit: Octokit = new Octokit({ auth: "your githubToken" });
const response = await this.octokit.rest.issues.listForAuthenticatedUser({
filter: 'assigned',
state: 'open',
});
console.dir(response.data);
simple-gitを使ったGitのcloneは下記。
こちらも簡単です。
import simpleGit, { SimpleGit, SimpleGitOptions } from 'simple-git';
let git: SimpleGit = simpleGit({});
await git.clone("clone url", "your repo pash");
Bedrockへアクセス
AWS SDKでのBedrockアクセスもここのような感じで実装します。
モデルはClaude 3 Sonnetを指定します。
const client = new BedrockRuntimeClient({ region: "us-east-1" });
const modelId = "anthropic.claude-3-sonnet-20240229-v1:0";
const payload = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1024,//必要に応じてトークン数を調整
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "your prompt"
}
]
}
],
"temperature": 0.5,
"top_p": 0.9
};
const command = new InvokeModelCommand({
body: JSON.stringify(payload),
contentType: "application/json",
accept: "application/json",
modelId,
});
const response = await client.send(command);
const decodedResponseBody = new TextDecoder().decode(response.body);
/** @type {ResponseBody} */
const responseBody = JSON.parse(decodedResponseBody);
console.dir(responseBody);
プロンプトの作り方
issue内容に対してどのように修正すればよいかをAIに返してもらいます。
プロンプトは下記のような感じでJSON SCHEMAとcodebase全体のコードを渡して
修正内容を提案してもらいました。
下記に幾つかの要求のポリシーを示します。これに従って回答してください。
- 回答は下記JSON SCHEMAに合致するJSON形式で実施してください。
```
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"hoge": {
"type": "string"
},
"fuga": {
"type": "string"
}
}
}
```
- 上記JSON SCHEMAのプロパティの意味は下記です
- ここに各プロパティ内容や仕様を記述
以下の<issue/>が依頼する内容です。
<codebase/>は依頼する内容に関連するコード内容です。
--------------------------------------------------------
<codebase>
//codebase全体の情報
</codebase>
<issue>
//issue内容
</issue>
codebase全体のコードはcode2promptで
ファイル出力したものをloadしてます。
AIからの出力はJSON SCHEMAを渡してその形式で返してもらいます。
こうしておけば自分で定義したclassにJSON.parseでそのまま変換できます。
ファイル修正〜pull request
修正すべき内容が取得できたので、それを適用していきます。
cloneしたcodebaseに対してファイルのupdateを適用していきます。
修正できたらsimple-gitでadd,commit,push。
let git: SimpleGit = simpleGit({});
await git.add("file path");
await git.commit("commitMessage");
await git.push('origin', "branch name");
pushできたらPRを作成します。
const { data } = await octokit.pulls.create({
owner:"リポジトリオーナー名",
repo: "リポジトリ名",
head: `Pull Requestを作成するブランチ名`,
base: 'mainとかのbaseになるブランチ',
title: "PR タイトル",
body: "PR 内容",
});
console.dir(data);
テスト用リポジトリで適当なコードをmainブランチに用意し、
コラボレータとしてAI Agentをinviteします。
そして、↓のようなissueを作成してAgentをアサインします。
作成されたPRは↓みたいな感じです。
「CSSファイル作成してhtmlに適用して」というissueを作成し
実際にマージして動作確認したら、ちゃんとCSSが適用されてました。
今回はシンプルな例でしたが、プロンプトやissue内容を調整すれば
けっこういけるはず。
Summary
今回はGithubでコラボレータとして追加されたAgentに対し、
issueをアサインしたらコード修正〜Pull Requestまで実行する処理を実装してみました。
いくつissueを同時にアサインしても、(tokenが許す限り)並行で作業できるのは便利。
また、一度作ってしまえば、モデルをバージョンアップしただけで
生成されるコードの品質が高くなり、対応できるissueの幅が広がるのもよいです。
シンプルなissueには単価が安いモデルのAgentをassignし、
複雑/重要なissueには高単価のハイスペックなモデルを、なども可能ですね。
PRをレビューしてくるAgentなどもどこかで見ましたが、
AIが生成したコードをAIがレビューして修正する、なども普通にありそうです。
ちなみに、今回はポーリングでissueチェックしましたが、
Guthubのwebhookを使えばissue登録など
任意のタイミングでリクエストをpostできるので、
それのほうがよいかもしれません。
あとはBedrockアクセス〜PR作成まで、MQとか使って処理したほうがよいかも。