🤖
ChatGPTにモックサーバーをやってもらった
概要
ChatGPTは Linux Terminal や SQL Terminalの動作をシミュレーションできたりします。しかも、過去に実行したコマンド履歴に応じた応答をしてくれます。すごい。
Linux Terminalをシミュレーションしてもらった例
そこで、今回はChatGPTにREST APIの動作をシミュレーションしてもらってそれをモックサーバとして利用してみました。
最初に言っておきますが、実用性はないただのお遊びです。
コード
機能としては以下の2つです。
-
/swagger
REST APIの仕様を自然言語で指示します。その内容に基づいてCompletions APIでSwaggerが内部的に生成されます。 -
(.*)
Swagger仕様とリクエストに応じたレスポンスをChat Completions APIで生成して返します。リクエストは履歴として保存しておき、更新内容が反映されるようにします。ただし、現状はトークンの制限があるため覚えておける履歴には限度があります。
app.js
const Koa = require('koa');
const Router = require('koa-router');
const { koaBody } = require('koa-body');
const { Configuration, OpenAIApi } = require('openai');
require('dotenv').config();
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
let swagger = 'Nothing yet';
const history = [];
const router = new Router();
router
.post('/swagger', async(ctx) => {
try {
const SWAGGER_GENERATOR_PROMPT =
`I would like you to generate a Swagger based on the following specifications
I want you to complete the missing specifications.
I WANT YOU TO RETURN ONLY THE SWAGGER YAML INSIDE ONE UNIQUE CODE BLOCK, AND NOTHING ELSE.
# Specification
${ctx.request.body.prompt}
Let's begin.
`;
const messages = [
{role: 'user', content: SWAGGER_GENERATOR_PROMPT},
];
console.debug(messages);
const response = await openai.createChatCompletion({
model: 'gpt-3.5-turbo',
messages,
temperature: 0,
});
swagger = response?.data?.choices[0]?.message?.content;
console.log(swagger);
ctx.response.body = { completion: swagger };
ctx.response.status = 200;
} catch (e) {
ctx.response.body = e?.response?.data || e;
ctx.response.status = 500;
}
})
.all('(.*)', async(ctx) => {
try {
history.push({role: 'user', content: `${ctx.request.method} ${ctx.request.url} HTTP/1.1\n\n${JSON.stringify(ctx.request.body || '')}`});
const REST_API_SYSTEM_PROMPT =
`I want you to act as a REST API based on the following Swagger spec.
I want you to prepare the initial data.
I want you to update the data based on the Swagger spec.
I want you to return an error response if there is an issue with a request.
I WANT YOU TO ONLY REPLY WITH THE HTTP RESPONSE, AND NOTHING ELSE.
# Swagger spec
${swagger}
Let's begin.
`;
const messages = [
{role: 'system', content: REST_API_SYSTEM_PROMPT},
...history,
];
console.debug(messages);
const response = await openai.createChatCompletion({
model: 'gpt-3.5-turbo',
messages,
temperature: 0,
});
const completion = response?.data?.choices[0]?.message?.content;
console.debug(completion)
const [ headers, body ] = completion.split('\n\n');
const headerLines = headers.split('\n');
const [ _, status ] = headerLines.shift().split(' ');
history.push({role: 'assistant', content: completion});
ctx.response.body = JSON.parse(body.replace('#.*', '').trim());
ctx.response.status = Number(status);
} catch (e) {
ctx.response.body = e?.response?.data || e;
ctx.response.status = 500;
}
});
const app = new Koa();
app.use(koaBody());
app.use(router.routes());
app.listen(3000);
使い方
まずはREST APIの仕様を指定してみます。今回はTODOアプリのAPIとして振る舞ってもらいます。それなりに大きな定義になるのでかなり時間がかかります。
POST /swagger
$ curl -X POST -H "Content-Type: application/json" -d '{"prompt": "TODO(タイトル、期限、ステータス)を管理するアプリケーション"}' http://localhost:3000/swagger
Swaggerが生成できたら、さっそくデータを登録してみます。
POST /todos
$ curl -X POST -H "Content-Type: application/json" -d '{ "title": "Buy eggs", "description": "I need eggs to make an omelet", "deadline": "2023-03-24"}' http://localhost:3000/todos
{"id":1,"title":"Buy eggs","description":"I need eggs to make an omelet","deadline":"2023-03-24","status":"TODO"}%
次にデータを取得してみます。ちゃんと先ほど登録したデータが返ってきました。
GET /todos
$ curl -X GET -H "Content-Type: application/json" http://localhost:3000/todos
[{"id":1,"title":"Buy eggs","description":"I need eggs to make an omelet","deadline":"2023-03-24","status":"TODO"}]%
リクエストが正しくないとちゃんとエラーも返してくれます。
POST /todos
$ curl -X POST -H "Content-Type: application/json" -d '{ "title": "Buy eggs", "deadline": "2023-03-24"}' http://localhost:3000/Todos
{"error":"Todo description and status are missing"}%
まとめ
現在の段階では、安定性やコスト、パフォーマンス、データの永続性といった課題が存在し、今回のような遊び用途以外での活用は難しいでしょう。しかし、今後のLLMの進化によっては、このようなアプローチで非常に柔軟なWebアプリケーションを構築することが一般的になるかもしれません。楽しい時代になってきました!
Discussion