VS Code + Cline で ”Devin 風”開発を行う方法③ - マルチエージェント環境の構築編

前回は、VS Code と Cline 拡張機能を使って、いわゆる「Devin 風」の開発環境を構築し、AI エージェントと一緒に既存コードのリファクタリングを進めました。

その中で見えてきたのは、「AI に任せる」というよりも、「人間がタスクを分解し、AI に段階的に作業させる」ことの重要性です。

では、そのタスク分解をさらに一歩進めて、Devin のように複数の AI エージェントで役割分担させることはできるのでしょうか?

今回からは、Cline の Kanban 機能を使ってタスクを管理しながら、AI エージェントを“チーム”のように動かす、いわゆるマルチエージェント的な開発スタイルを検証していきます。

今回の記事を作成するにあたり、以下の講座で勉強させていただきました。コードを 1 行も書かずに、リアルタイムチャートと AI アシスタンス機能付きの株取引アプリ (トレーディング・ワークステーション) を構築されています。英語の講座ですが、Udemy の「トランスクリプション」という機能を使用すると講義内容がテキスト形式で右側に表示されるので、Chrome などの「翻訳機能」を使用すると日本語で表示できます。Microsoft Edge ブラウザーを使用すればビデオ内の字幕を直接日本語に変換できるようです。

Udemy

AI Coder: Complete Claude Code & Coding Agents Course

この記事で使用している実行環境は以下のようになっています。

  • VS Code (Windows 版)・・・バージョン 1.118.1
  • Node.js (Windows 版)・・・バージョン 24.15.0
  • Cline・・・バージョン 2.18.0
  • Kanban・・・バージョン 0.1.63

※本記事でご紹介している内容は、確立された手法ではなく著者の独自の解釈に基づいています。その点をご理解のうえご参照ください。

動作環境について

本記事では LLM の実行環境として、「Ollama」を使用したローカル環境で実行しています。Cline の こちら の公式ページを参考にしてインストールします。

公式ページでは「qwen2.5-coder:32b」というモデルが推奨されていますが、このモデルが快適に動作する GPU ボードは非常に高価なため、本記事では「gpt-oss:20b」というモデルを使用しています。このモデルであれば 16 GB の VRAM でも 量子化CPU オフロード 機能のおかげでそれほどストレスなく動作します。Windows や VS Code のターミナル画面を開いて、以下のように入力するとモデルがインストールされます。

ollama pull gpt-oss:20b

今回もコストを抑えるためにローカル LLM を使用していますが、精度を重視する場合はクラウドモデルの方が安定します。

Ollama のインストーラーは こちら からダウンロードできます。デフォルト設定でインストールしてください。起動前に Windows のターミナル画面などで以下のコマンドを実行しておくと、GPU の専用メモリ (VRAM) が効率的に利用できます。

[Environment]::SetEnvironmentVariable("OLLAMA_MAX_LOADED_MODELS","1","User")
[Environment]::SetEnvironmentVariable("OLLAMA_NUM_PARALLEL","1","User")
[Environment]::SetEnvironmentVariable("OLLAMA_KEEP_ALIVE","30m","User")
Kanban をインストールします

VS Code のターミナル画面で、以下のように入力して Kanban (Cline) をインストールします。

npm install -g cline

インストールが完了したら、プロジェクト (コードベース) を VS Code で開いてターミナル画面に「cline」と入力すると Kanban が起動します。プロジェクトを Kanban で操作するには、git リポジトリが初期化されていて、初回のコミットが終了している必要があります。

Kanban が起動したら、右上の設定ボタンを押します。

Ollama をローカルエージェントとして使用するには、以下のように設定します。

  1. 左側のメニューから「Cline」を選択します。
  2. API provider から「Ollama」を選択します。
  3. API key は任意の文字列 (local 等) を入力します。
  4. Base URL は Ollama が起動している PC のアドレスを入力します。
    同じ PC 上で動作している場合は画像に示されている設定にします。
  5. Model ID から「gpt-oss:20b」を選択します。
  6. Reasoning effort から任意の値を選択します。
    複雑な処理を行う場合は「High」などを選択します。

設定が終了したら、右下の「Save」ボタンで設定を保存します。

設定が完了したら、動作テストを行います。「Kanban Agent」タブを選択して、左下の入力欄に「こんにちは」などと入力してエンターキーを押します。何か応答が返ってくれば正常に動作しています。(Kanban を安定動作させるには パラメータ数 が 20B 程度以上の LLM モデルが必要なようです。LiteLLM を使用すれば「gemma3:12b」などのモデルも接続はできますが、上手く動作しないようです。)

Kanban の動作確認を行います

「Backlog」レーンにある「Create task」をクリックすると手動でタスクを作成できますが、マルチエージェントで作業ループを回すには、レビューなどを担当する AI エージェントが新しいタスクを自動的に作成できる必要があります。

