Dockerは最近、生産性を高めるのに役立つエージェントを作成するという非常に単純な目標を持つ社内 24時間のハッカソンを完了しました。
このトピックについて考えているうちに、チャット インターフェイスにこれ以上時間を費やしたくないことに気づきました。ワークフローをトリガーするために人間を必要としない完全に自動化されたエージェントを作成できないのはなぜですか?結局のところ、 エージェントは機械が生成した入力によってトリガーされます。
この投稿では、エージェント AI を使用してイベント駆動型アプリケーションを構築します。ビルドするイベント駆動型エージェントは、GitHub Webhook に応答して、PR を自動的に閉じる必要があるかどうかを判断します。Gemma3 モデルと Qwen3 モデルを使用する理由、GitHub MCP サーバーと新しい Docker MCP ゲートウェイの接続、Mastra エージェント フレームワークの選択など、計画からコーディングまでのプロセス全体を順を追って説明します。
問題空間
Docker には、サンプル アプリケーション、チュートリアル、ワークショップに使用されるリポジトリが多数あります。これらは、 学生が最初の Dockerfile の作成、 エージェント アプリケーションの構築など、Docker のさまざまな側面を学習できるように慎重に作成されています。
場合によっては、新しい Docker ユーザーから、作成した新しい Dockerfile や行ったアプリケーションの更新を含む pull request を受け取ることがあります。

