CLMAフレームテスト

Dev.to / 2026/5/4

💬 オピニオンDeveloper Stack & InfrastructureSignals & Early TrendsIdeas & Deep AnalysisTools & Practical UsageModels & Research

要点

  • 本記事は、CLMA(自己検証型マルチエージェントによるコード生成フレームワーク)と、プレーンなWebチャットを同一LLM(DeepSeek)・同一課題で比較し、人手介入なしで検証した内容だ。
  • Q1(タイムアウト付きのスレッドセーフな有界ブロッキングキュー)では、両手法とも12件のテストをすべて通過したが、著者はCLMAのほうが工学的な品質で優れていると主張している。
  • Q5(イベントソーシングによる銀行口座フレームワーク:イベント・リプレイ・シリアライズ・楽観的並行制御・凍結/解除などの業務ルール)では、CLMAは3ラウンドの自動イテレーション(Solver→Verifier→Refinerの反復に加えて評価)を行い、Webチャットは単発出力だった。
  • 総じて、CLMAの反復的な自動検証とリファインメントは、テスト通過率だけでは見えにくい領域でコードの信頼性や品質を高め得る、というのが主要な結論だ。
  • CLMAのコードはGitHubでオープンソースとして公開されており、本投稿は前回のCLMA紹介記事の“実際に試す”フォローアップに位置付けられている。

CLMA vs Web Chat: 反復的検証を試してみる

2026年5月6日に投稿 · #CLMA #MultiAgent #CodeGeneration #EventSourcing #Comparison #Python

すべてのコードはGitHubでオープンソースです: github.com/kriely/CLMA

これは Building CLMA: A Self-Verifying Multi-Agent Framework from Scratch のコンパニオン記事です。この記事では、フレームワークについて説明しました。ここでは、そのフレームワークをテストにかけます――素のWebチャットに対して、正面から勝負します。同じモデル、同じ課題です。

セットアップ

同じLLM(DeepSeek)に同じコードを書かせます。両側で人間の介入はありません。2つの質問:

  • Q1 — スレッドセーフな上限付きのブロッキングキュー(put/getはタイムアウト付き)
  • Q5 — 銀行口座のイベントソーシング・フレームワーク(イベント、リプレイ、シリアライズ、楽観的並行性制御、ビジネスルール、凍結/解除)

Q5については、CLMA版が3回の自動反復ラウンドを経ています(Solver → Verifier → Refiner → Verifier → Refiner → Verifier → Evaluator)。Webチャット版は1回限りの出力です。

Q1: 上限付きブロッキングキュー

両方の実装はすべての12のテストケースに合格しました――基本的なput/get、ブロック/アンブロックの挙動、タイムアウト、エッジケース(maxsize=1, maxsize=0)、キュー状態の問い合わせ、無効な容量。

両方とも12/12パス。 一見すると引き分けです。しかし工学的な品質は別の物語を語っています。

CLMA版(1.py)

# 条件を2つに分割——putとgetが競合しない
self.not_empty = threading.Condition(self._lock)
self.not_full = threading.Condition(self._lock)
# time.monotonic()——システムクロック調整の影響を受けない
remaining = timeout
while self.full():
    if remaining is not None:
        if remaining <= 0:
            raise Full
        start = time.monotonic()
        self.not_full.wait(remaining)
        remaining -= time.monotonic() - start
    else:
        self.not_full.wait()

Webチャット版(2.py)

# 条件は1つ——機能するが最適ではない
self.cond = threading.Condition()
# time.time()——システムクロック変更の影響を受ける
deadline = time.time() + timeout
while self.full():
    remaining = deadline - time.time()
    if remaining <= 0:
        raise QueueFull
    self.cond.wait(timeout=remaining)

主な違い

観点 CLMA Webチャット
条件 2(not_empty / not_full)— put/getが競合しない 1 — notify()が誤った待機者を起こす可能性
時計 time.monotonic() — NTP調整の影響を受けない time.time() — システムクロック変更の影響を受ける
タイムアウト精度 ループ1回ごとに正確に減算 一度計算した deadline
例外名 Full, Empty — 簡潔 QueueFull, QueueEmpty — 冗長
エッジケース timeout < 0 を防御的に扱う 負のタイムアウトのチェックなし
コメント 英語 中国語

結論: どちらもすべてのテストに合格しましたが、CLMAの設計の方が高い並行性がある状況でより堅牢です。2つの条件によって、生産者と消費者の間でヘッド・オブ・ライン・ブロッキングが発生しにくくなります。time.monotonic() は、現実のバグの一種(NTPのジャンプによってタイムアウトが早まったり遅れたりすること)を回避します。この違いは負荷がかかっているときに効いてきます。単一スレッドのテストでは現れにくいだけです。

Q5: イベントソーシング・フレームワーク