そこで Kanban Agent に以下のようなプロンプトを入力して実際にタスクが作成できるか確認してみます。

デフォルト状態では自動で作成してくれないようです。PowerShell を使用して手動で作成するように提案されます。

提案されたコマンドは非常に長く、実行プログラム (node.exe) のパスにスペースが含まれるなど誤動作を引き起こしそうなので、いろいろ調べてみると以下のようなコマンドでタスクを作成できるようです。

npx kanban task create --title "test" --prompt "test"

確認のため Kanban 画面の右上にあるターミナルボタン (>_) を押して、表示されたターミナル画面に上記のコマンドを入力するとタスクが作成されました。

同様のコマンドを生成するようなルールを作成してみましたが、処理が完了しなかったり、同じタスクがいくつも作成されるようなケースがありました。

詳しく調べてみると、タスクの生成は成功しているのに、以下のようなエラーが発生しているために何度かリトライしているようです。このエラーは Node.js で使用している libuv という中核的なライブラリの async.c というファイル内で発生しています。終了処理中のハンドルに再度アクセスなどを行うとアサーションエラーが発生するようです。Windows 固有の現象らしく、Linux や macOS では報告されていないようです。

Command failed: Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 76

標準エラー出力を出さないようにしても、終了コードなどもチェックするので、完了したとは認識してくれないようです。

そこで本記事では、PowerShell -File 経由で npx kanban task create を呼び出す方式に変更して、さらに .clinerules を使ってタスク生成ルールを固定しました。

まず「.clinerules\scripts\kanban-create-task.ps1」というファイルを作成して、タスク生成はこの PowerShell スクリプトに一本化します。

param(
  [Parameter(ValueFromRemainingArguments = $true)]
  [string[]]$RemainingArgs
)

$KanbanArgs = @(
  "kanban"
  "task"
  "create"
)
$KanbanArgs += $RemainingArgs

$Output = npx @KanbanArgs 2>$null

$Text = $Output | Out-String
Write-Output $Text

if ($Text -match '"ok"\s*:\s*true' -and $Text -match '"id"\s*:\s*"[^"]+"') {
  exit 0
}

exit 1

上記スクリプトの 13 行目で標準エラー出力を廃棄していますが、タスク生成が成功すると以下のような JSON 形式の文字列が返ってきますので、「ok」項目と「task.id」項目を見て成功の可否を判定しています。

{
  "ok": true,
  "task": {
    "id": "d13c5",
    "column": "backlog",
    "workspacePath": "D:\\MAMP\\htdocs\\cline\\YelpCampCleanArch",
    "title": "test",
    "prompt": "test",
    "baseRef": "main",
    "startInPlanMode": false,
    "autoReviewEnabled": false,
    "autoReviewMode": "commit"
  }
}

次に「.clinerules\task-create-rules.md」というルールファイルを作成して、スクリプト経由以外でのタスク作成を禁止します。

# High Priority Rules - MUST FOLLOW

## kanban task create ルール
- `command` には必ず `"powershell"` を指定すること。
- 直接 `node` や `npx` コマンドを `command` に指定してはいけない。
- タスク作成は `.clinerules\scripts\kanban-create-task.ps1` 経由のみ許可する。
- run_commands は以下の形式で実行する
{
  "command": "powershell",
  "args": [
    "-NoProfile", "-ExecutionPolicy", "Bypass", "-File",
    ".clinerules\scripts\kanban-create-task.ps1", `<options>`]
}
- `<options>` は PowerShell パラメータ形式でそのまま args 配列へ追加すること。

さらにルールファイルが読み込まれやすくなるように、「.clinerules\AGENTS.md」というファイルを作成して以下の内容を書き込みます。

# High Priority Rules - MUST FOLLOW

## 必須ルール読込
- kanban タスクを作成する前に以下のルールを必ず読み込む:
  `.clinerules\task-create-rules.md`

Kanban Agent から先ほどと同じ要領でタスクを作成するとうまく動作したようです。(必ずしもこのような結果になるとは限らないようです。「”.clinerules\task-create-rules.md” ファイルを読み込んでルールに従ってください。」 などとあらかじめ指定しておくと動作が安定するようです。)

ローカルモデルのため推論が弱いですが、このようにスクリプトに誘導してあげればかなり複雑な処理も安定して実行できそうです。

※(2026/5/10 追記)

Kanban の task コマンドには以下のサブコマンドがあるようです。取得系のコマンドは libuv 関連のエラーは出ませんが、作成・更新系のコマンドではやはり同様のエラーが出るようなので、ルールとスクリプトを置き換えます。

PS D:\MAMP\htdocs\cline\YelpCampCleanArch> kanban task --help
Usage: kanban task|tasks [options] [command]

