2023年3月23日
Backlog向けChatGPT bot をLarabelで作る
システムソリューション部のYです。
マイクロソフトが「Microsoft 365 Copilot」を発表して話題になったり、ChatGPT 流行してますね。
このようなAIは、企業の生産性にも大きなインパクトを与える可能性が高く、今後、実際の業務で活用する機会が増えてくると思います。
弊社セラフでは、プロジェクト管理にBacklog使うこと多いのですが、
課題担当者に設定すると、ChatGPTでコメントを戻してくれるbotをLarabelで作ってみました。
例えば、「ここまでの話をまとめてください」とコメントすると、要約がコメント投稿されます。
(業務で利用する場合、Azure OpenAI Service 経由 とかでしょうかね)
実装の内容
今回は、WebHookではなく、コマンドなので、Kernel.phpで定期実行させる必要があります。
// Backlog Bot
$schedule->command(ReplyBacklogComment::class)
->cron('* * * * *')
->withoutOverlapping()->runInBackground();
プログラム ReplyBacklogComment.php です。
課題の「件名」「課題の詳細」、投稿コメントを読み込んで、APIに渡し、得た回答をBacklogへコメント投稿してます。
参考になれば。
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use atomita\Backlog;
use atomita\BacklogException;
use Exception;
use App\Models\BacklogNotification;
/**
* composer require しておく
* atomita/backlog-v2
* guzzlehttp/guzzle
*
* 利用テーブル
* Schema::create('backlog_notifications', function (Blueprint $table) {
* $table->id();
* $table->timestamps();
* $table->bigInteger('last_notification_id')->default(0);
* $table->collation = 'utf8mb4_bin';
* });
*/
class ReplyBacklogComment extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:ReplyBacklogComment';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Backlogコメントへ返信';
private $backlog;
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
set_time_limit(0);
try {
$minId = 1;
$res = BacklogNotification::first();
if (!empty($res)) {
$minId = $res->last_notification_id + 1;
}
// backlog初期化
$backlogSpaceId = config('const.backlog_space_id');
$botBacklogApiKey = config('const.bot_backlog_api_key');
$this->backlog = new Backlog($backlogSpaceId, $botBacklogApiKey);
// backlogお知らせ取得
$list = $this->backlog->notifications->get([
'minId' => $minId,
]);
// 1:課題の担当者に設定
$list = array_filter($list, fn($x) => in_array($x['reason'], [1]));
foreach ($list as $item) {
$this->replyComment($item['issue']);
}
// 重複防止
if (!empty($list)) {
BacklogNotification::truncate();
$data = new BacklogNotification();
$data->last_notification_id = $list[0]['id'];
$data->save();
}
return 0;
} catch(Exception $e) {
$this->error($e->getMessage());
Log::debug($e->getMessage());
return 1;
}
return 0;
}
/**
* openaiから得た回答で、コメント生成
*/
public function replyComment($issue)
{
$botBacklogUserId = config('const.bot_backlog_user_id');
$chatMessages = [];
$eventText = "件名: {$issue['summary']}\n\n概要: {$issue['description']}";
$chatMessages[] = ['role'=>'user', 'content'=>$eventText];
try {
$assigneeId = $issue['createdUser']['id'];
// コメント一覧からメッセージ作成
$minId = 0;
for($j=0;$j<10000;$j++) {
$comments = $this->backlog->issues->param($issue['id'])->comments->get([
'minId' => $minId,
'order' => 'asc',
'count' => 100,
]);
if (empty($comments)){
break;
}
foreach ($comments as $item){
if (empty($item['content'])) {
continue;
}
// アシスタント(ChatGPT)側とユーザー側に振り分けます
if ($item['createdUser']['userId'] == $botBacklogUserId) {
$chatMessages[] = ['role'=>'assistant', 'content'=>$item['content']];
} else {
$chatMessages[] = ['role'=>'user', 'content'=>$item['content']];
$assigneeId = $item['createdUser']['id'];
}
}
$minId = $comments[sizeof($comments) - 1]['id'] + 1;
}
// openai
$client = new \GuzzleHttp\Client();
$url = 'https://api.openai.com/v1/chat/completions';
$token = config('const.openai_api_key');
$headers = [
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'application/json',
];
$json = [
"model" => "gpt-3.5-turbo",
"messages" => $chatMessages,
"max_tokens" => 1024,
"temperature" => 1,
];
$data = $client->request('POST', $url, [
'json' => $json,
'headers' => $headers,
])->getBody()->getContents();
$data = json_decode($data);
$replyText = "回答できませんでした";
if (!empty($data->choices) && !empty($data->choices[0])) {
$replyText = trim($data->choices[0]->message->content);
}
// backlogコメントする
$res = $this->backlog->issues->param($issue['id'])->comments->post([
'content' => $replyText,
'notifiedUserId[]' => $assigneeId,
]);
$res = $this->backlog->issues->param($issue['id'])->patch([
'assigneeId' => $assigneeId,
]);
return;
} catch(Exception $e) {
Log::debug($e->getMessage());
return;
}
}
}
The following two tabs change content below.
システムY
最新記事 by システムY (全て見る)
- Backlog向けChatGPT bot をLarabelで作る - 2023年3月23日