ユーザーがチュートリアルの完了中に Web サイトに加えた更新を送信したサンプルの pull request
彼らがチュートリアルを完了し、自分の作品を披露したいと思っていることに興奮していますが、次のユーザーが作業を完了する能力に影響を与えるため、プルリクエストを受け入れることはできません。
これらの PR の多くはまったく新しい開発者からのものであることを認識し、PR を受け入れることはできないことを伝えながらも、学習を続けるよう促すために素敵なコメントを書きたいと思います。
これにはそれほど時間はかかりませんが、自動化の候補として適しているように感じます。よりタイムリーに対応し、PRキューを資料の実際の改善に集中させることができます。
自動化の計画
目標: エージェントを使用して PR を分析し、それが "チュートリアルを完了しました" という送信であるかどうかを検出し、コメントを生成して PR を自動閉じます。そして、プロセス全体を自動化できるでしょうか?
幸いなことに、GitHubには、新しいPRが開かれたときに受け取ることができるWebhookがあります。
タスクを分解する中で、完了する必要がある 3 つのタスクを特定しました。
- PR を分析 する – PR の内容を見て、リポジトリの内容に拡張する可能性があります (チュートリアルは実際には何についてですか?PR を閉じる必要があるかどうかを判断します。
- コメントを生成する – PR が終了することを示すコメントを生成し、励ましを提供し、貢献に感謝します。
- コメントを投稿してPRを閉じる – コメントを実際に投稿してPRを閉じます。
この設定では、次のようなエージェント アプリケーション アーキテクチャが必要でした。

アプリのフローを示すアーキテクチャ図: GitHub で開かれた PR は、エージェント アプリケーションによって受信された Webhook をトリガーし、作業を 3 つのサブエージェントに委任します
エージェント AI を使用したイベント駆動型アプリケーションの構築
私が最初にしたことは、エージェントの枠組みを選ぶことでした。最終的に、マルチエージェント フローや条件付きワークフローなどをサポートする Typescript ベースのフレームワークである Mastra.ai にたどり着きました。私がそれを選んだのは、私が JavaScript に最も慣れていて、フレームワークが提供する機能に興味をそそられたからです。
1。適切なエージェントツールを選択する
フレームワークを選択した後、次にエージェントが必要とするツールを選択しました。これにはGitHubの分析と操作が含まれていたため、 GitHub公式MCPサーバーを選択しました。
新しくリリースされた Docker MCP Gateway を使用すると、Composeファイルに簡単にプラグインできました。GitHub MCPサーバーには 70 を超えるツールがあるため、必要なコンテキストサイズを減らして速度を上げるために必要なツールのみを含めるように、公開されているツールをフィルタリングすることにしました。
services:
mcp-gateway:
image: docker/mcp-gateway:latest
command:
- --transport=sse
- --servers=github-official
- --tools=get_commit,get_pull_request,get_pull_request_diff,get_pull_request_files,get_file_contents,add_issue_comment,get_issue_comments,update_pull_request
use_api_socket: true
ports:
- 8811:8811
secrets:
- mcp_secret
secrets:
mcp_secret:
file: .env
.env
ファイルには、API へのアクセスに必要な GitHub 個人用アクセス トークンが提供されました。
github.personal_access_token=personal_access_token_here
2。AI モデルを選択して追加する
さて、モデルを選ぶ必要がありました。エージェントが3人いたので、理論的には3つの異なるモデルを選ぶことができました。しかし、可能であればモデルの入れ替えを減らしつつ、パフォーマンスをできるだけ速く保ちたいとも考えていました。いくつかの異なるアプローチを試しましたが、次の結果が得られました。
- PRアナライザー – ai/qwen3 – より多くの推論を実行でき、必要なコンテキストを収集するために複数のステップを実行できるモデルが欲しかった
- コメントジェネレーター – ai/gemma3 – Gemma3 モデルはテキスト生成に最適で、非常に高速に実行されます
- PRエグゼキューター – ai/qwen3 – いくつかの実験を実行したところ、コメントを投稿してPRを閉じるために必要な複数のステップでqwenモデルが最適でした
モデルを定義するために、Composeファイルを次の構成で更新しました。Qwen3 モデルにコンテキストサイズを大きくして、ツールの実行、追加の詳細の取得などのためのスペースを増やしました。
models:
gemma3:
model: ai/gemma3
qwen3:
model: ai/qwen3:8B-Q4_0
context_size: 131000
3。アプリケーションを作成する
モデルとツールを選択して構成したら、アプリ自体を記述する時が来ました。小さなDockerfileを作成し、Composeファイルを更新して、環境変数を使用してモデルとMCPゲートウェイを接続しました。また、ファイルの変更をコンテナに同期するための Compose Watch 構成も追加しました。
services:
app:
build:
context: .
target: dev
ports:
- 4111:4111
environment:
MCP_GATEWAY_URL: http://mcp-gateway:8811/sse
depends_on:
- mcp-gateway
models:
qwen3:
endpoint_var: OPENAI_BASE_URL_ANALYZER
model_var: OPENAI_MODEL_ANALYZER
gemma3:
endpoint_var: OPENAI_BASE_URL_COMMENT
model_var: OPENAI_MODEL_COMMENT
develop:
watch:
- path: ./src
action: sync
target: /usr/local/app/src
- path: ./package-lock.json
action: rebuild
Mastraフレームワークにより、エージェントの作成が非常に簡単になりました。次のスニペットは、MCP クライアントを定義し、モデル接続を定義し、定義されたシステムプロンプト (このブログ投稿では省略) を使用してエージェントを作成します。
Compose ファイルで定義されているものと一致する環境変数の使用法に気付くでしょう。これにより、アプリの構成が非常に簡単になります。
import { Agent } from "@mastra/core/agent";
import { MCPClient } from "@mastra/mcp";
import { createOpenAI } from "@ai-sdk/openai";
import { Memory } from "@mastra/memory";
import { LibSQLStore } from "@mastra/libsql";
const SYSTEM_PROMPT = `
You are a bot that will analyze a pull request for a repository and determine if it can be auto-closed or not.
...`;
const mcpGateway = new MCPClient({
servers: {
mcpGateway: {
url: new URL(process.env.MCP_GATEWAY_URL || "http://localhost:8811/sse"),
},
},
});
const openai = createOpenAI({
baseURL: process.env.OPENAI_BASE_URL_ANALYZER || "http://localhost:12434/engines/v1",
apiKey: process.env.OPENAI_API_KEY || "not-set",
});
export const prExecutor = new Agent({
name: 'Pull request analyzer,
instructions: SYSTEM_PROMPT,
model: openai(process.env.OPENAI_MODEL_ANALYZER || "ai/qwen3:8B-Q4_0"),
tools: await mcpGateway.getTools(),
memory: new Memory({
storage: new LibSQLStore({
url: "file:/tmp/mastra.db",
}),
}),
});
エージェントと個別に直接対話できる Mastra Playground に非常に感銘を受けました。これにより、さまざまなプロンプト、メッセージ、モデル設定を簡単にテストできます。うまく機能するプロンプトを見つけたら、その新しいプロンプトを使用するようにコードを更新します。

Mastra Playground は、「プル リクエスト アナライザー」エージェントと直接対話したり、設定を調整したりする機能を示しています。
エージェントを定義すると、すべてのエージェントを接続するステップとワークフローを定義できるようになりました。次のスニペットは、PR を閉じるかどうかを決定した後に発生する、定義されたワークフローと条件付きブランチを示しています。
const prAnalyzerWorkflow = createWorkflow({
id: "prAnalyzerWorkflow",
inputSchema: z.object({
org: z.string().describe("The organization to analyze"),
repo: z.string().describe("The repository to analyze"),
prNumber: z.number().describe("The pull request number to analyze"),
author: z.string().describe("The author of the pull request"),
authorAssociation: z.string().describe("The association of the author with the repository"),
prTitle: z.string().describe("The title of the pull request"),
prDescription: z.string().describe("The description of the pull request"),
}),
outputSchema: z.object({
autoClosed: z.boolean().describe("Whether the PR was auto-closed"),
comment: z.string().describe("Comment to be posted on the PR"),
}),
})
.then(determineAutoClose)
.branch([
[
async ({ inputData }) => inputData.recommendedToClose,
createCommentStep
]
])
.then(prExecuteStep)
.commit();
ワークフローが定義されたら、Webhook サポートを追加できるようになりました。これは単純なハッカソンプロジェクトであり、まだ実際にデプロイする予定はないので(おそらくいつか!smee.io サービスを使用してWebhookをリポジトリに登録し、次にsmee-clientでペイロードを受信し、ペイロードをHTTPエンドポイントに転送しました。
次のスニペットは、smee-client から Webhook を処理し、データを抽出してから Mastra ワークフローを呼び出す小さな Express アプリを作成する簡略版です。
import express from "express";
import SmeeClient from 'smee-client';
import { mastra } from "./mastra";
const app = express();
app.use(express.json());
app.post("/webhook", async (req, res) => {
const payload = JSON.parse(req.body.payload);
if (!payload.pull_request)
return res.status(400).send("Invalid payload");
if (payload.action !== "opened" && payload.action !== "reopened")
return res.status(200).send("Action not relevant, ignoring");
const repoFullName = payload.pull_request.base.repo.full_name;
const initData = {
prNumber: payload.pull_request.number,
org: repoFullName.split("/")[0],
repo: repoFullName.split("/")[1],
author: payload.pull_request.user.login,
authorAssociation: payload.pull_request.author_association,
prTitle: payload.pull_request.title,
prBody: payload.pull_request.body,
};
res.status(200).send("Webhook received");
const workflow = await mastra.getWorkflow("prAnalyzer").createRunAsync();
const result = await workflow.start({ inputData: initData });
console.log("Result:", JSON.stringify(result));
});
const server = app.listen(3000, () => console.log("Server is running on port 3000"));
const smee = new SmeeClient({
source: "https://smee.io/SMEE_ENDPOINT_ID",
target: "http://localhost:3000/webhook",
logger: console,
});
const events = await smee.start();
console.log("Smee client started, listening for events now");
4。アプリをテストする
この時点で、プロジェクト全体を開始し( docker compose up
実行)、PRを開くことができます。Webhookがトリガーされ、ワークフローが実行されるのを確認します。そして、しばらくすると、結果が完成します。うまく行きました!

生成されたコメントでエージェントによって自動的に閉じられた GitHub PR のスクリーンショット。
プロジェクト全体をご覧になりたい場合は、GitHub の mikesir87/hackathon-july-2025でチェックしてください。
教訓
このハッカソンを振り返って、この投稿の要約として共有する価値のあるいくつかのことを学びました。
1。はい、エージェントを使用するとワークフローを自動化できます。
チャットボットを超えることで、自動化の可能性が数多く開かれ、この分野についてもっと考えられることに興奮しています。
2。プロンプトエンジニアリングは依然として難しいです。
モデルが一貫して正しいことをするように導くプロンプトを開発するには 、多くの 反復が必要でした。素早く反復できるツールやフレームワークを使用すると、非常に役立ちます (Mastra Playground に感謝します!
3。Docker のツールを使用すると、多くのモデルを簡単に試すことができました。
私は、ツールの呼び出し、推論、コメントの生成を処理するモデルを見つけるために、かなりの数のモデルを実験しました。私は、まだ機能する可能な限り最小のモデルが欲しかったのです。Compose ファイルの調整、環境変数の更新、新しいモデルの試行は簡単でした。
4。 エージェントにやりすぎる可能性があります。エージェントとプログラマティックの分割ワークフローは強力です。
私は、最終エージェントがコメントを投稿するだけでPRを確実に閉じるプロンプトを書くのに苦労していました。しかし、私は「エージェントはこのステップを実行する必要があるのか?」と疑問に思っていることに気づきました。このステップは、モデルや GPU の使用などを行わずにプログラムで実行できるような感じです。そして、それもはるかに速くなるでしょう。」一部のステップはエージェントを使用し、一部のステップは単にプログラムによるワークフローを構築する方法など、考慮すべきことだと思います(ちなみに、Mastraはこれをサポートしています)。
5。テスティング。
タイミングの関係で、テストの面であまり調べる機会がありませんでした。私の「テスト」はすべて手動検証でした。したがって、これについては将来の反復でループバックしたいと思います。このタイプのワークフローをどのようにテストしますか?エージェントを単独でテストするのか、それともフロー全体でテストするのか?MCPサーバーからの結果をモックしますか?質問がたくさんあります。
まとめ
この社内ハッカソンは、イベント駆動型のエージェントアプリケーションを構築する上で素晴らしい経験でした。起動にチャット インターフェイスを必要としないエージェント アプリケーションについて考えることをお勧めします。イベント駆動型エージェントを使用して、仕事や生活の一部を自動化するにはどうすればよいでしょうか?あなたが何を考えているのかぜひ聞きたいです!
- GitHub でハッカソン プロジェクトを表示する
- Docker Model Runner と MCP Gateway を試す
- Docker Offload ベータ プログラムにサインアップして、エージェントを強化するための 300 分の無料 GPU を取得してください。
- Docker Compose を使用して AI エージェントをビルドして実行する
- Docker MCP カタログでエージェントの信頼できる安全な MCP サーバーを検出する