Manage Kanban board tasks from the CLI.

Options:
  -h, --help        display help for command

Commands:
  list [options]    List Kanban tasks for a workspace.
  create [options]  Create a task in backlog.
  update [options]  Update an existing task.
  trash [options]   Move a task or an entire column to trash and clean up task workspaces.
  delete [options]  Permanently delete a task or every task in a column.
  link [options]    Link two tasks so one task waits on another.
  unlink [options]  Remove an existing dependency link.
  start [options]   Start a task session and move task to in_progress.
  help [command]    display help for command

「.clinerules\kanban-task-rules.md」というルールを新しく作成して、以下の内容を書き込みます。

# High Priority Rules - MUST FOLLOW

## kanban task コマンドルール
- `command` には必ず `"powershell"` を指定すること。
- `kanban task ...` / `kanban tasks ...` / `npx kanban task ...` / `node ... kanban ...` を直接実行してはいけない。
- Kanban task 系コマンドはすべて `.clinerules\scripts\kanban-task.ps1` 経由のみ許可する。

## 対象サブコマンド:
- list
- create
- update
- trash
- delete
- link
- unlink
- start
- help

## run_commands の形式
run_commands は必ず以下の形式で実行する:
{
  "command": "powershell",
  "args": [
    "-NoProfile",
    "-ExecutionPolicy",
    "Bypass",
    "-File",
    ".clinerules\\scripts\\kanban-task.ps1",
    "<subcommand>",
    "<options>"
  ]
}

## 実行例
### list
{
  "command": "powershell",
  "args": [
    "-NoProfile",
    "-ExecutionPolicy",
    "Bypass",
    "-File",
    ".clinerules\\scripts\\kanban-task.ps1",
    "list"
  ]
}

### create
{
  "command": "powershell",
  "args": [
    "-NoProfile",
    "-ExecutionPolicy",
    "Bypass",
    "-File",
    ".clinerules\\scripts\\kanban-task.ps1",
    "create",
    "--title",
    "test",
    "--prompt",
    "test"
  ]
}

### update
{
  "command": "powershell",
  "args": [
    "-NoProfile",
    "-ExecutionPolicy",
    "Bypass",
    "-File",
    ".clinerules\\scripts\\kanban-task.ps1",
    "update",
    "--task-id",
    "abc12",
    "--title",
    "new title"
  ]
}

### delete
{
  "command": "powershell",
  "args": [
    "-NoProfile",
    "-ExecutionPolicy",
    "Bypass",
    "-File",
    ".clinerules\\scripts\\kanban-task.ps1",
    "delete",
    "--task-id",
    "abc12"
  ]
}

## オプションの渡し方
- `<subcommand>` は必ず最初の引数として渡すこと。
- `<options>` は Kanban CLI のオプションをそのまま args 配列へ追加すること。
- `-Title` / `-Prompt` のような PowerShell 独自パラメータ形式ではなく、Kanban CLI と同じ `--title` / `--prompt` 形式を使うこと。

## 成功判定
- 成功判定は `.clinerules\scripts\kanban-task.ps1` に委譲する。
- エージェントは `ok: true` / `task.id` / exit code を独自に再解釈しない。
- スクリプトが成功終了した場合は、その時点で処理を完了する。
- スクリプトが「既存タスクあり」「削除済み」「更新済み」などを成功として返した場合も、再実行してはならない。

## リトライ禁止
- `kanban task create` / `update` / `trash` / `delete` / `link` / `unlink` / `start` は副作用のある操作なので、同じコマンドをリトライしてはならない。
- 確認が必要な場合は、同じ副作用コマンドを再実行せず、`list` を使用すること。
- assertion エラーや stderr 出力を理由に同じ副作用コマンドを再実行してはならない。

## 禁止例
禁止:
{
  "command": "kanban"
}
{
  "command": "npx"
}
{
  "command": "node"
}
{
  "command": "powershell",
  "args": [
    "-NoProfile",
    "-ExecutionPolicy",
    "Bypass",
    "-Command",
    "kanban task create --title 'test' --prompt 'test'"
  ]
}

「.clinerules\scripts\kanban-task.ps1」というスクリプトファイルを作成して、以下の内容を書き込みます。

param(
    [Parameter(ValueFromRemainingArguments = $true)]
    [string[]]$RemainingArgs
)

#
# 日本語文字化け対策 (入出力を UTF-8 に統一)
#
chcp 65001 > $null
[Console]::InputEncoding  = [System.Text.Encoding]::UTF8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8

$LogDir = ".clinerules\logs"
$LogPath = Join-Path $LogDir "kanban-task.log"

