まとめ

command-a-reasoning-08-2025-nvfp4 を Aider のループに入れて、knowledge/service.go に対するユニットテスト作成を題材にした評価を行った。

結論を先に出しておく。

  • テスト観点の整理と雛形生成まではかなり使える
  • Aider の edit format を守り切れない場面があり、長いセッションでは安定性が落ちる
  • 最終的に token limit に達して止まった
  • 「大きな編集を一気に任せるメインエージェント」ではなく、「観点整理と下書き生成を担当させる補助モデル」として使う方が筋が良い

モデル単体の reasoning 品質と、ツール前提の差分編集ループでの運用安定性は別の評価軸が必要だ、という記録になっている。


評価の背景と動機

このセッションでやりたかったのは、モデルに丸ごと実装を任せることではなかった。むしろ、Aider のように「既存ファイルを読み、変更対象を限定し、編集プロトコルに従って差分を返す」環境で、command-a-reasoning-08-2025 がどの程度実用になるかを見たかった。

対象に選んだのは internal/domain/knowledge/service.gointernal/domain/knowledge/service_test.go だった。前者には ChatRetrieveComposePublishPipelineSubscribePipeline などの機能が含まれており、後者はその検証用テストファイルとして整備したい、という流れだった。

この題材が良いのは、単純な CRUD より評価しやすいことだ。LLM 呼び出し、Vectorstore 呼び出し、Pipeline 呼び出し、stream と non-stream の分岐、goroutine と channel の扱いなど、モデルの設計理解とコード生成の両方が見える。


テスト観点の整理はかなり良かった

ログの冒頭は、いきなりコード生成ではなく、service_test.go 全体で何を検証すべきかを仕様観点で整理するところから始まっている。ここは率直にかなり良かった。

Chat に対しては次の観点を切り分けていた。

  • stream 応答では Answer.Stream != nilAnswer.Text は空、Sources は空
  • non-stream 応答では Answer.TextUsage、検索結果 Sources を確認
  • LLMService.ChatCompletion が 1 回呼ばれる
  • model ごとの backend 切り替え前提を意識している
  • 非 stream 時のみ Vectorstore.Search が呼ばれる
  • LLM error をそのまま返す

この段階で、単なる「テストを書きます」ではなく、どこが契約でどこが境界なのかを分けて見ていた。RetrieveComposePublishPipelineSubscribePipeline、helper 関数まで整理していたので、少なくともレビュー相手としてはかなり話が通じる。

同時に、未検証項目も自分で列挙していた。

  • ChatResult の異常形
  • Choices 空や Text
  • RetrieveQuery 空や TopK=0
  • PublishPipeline の JSON marshal 失敗
  • SubscribePipelinectx cancel

ここは重要で、モデルが「自分が挙げたテストだけで十分」と言い切らず、まだ弱い部分を残件として示せていた。設計レビュー用途ならかなり使える。


実際の service_test.go 生成も悪くなかった

次のフェーズでは、Aider への指示として knowledge/service_test.go のテスト作成が投げられている。モデルは knowledge_test パッケージのテストファイルを提案し、依存サービスのモックを testify/mock で構築していた。

構成は次のようなものだった。

  type mockLLMService struct { mock.Mock }
type mockVectorstoreService struct { mock.Mock }
type mockPipelineService struct { mock.Mock }
  

エントリポイントとして並んでいたテスト関数は次の通り。

  func TestChat(t *testing.T)
func TestRetrieve(t *testing.T)
func TestCompose(t *testing.T)
func TestPublishPipeline(t *testing.T)
func TestSubscribePipelineWithCorrelation(t *testing.T)
func TestSelectAnswerFromResult(t *testing.T)
func TestExtractUserMessage(t *testing.T)
  

この時点では、少なくとも「対象機能を抜かさず一通り押さえる」ことには成功している。

特に TestChat は段階的に改善されていて、最初は stream / non-stream / llm error の基本3ケースだったものが、後の提案では non-stream で user message を使った Vectorstore 検索と Sources 反映まで含めるようになっていた。

  vsSvc.On("Search", mock.Anything, "user message", 3).
    Return([]vectorstore.Hit{{ID: "hit1"}}, nil)
  

これは「回答文が返るか」だけではなく、「non-stream の時だけ retrieval が走る」という実装契約を見ようとしている。実務的にはこの視点があるかどうかでテストの価値がだいぶ違う。


Aider の edit format を守り切れなかった

ここで一番大きく見えた弱点は、生成したコードの中身そのものより、Aider の操作プロトコルに継続して従えなかったことだった。

ログには途中で次のメッセージがそのまま残っている。

  The LLM did not conform to the edit format.