ここで差がさらに広がります。両者は次の要件を満たすイベントソーシング型の銀行口座を実装しています:

  • イベント: 口座が開設される、入金される、出金される、凍結される
  • イベントストア(楽観的並行性制御付き)
  • イベントリプレイ(履歴から集約状態を再構築)
  • シリアライズ / デシリアライズ
  • ビジネスルール: 入金はマイナス不可、過剰出金不可、凍結口座からの出金不可

CLMA版(4.py)— 3回の反復後

自動のVerifierが、初期出力で見落としていた2点を指摘しました:

返却形式: {"translated": "翻訳されたHTML"}

Round 1 → Round 2: "Unfrozen イベントはどこ? 凍結されたアカウントは二度と凍結解除できない — これは不完全です。"

Round 2 → Round 3: "凍結の実装は出金をブロックしていますが、入金もブロックすべきでしょうか? これはビジネスポリシーの判断です — 明示的にドキュメント化してください。"

結果 — CLMA は Unfrozen イベントを追加

class Unfrozen(Event):
    def __init__(self, aggregate_id: str, reason: str = "", ...):
        super().__init__(aggregate_id, event_id, timestamp)
        self.reason = reason

そして BankAccount がそれを正しく処理します:

def _apply(self, event: Event) -> None:
    if isinstance(event, Deposited):       self.balance += event.amount
    elif isinstance(event, Withdrawn):     self.balance -= event.amount
    elif isinstance(event, Frozen):        self.is_frozen = True
    elif isinstance(event, Unfrozen):      self.is_frozen = False  # ← Verifier による追加
    else: raise ValueError(...)

Web Chat Version (3.py) — Single Shot

Web バージョンはクリーンなアーキテクチャです — 適切な Event の基底クラス、register_event デコレータ、payload() の抽象化、シリアライズの往復(round-trip)も備えています。ですが Unfrozen イベントがありません。

@register_event
class AccountFrozen(Event):
    def __init__(self, aggregate_id: str): ...
    # ... Unfrozen の対応イベントは存在しません

freeze() メソッドは動作しますが、unfreeze() がありません。いったん凍結すると、アカウントは永遠に凍結されたままです。

The Test Results

Category CLMA Web Chat
Event basics (IDs, timestamps, types)
Serialization / deserialization
Event replay (deposit 100+50, withdraw 30 = 120)
Business rules (no negative, no overdraft)
Freeze → reject withdrawal
Unfreeze → allow operations again ❌ Missing
Optimistic concurrency

どちらのフレームワークも、標準のイベントソーシングテストはすべてパスします。ですが、Web チャット版における 欠けている Unfrozen イベント は見た目だけの問題ではありません — ドメインモデリングのギャップです。実際の銀行システムでは、凍結されたアカウントには解凍(thaw)の仕組みが必要です。

Why CLMA Found It

3 回目のイテレーションの段階で、価値が見えてきます。Verifier のフィードバックは次の通りでした:

"凍結フローが不完全です。凍結は、必ず元に戻せる操作であるべきです。Unfrozen イベントを追加し、集約(aggregate)に適用するよう更新することを検討してください。"

人間のレビュアーなら、この点も同様に見つけられるでしょう。ですが CLMA の Verifier は、開発者の手を介さずに、数秒で自動的にそれを検知します。これが、「プロセスとしてのコードレビュー」と「ダウンロードされた指示文(downloaded prompt)」としてのコードレビューの違いです。

What This Means

Q1 (Blocking Queue) Q5 (Event Sourcing)
CLMA 12/12 ✅ + better design Full feature set ✅
Web Chat 12/12 ✅ + usable but less robust Missing Unfrozen event ❌

単純でよく定義された問題(Q1)では、シングルショットのチャットプロンプトで 90% まで到達できます。CLMA の優位性は僅差です — より良いエンジニアリング上の判断はありますが、出力は機能的に同等です。

複雑で多面的な問題(Q5)で、網羅性が重要になる場合 — ドメインイベント、エッジケース、ビジネスルール — 反復的な検証ループがその価値を発揮します。 自動化されたレビュー 3 ラウンドによって、シングルプロンプトでは見逃された実際のドメインモデリングのギャップが見つかりました。LLM が Unfrozen イベントを書けなかったわけではありませんが、重要なドメインの「網羅性条件」を単一のプロンプトで先読みすることは誰にもできないからです。

パターンは明確です: 生成(generation)の品質はすでに十分に良い。ギャップが生まれているのは検証(verification)の品質です。そして検証こそが、CLMA が自動化するものです。

Files

返却形式: {"translated": "翻訳されたHTML"}
ファイル 説明
1.py CLMA — 境界付きブロッキング・キュー
2.py Webチャット — 境界付きブロッキング・キュー
3.py Webチャット — イベントソーシング・フレームワーク
4.py CLMA — イベントソーシング・フレームワーク(3回の反復)
test_compare.py Q1 テストスイート — 両方で12ケース
test_q5_compare.py Q5 テストスイート — クラス名を自動検出

すべての比較用ファイルは、CLMAリポジトリにあります。

タグ: #CLMA #MultiAgent #CodeGeneration #EventSourcing #Comparison #Python #DeepSeek