New-Item `
    -ItemType Directory `
    -Force `
    -Path $LogDir | Out-Null

function Write-Log {
    param([string]$Message)

    Add-Content `
        -Path $LogPath `
        -Encoding UTF8 `
        -Value $Message
}

Write-Log "===================="
Write-Log ("Time: " + (Get-Date -Format "yyyy-MM-dd HH:mm:ss"))

#
# kanban task <subcommand> ...
#
$KanbanArgs = @(
    "kanban"
    "task"
)

$KanbanArgs += $RemainingArgs

Write-Log ("Command: npx " + ($KanbanArgs -join " "))

#
# libuv assertion を捨てる
#
$Output = npx @KanbanArgs 2>$null

$Text = $Output | Out-String

Write-Log "Output:"
Write-Log $Text

#
# stdout をそのまま返す
#
Write-Output $Text

#
# JSON success 判定
#
if ([string]::IsNullOrWhiteSpace($Text)) {
    Write-Log "Result: failed (empty output)"
    exit 1
}

#
# "ok": true を探す
#
if ($Text -match '"ok"\s*:\s*true') {
    Write-Log "Result: success"
    exit 0
}

Write-Log "Result: failed"
exit 1

「.clinerules\AGENTS.md」ファイルも以下のように変更します。

# High Priority Rules - MUST FOLLOW

## 必須ルール読込
- kanban タスクを作成する前に以下のルールを必ず読み込む:
  `.clinerules\kanban-task-rules.md`

以下のファイルは不要なので削除します。

  • .clinerules\task-create-rules.md
  • .clinerules\scripts\kanban-create-task.ps1

以上で修正は完了です。Kanban (Cline) がスクリプトファイルを使用すると「.clinerules\logs\kanban-task.log」というログファイルに以下のような内容が記録されますので、デバッグ用途などに利用可能です。

====================
Time: 2026-05-09 19:05:45
Command: npx kanban task create --title test1 --prompt test1
Output:
{
  "ok": true,
  "task": {
    "id": "b25d8",
    "column": "backlog",
    "workspacePath": "D:\\MAMP\\htdocs\\cline\\YelpCampCleanArch",
    "title": "test1",
    "prompt": "test1",
    "baseRef": "main",
    "startInPlanMode": false,
    "autoReviewEnabled": false,
    "autoReviewMode": "commit"
  }
}

Result: success
====================
Time: 2026-05-09 19:07:01
Command: npx kanban task create --title test2 --prompt test2
Output:
{
  "ok": true,
  "task": {
    "id": "9fcab",
    "column": "backlog",
    "workspacePath": "D:\\MAMP\\htdocs\\cline\\YelpCampCleanArch",
    "title": "test2",
    "prompt": "test2",
    "baseRef": "main",
    "startInPlanMode": false,
    "autoReviewEnabled": false,
    "autoReviewMode": "commit"
  }
}

Result: success
====================
Time: 2026-05-09 19:08:00
Command: npx kanban task link --task-id b25d8 --linked-task-id 9fcab
Output:
{
  "ok": true,
  "workspacePath": "D:\\MAMP\\htdocs\\cline\\YelpCampCleanArch",
  "dependency": {
    "id": "dc9c8e0d",
    "backlogTaskId": "b25d8",
    "backlogTaskColumn": "backlog",
    "linkedTaskId": "9fcab",
    "linkedTaskColumn": "backlog",
    "createdAt": 1778321283439
  }
}

Result: success
====================
Time: 2026-05-09 19:08:49
Command: npx kanban task list
Output:
{
  "ok": true,
  "workspacePath": "D:\\MAMP\\htdocs\\cline\\YelpCampCleanArch",
  "column": null,
  "tasks": [
    {
      "id": "9fcab",
      "prompt": "test2",
      "column": "backlog",
      "baseRef": "main",
      "startInPlanMode": false,
      "autoReviewEnabled": false,
      "autoReviewMode": "commit",
      "createdAt": 1778321224769,
      "updatedAt": 1778321224769,
      "session": null
    },
    {
      "id": "b25d8",
      "prompt": "test1",
      "column": "backlog",
      "baseRef": "main",
      "startInPlanMode": false,
      "autoReviewEnabled": false,
      "autoReviewMode": "commit",
      "createdAt": 1778321148370,
      "updatedAt": 1778321148370,
      "session": null
    }
  ],
  "dependencies": [
    {
      "id": "dc9c8e0d",
      "backlogTaskId": "b25d8",
      "backlogTaskColumn": "backlog",
      "linkedTaskId": "9fcab",
      "linkedTaskColumn": "backlog",
      "createdAt": 1778321283439
    }
  ],
  "count": 2
}

Result: success

想定より設定に時間がかかってしまったため、今回はここまでを公開します。
次回は、実際に複数の AI エージェントに役割を分担させ、タスクループを回す構成を検証する予定です。