No filename provided before ```` in file listing
  

コードの内容自体はそれなりに筋が通っていても、Aider が期待している「どのファイルにどう適用するか」の出力形式で崩れた。その結果、せっかくの提案コードがそのまま差分として流し込めず、やり直しになっている。

この問題は単にフォーマッタの瑕疵ではない。Aider のような環境では、モデルの価値は「賢い提案をすること」と「編集プロトコルに従って確実に適用可能な差分を返すこと」の積で決まる。前者が良くても後者が崩れると、運用上はかなり厳しい。


ログの中では修正版まで出せている

完全に失敗したわけでもなく、その後の出力では不足していた import や追加ケースを補った修正版まで出している。encoding/json を追加し、SubscribePipelineWithCorrelation を独立テストとして追加し、SelectAnswerFromResultno choices ケースや ExtractUserMessage の空入力まで広げていた。

PublishPipeline では mock.MatchedBy を使って pipeline.TriggerRequest の中身を検証しようとしていた。

  mock.MatchedBy(func(req pipeline.TriggerRequest) bool {
    return req.Name == "test-pipeline" &&
        string(req.Payload) == string(expectedPayload) &&
        req.CorrelationID == "corr-1"
})
  

この辺りを見ると、モデルは単に「とりあえずコンパイルしそうなテスト」を出しているのではなく、依存呼び出しの契約確認まで見ようとしている。提案の方向性はかなり良い。


長いセッションでは崩れていく

問題は、この修正版まで含めて会話が長くなりすぎたことだった。Aider の画面ログ、前回出力の貼り直し、修正版コード、説明文、ファイル一覧、commit message 生成といった情報が積み重なり、最終的には次のエラーで止まっている。

  Model openai/Firworks/command-a-reasoning-08-2025-nvfp4 has hit a token limit!
Input tokens: ~13,597 of 0 -- possibly exhausted context window!
  

この記録はかなり示唆的だった。少なくともこのモデルは、「一発の reasoning」より「長いエージェントセッションの持久力」で見ると別の評価軸が必要になる。最初の数ターンでは十分まともでも、過去の出力を大量に抱えたまま編集ループを回すと、整形品質も追従性も落ちる。


command-a-reasoning-08-2025 の実力評価

ここまでのログを踏まえると、評価はかなり分かりやすい。

強い点

  • テスト対象の仕様整理が上手い
  • モックを使ったユニットテストの骨組みを作れる
  • 改善フィードバックを受けて、2 回目の提案で不足ケースを補える

弱い点

  • edit format を守り切れない場面がある
  • 長いセッションで出力が冗長化しやすい
  • context が膨らむと、適用可能な差分より説明文の比率が上がる
  • 最終的に token limit に達して止まる

モデル単体の reasoning 品質は一定以上ある。ただし、Aider や Codex のような「ツール前提の差分編集」を長く回す用途では、単純な知能評価だけでは足りない。プロトコル順守とセッション継続性を別で見ないと判断を誤る。


実運用で使うならどう切るべきか

このログを踏まえると、次に同じモデルを使うなら依頼の粒度をかなり小さくする。

service_test.go 全体を一気に頼むのではなく、次のように分けた方が良い。

  1. Chat 系だけを追加
  2. RetrieveCompose だけを追加
  3. Pipeline 系だけを追加
  4. helper 関数だけを追加

さらに、Aider セッションに読み込ませるファイルも最小にした方がいい。今回のログでも、同じコードブロックや説明を何度も抱えたまま会話が伸びた結果、最後は context が作業の足を引っ張っていた。

特に次のような運用が効くはずだ。

  • 編集対象ファイルを必要最小限に絞る
  • 一段落ついたら /drop で不要ファイルを外す
  • 履歴が長くなったら /clear して新しいセッションに切る
  • モデルには「説明より diff を優先」と強めに要求する

次にやること

評価軸を 2 つに分けるべきだと思う。

1 つ目は、純粋なコード理解とテスト設計の質。ここはこのログの範囲でも悪くない。2 つ目は、エージェント運用での安定性、つまり edit format 準拠率、無駄な再説明の少なさ、長いセッションでの失速耐性だ。今回の記録では、後者にかなり課題が見えている。

未検証として残っていた項目も、次は短いセッションで個別に詰めたい。

  • ChatResult の nil 組み合わせ
  • SelectAnswerFromResult の choices 空や text 空
  • PublishPipeline の JSON marshal 失敗
  • SubscribePipelinectx cancel

このモデルを完全に切るほどではないが、「大きな編集を一気に任せるメインエージェント」として使うより、「観点整理と下書き生成を担当させる補助モデル」として使う方が、かなり筋が良いというのが今回の結論だ。