アーカイブへ戻る

システム境界:ChatBot、Workflow、Agent、Harness の違い

多くの人が初めて Agent システムを作るとき、自然にそれを一つのアップグレード経路として理解しがちです。

システム境界:ChatBot、Workflow、Agent、Harness の違い

多くの人が初めて Agent システムを作るとき、自然にそれを一つのアップグレード経路として理解しがちです。

ChatBot は単純すぎる
-> Workflow はよりエンジニアリングされている
-> Agent はより賢い
-> Harness はより高度である

この線は分かりやすいですが、エンジニアリング判断を誤らせます。

Agent は、より高度なデフォルト選択肢ではないからです。

実プロジェクトでは、多くの問題は ChatBot で十分です。多くの自動化は Workflow の方がむしろ安定します。タスク経路を事前に固定できず、実行中に新しい証拠を見ながら次の一手を判断する必要があるときにだけ、Agent を導入する価値があります。Harness も「さらに一層かっこいいアーキテクチャを包む」ものではありません。Agent が実環境に入ったあと、安定したホスティング、権限制御、状態管理、ログ追跡、復旧、検証、ガバナンスのために、やむを得ず育つモデル外部の制御システムです。

前の二篇と同じ例を使い続けます。

このプロジェクトのテストがなぜ失敗しているか見て、直してください。

この一文は Agent タスクのように見えますが、完全に異なる四つのシステム形態へ分解できます。

ユーザーがただ次のように聞いているだけなら:

Jest の Cannot find module は通常どんな原因ですか?

これは ChatBot の質問応答かもしれません。

チームがすでに調査手順を決めているなら:

コードを pull -> 依存関係をインストール -> テストを実行 -> ログを収集 -> Slack へ送る

これは Workflow に近いです。

システムが失敗原因の場所を知らず、どのファイルを先に読むか、どのコマンドを走らせるか、どこを変更するか、その後どう検証するかを自分で決める必要があるなら、そこで初めて Agent の範囲に入ります。

さらにこの Agent をチームのために毎日自動実行し、中断から復旧でき、権限を制限し、監査を記録し、成功率を集計し、失敗セッションをリプレイできるようにするなら、Harness が必要になり始めます。

したがってこの記事が答えるのは「どの概念がより高度か」ではありません。

いつ ChatBot を使うべきか、いつ Workflow を使うべきか、いつ Agent が必要になり、いつ Harness を構築しなければならないのか?

先に総論を一文で置きます。

ChatBot は対話問題を解き、Workflow は確定した手順の問題を解き、Agent は動的判断の問題を解き、Harness は安定ホスティングの問題を解きます。

この四者は同じ直線上の豪華版ではありません。異なる不確実性と異なるリスク境界に向き合うためのエンジニアリング選択です。実際のプロダクトでは組み合わせて現れることもあります。一つのシステムに ChatBot 入口、Workflow パイプライン、Agent loop、そして徐々に厚くなる Harness が同時に存在することがあります。

問題の連鎖

不確実性がどこにあるかで ChatBot、Workflow、Agent、Harness の境界を判断する

この記事の問題の連鎖は次の通りです。

ユーザーが必要としているのが理解と表現だけである
-> ChatBot で対話を管理すれば十分
-> タスク手順がすでに確定しており、自動実行だけが必要
-> Workflow の方が信頼でき、安く、テストしやすい
-> タスク経路が不確定で、実行中に次の一手を判断する必要がある
-> そこで初めて Agent を導入し、モデルを動的判断に参加させる
-> Agent が実環境に触れると、ツール、権限、状態、副作用、検証の問題が生まれる
-> モデルが提案したアクションを、そのままシステム実行と同一視してはいけない
-> モデル外部のエンジニアリング制御を受け止める Harness が必要になる

ここで最も重要な判断は次です。

Agent は、より高度なデフォルト選択肢ではない。
Agent は、不確定なタスクのために支払う複雑性コストである。

不確実性がなければ、Agent はしばしば不要な揺らぎをシステムへ持ち込みます。対話だけが必要なのに Agent にすると、システムはテストしにくく、制御しにくく、説明しにくくなります。手順が完全に確定しているのに Agent に毎ステップ判断させるのは、本来は書き固められた信頼性を、モデル出力の不安定さと交換するようなものです。

まず一枚の図で四つの境界を固定します。

システム境界:ChatBot、Workflow、Agent、Harness の違い Mermaid 1

この図で最も重要なのは四つの名詞ではなく、中央の判断問題です。不確実性はどこにあるか?

不確実性が「ユーザーがどう聞くか、モデルがどう答えるか」だけにあるなら、ChatBot で十分です。不確実性がすでに人によってフローチャートへ消化されているなら、Workflow が適しています。不確実性を実行時に新しい証拠に基づいて判断しなければならないなら、Agent に価値があります。Agent が一回限りのスクリプトではなく、安定稼働、復旧可能性、監査、検証を必要とするなら、Harness は避けられません。

注意してください。Harness は ChatBot、Workflow、Agent と完全に同類の「第四のより高度な形態」ではありません。より正確には、ChatBot、Workflow、Agent はタスク処理形態であり、Harness は Agent の動的プロセスが実環境へ入ったあとに、実行境界と制御面を提供するエンジニアリングシステムです。

一、ChatBot:問題が主に対話の中で起きるとき

まず ChatBot から始めます。

ChatBot は最も過小評価されやすい層です。多くの開発者は ChatBot と聞くと「会話できるだけ」で、エンジニアリングされていないと感じます。しかしユーザーの本当の需要が理解、説明、要約、書き換え、質問応答なら、ChatBot は最も軽く、安定し、安いシステム形態です。

たとえばユーザーがこう聞く場合:

この Cannot find module エラーは何を意味しますか?

または:

package.json の scripts フィールドを説明してください。

この種のタスクの核心は「アクションを実行する」ことではなく、「既存情報を分かりやすく説明する」ことです。システムが通常行うのは次の通りです。

ユーザー入力を受け取る
-> メッセージ履歴を組み立てる
-> モデルを呼ぶ
-> 自然言語の回答を返す

製品機能をいくらか加えるとしても、会話管理、参照元、整形出力、ユーザー嗜好が増える程度です。それでもまだ「自律的にタスクを実行する」範囲には入りません。

最小 ChatBot は次のように書けます。

type ChatMessage = {
  role: "system" | "user" | "assistant";
  content: string;
};

async function chat(messages: ChatMessage[]) {
  return model.generate({
    messages,
    temperature: 0.3,
  });
}

このコードには loop も、tool runtime も、状態機械も、権限システムもありません。これは欠陥ではなく、境界が明確だということです。

ChatBot のエンジニアリング上の重点は次のような点です。

  • メッセージ履歴をどう剪定するか
  • system prompt をどう安定させるか
  • 回答形式をどう制約するか
  • 複数ターンの会話で文脈をどう保つか
  • ユーザー入力をどう安全にフィルタするか
  • モデル失敗時にどうフォールバックするか

これらで問題を解けるなら、急いで Agent へアップグレードしないでください。

Agent を導入した瞬間、もともと存在しなかったコストが増えるからです。モデルは誤ったツールを選ぶかもしれません。ツールは実行に失敗するかもしれません。状態が次の判断を汚染するかもしれません。権限は明示的にガバナンスする必要があり、結果は検証される必要があり、各ステップでリトライとリプレイを考えなければなりません。

CLI アシスタントへ戻りましょう。

ユーザーがただ次のように聞くなら:

このテストエラーは通常どう調査しますか?

ChatBot は調査の考え方を出せます。ユーザーが貼り付けたログに基づいて説明することもできます。しかしプロジェクトを確認したふりをしたり、「修正しました」と言ったりすべきではありません。

ファイルを読んでおらず、コードを変更しておらず、テストを実行していないからです。

ChatBot の誠実な境界は次の通りです。

あなたが提供した情報に基づいて推論できます。
しかし外部環境を能動的に観測してはいません。

「Agent っぽく見えるが信頼できない」システムの多くは、ChatBot 段階で境界を越えています。ツールも Runtime 状態もないのに、言語表現だけはすでに行動を完了したように見せます。ユーザーはシステムが作業していると思いますが、システムは説明を生成しているだけです。

したがって ChatBot の第一のエンジニアリング規律は次です。

生成された行動説明を、すでに起きた行動として扱わないこと。

システムが会話しかできないなら、会話システムとしてきちんと作りましょう。行動するなら、次の境界へ進む必要があります。

二、Workflow:手順が確定しているなら、モデルにフローを再発明させない

Workflow が解くのは「モデルが考えられるか」ではなく、「人がすでに知っている手順を、システムが安定して実行できるか」です。

たとえばチームが毎日 CI 失敗のサマリを作る必要があるとします。

最新コードを取得する
-> 依存関係をインストールする
-> テストを実行する
-> 失敗ログを収集する
-> レポートを生成する
-> Slack へ送る

この経路では、モデルが毎ステップ判断し直す必要はありません。必要なのは、手順の順序が安定していること、失敗時にリトライできること、タイムアウトで中断できること、各ステップにログがあること、結果を追跡できること、同じ入力からできる限り同じ出力が得られることです。

これが Workflow の領域です。

この CLI Agent チュートリアルの中で、仮に「テストを自動修正する」ことはまだせず、固定の調査フローだけを作るとします。

1. npm test を実行する
2. 失敗したらログを保存する
3. 失敗したテスト名を検索する
4. 関連ファイルパスを出力する
5. モデルにログから考えられる原因を要約させる

これは Workflow です。モデルは最後の要約だけに参加し、フロー決定には参加していません。

疑似コードはおおよそ次のようになります。

async function diagnoseTestFailureWorkflow(repo: Repo) {
  await repo.install();

  const testResult = await repo.run("npm test");

  if (testResult.ok) {
    return { status: "passed" };
  }

  const symbols = extractFailedSymbols(testResult.output);
  const files = await repo.search(symbols);

  const summary = await model.generate({
    messages: [
      system("你是测试失败分析助手。"),
      user(renderFailureContext(testResult.output, files)),
    ],
  });

  return {
    status: "failed",
    log: testResult.output,
    relatedFiles: files,
    summary,
  };
}

これは Agent ではありません。

次に何をするかをモデルが決めていないからです。フローの作者が経路をプログラムへ書き込んでおり、モデルはあるノード上の能力にすぎません。

このとき無理に Agent にすると、かえって問題が起きます。

たとえばモデルに毎ラウンドこう判断させるとします。

次にテストを実行するべきか?
ファイルを検索するべきか?
レポートを生成するべきか?
Slack に通知するべきか?

これらの判断には本来、動的判断は必要ありません。Workflow として書いた方が明確で、安く、テストしやすくなります。

Workflow の強みは、まさに「モデルへ渡す自由度を少なくする」ことにあります。この言い方はあまり派手ではありませんが、とてもエンジニアリング的です。確定したフローにおける自由度は、通常は能力ではなくリスクです。毎回同じようにやるべきことなら、それをフローとして書き、モデルに毎回その場で判断させないでください。

Workflow の典型的な実装形態には次があります。

  • CI/CD pipeline
  • 定期タスク
  • データ処理パイプライン
  • 承認フロー
  • アラート配信
  • 固定レポート生成
  • 複数システム同期

これらのシステムは LLM を呼び出せますが、それだけで Agent になるわけではありません。「LLM ノードを含む Workflow」は、依然として Workflow です。

重要な判断は次です。

誰が次の一手を決めているか?

次の一手をフローチャートが決めているなら、それは Workflow です。モデルが現在の現場に基づいて動的に決めているなら、そこで初めて Agent に近づきます。

図で見ると、より直感的です。

システム境界:ChatBot、Workflow、Agent、Harness の違い Mermaid 2

この図では、LLM が原因を要約する は一つのノードにすぎません。フローを引き継いではいません。システムを本当に前へ進めているのは Workflow 自身です。

これが Workflow と Agent の第一の分界線です。

LLM がフローの中に現れることは、フローが Agent になったことを意味しない。
LLM がフローの次の一手を決めるときにだけ、システムは Agent 境界に入る。

Workflow が欠けるとどうなるでしょうか。

多くのチームは固定自動化まで Agent に任せます。その結果、手順が確定しているのに毎回経路が違い、本来は速く失敗すべきところでモデルが回り道をし、単体テストで検証できるものを人の観察に頼ることになり、固定権限が開いた行動空間として包装されます。

この種のシステムは短期 demo では「賢く」見えますが、長期保守では「制御不能なスクリプト」のようになります。

したがって Workflow の第一のエンジニアリング規律は次です。

フローを確定できるなら、Agent に判断させるより先にフローとして書くこと。

三、Agent:タスク経路を実行中に決めなければならないとき

ここから Agent に入ります。

Agent が現れる理由は「システムをより人間らしくしたい」からではなく、「一部のタスクはフローを事前に固定できない」からです。

例のタスクに戻ります。

このプロジェクトのテストがなぜ失敗しているか見て、直してください。

この作業の経路は事前に固定しにくいものです。

テスト失敗は、依存関係バージョンの不一致、環境変数の欠落、テストスナップショットの古さ、型定義エラー、業務ロジックの回帰、mock 設定の不備、ビルドスクリプト変更、ファイルパスの大文字小文字問題など、多くの原因から起きます。

すべての状況を分岐で覆う大きな Workflow を書くことはできます。しかしすぐに保守困難な決定木へ膨らみます。新しいエラーが一種類増えるたび、新しい分岐を足さなければなりません。各分岐は、次にどのファイルを読むか、どのコマンドを走らせるか、結果をどう判断するかも知る必要があります。

ここで初めて Agent の価値が現れます。

各ラウンドで、現在の現場に基づいてモデルに次のアクションを選ばせる。

Agent の最小実行方式は次です。

現在の状態を見る
-> 次の一手を判断する
-> ツール意図を提出する
-> システムが制御された形で実行する
-> 結果を状態へ書き戻す
-> 判断を続ける

ここでの重要な変化は次です。

制御権の一部が、固定フローからモデルへ移る。

ただし、一部だけです。

モデルが現実世界の制御権を直接持つべきではありません。モデルは行動意図、つまり tool intent / action proposal を提出するだけです。外側の runtime が、その意図を実行できるか、どう実行するか、実行後にどう記録するかを決めます。

最小 Agent loop は次のように書けます。

async function runAgent(task: string, state: AgentState) {
  while (!state.done) {
    const modelOutput = await model.generate({
      messages: buildContext(task, state),
      tools: toolRegistry.schemas(),
    });

    if (modelOutput.type === "final") {
      state.done = true;
      return modelOutput.content;
    }

    const intent = parseToolIntent(modelOutput);
    const observation = await toolRuntime.handle(intent, state);

    state.events.push({
      type: "tool_observation",
      intent,
      observation,
    });
  }
}

この疑似コードは Workflow より固定手順がかなり少なくなっています。第一歩は必ずテスト実行、第二歩は必ずファイル検索、第三歩は必ずファイル読み取り、第四歩は必ず編集、とは書いていません。書き固めているのは実行境界だけです。

モデルは次の一手を判断できる
ただし次の一手はツールプロトコルで intent として表現されなければならない
intent はシステムにより制御された形で実行されなければならない
実行結果は state へ書き戻されなければならない
ループは停止できなければならない

これが Agent のエンジニアリング上の核心です。

Agent は「モデルが好き勝手に作業する」ことではありません。

Agent は「モデルが制御された loop の中で次の一手を選ぶ」ことです。

このプロセスをシーケンス図で描くと次のようになります。

システム境界:ChatBot、Workflow、Agent、Harness の違い Mermaid 3

この図で最も重要なのは二つの境界です。

第一の境界は Model -> Agent Runtime の間です。モデルが出力するのは意図であり、すでに起きたアクションではありません。

第二の境界は Agent Runtime -> Tool Runtime の間です。Runtime はツール意図を検証、実行、記録しなければならず、モデルのテキストをそのままシステムへ渡してはいけません。

この二つの境界がないと、Agent は非常に危険なものへ退化します。

モデルが shell を出力する
-> プログラムが直接実行する
-> 失敗と副作用をガバナンスできない

これは Agent の成熟形ではなく、demo 段階で最もよくある危険な近道です。

Agent の利点は明確です。開いた問題に向き合え、新しい証拠に基づいて経路を調整でき、検索、読み取り、変更、検証を動的プロセスとしてつなげられ、未知のプロジェクトの中で段階的に現場を構築できます。

しかし Agent のコストも同じくらい明確です。出力が不安定になり、経路が固定されず、テスト難度が上がり、状態管理が複雑になり、権限ガバナンスが必要になり、エラー復旧が難しくなり、評価も単なる関数戻り値のアサーションでは済まなくなります。

したがって Agent の第一のエンジニアリング規律は次です。

タスク経路を実行時に決める必要があるときにだけ、モデルを次の一手の意思決定へ参加させること。

これが本文で最も強調したい点です。

Agent はデフォルトのアップグレードではない。
Agent は不確実性予算である。

Agent を導入するということは、そのタスクの一部の経路を事前に固定できないと認めることです。同時に、その自由度に対してエンジニアリングコストを支払わなければなりません。そのコストを支払う気がないなら、Agent は「動的に問題を扱えるもの」ではなく、「毎回行動が不安定なもの」になります。

四、Harness:Agent を安定してホストする必要があるとき

ここまでで、Agent は動的判断だと説明しました。しかし動的判断できるシステムは、現実世界に入ったばかりです。

現実世界は、Agent loop があるからといって優しくなりません。さらに問い続けます。

このツール意図には権限があるのか?
このステップはどこで実行されるのか?
このタスクは中断後に復旧できるのか?
モデルはなぜこの一手を選んだのか?
どのファイルを変更したのか?
テストは本当に走ったのか?
失敗セッションをリプレイできるのか?
異なるモデル出力を比較できるのか?
ユーザー確認記録はどこにあるのか?
本番投入後、成功率をどう観測するのか?

これらの問題はモデル自身にも単一ツールにも属しません。Agent 外部のエンジニアリング制御システムに属します。

ここで Harness が登場します。

Harness は別の Agent ではありません。

Harness はより大きな prompt でもありません。

Harness はモデル外側の制御面です。Agent を制御可能な環境へホストする責任を持ちます。

CLI Agent では、最初は runAgent() だけかもしれません。しかしチームに使わせようとすると、徐々に次のものが生えてきます。

  • session id
  • event log
  • tool registry
  • permission policy
  • sandbox
  • context builder
  • checkpoint
  • retry policy
  • cancellation
  • telemetry
  • eval runner
  • audit trail

これらは「アーキテクチャ潔癖症」ではありません。Agent が実エンジニアリング環境に入った後の生存条件です。

Harness の責任範囲は、まず ETCLOVG という七層で軽く覚えられます。

Execution:コマンドはどこで走るか?サンドボックス、タイムアウト、作業ディレクトリ、リソース制限はあるか?
Tools:ツールはどう記述、発見、呼び出し、observation を返すか?
Context:このラウンドでモデルは何を見るべきか?
Lifecycle:タスクはどう開始、中断、復旧、終了するか?
Observability:各ステップにイベントログ、trace、リプレイ可能な証拠があるか?
Verification:システムはタスクが本当に完了したとどう知るか?
Governance:権限、承認、監査、安全境界を誰が制御するか?

本篇ではこの七層を展開しません。次の記事で専門に扱います。ただしここでは一点だけ覚えてください。これらはどれもモデル内部の能力ではありません。モデル外部のエンジニアリング責任です。

Harness は次のような層として簡略化できます。

システム境界:ChatBot、Workflow、Agent、Harness の違い Mermaid 4

この図では、Model は一つのノードにすぎません。システムを使えるものにしているのは外側の制御能力であり、とりわけ PolicyEvent LogVerification です。

権限ガバナンスがなければ、Agent は高リスクなアクションを起こすかもしれません。イベントログがなければ、失敗後に説明もリプレイもできません。検証機構がなければ、システムはモデルの「修正しました」という言葉を信じるしかありません。

これが Harness と Agent の境界です。

Agent はタスク内で次の一手を動的に選ぶ。
Harness はその動的プロセスを実行可能、監査可能、復旧可能、検証可能、ガバナンス可能にする。

さらに硬く言えば:

Agent はタスクの次の一手を判断する。
Harness は、その一手を着地させてよいか、どこに着地させるか、どう記録するか、どう検証するかを判断する。

ローカルでおもちゃの CLI を書くだけなら、完全な Harness はしばらく不要かもしれません。しかし次のどれか一つでも現れたら、Harness を作り始めるべきです。

  • タスクが多くのラウンドを走る
  • ツールが実副作用を生む
  • ユーザーが高リスクアクションを確認する必要がある
  • セッション中断後に復旧したい
  • 結果をテストまたは評価したい
  • 複数ユーザーが同じシステムを共有する
  • Agent を定期実行したい
  • チームが失敗原因を分析したい
  • 異なる prompt、モデル、ツール戦略を比較したい

Harness は「Agent を重く包む」ためのものではありません。目的はむしろ、不確実性をガバナンス可能な境界の中へ閉じ込めることです。

Agent は自由度を持ち込みます。

Harness はその自由度にガードレール、計器盤、ブラックボックスを足します。ただし自由度そのものを確定性へ変えるわけではありません。

とはいえ「Harness があれば Agent は信頼できる」という意味でもありません。より正確には、Harness は Agent を実エンジニアリングフローへ接続する複雑さを下げます。しかし信頼性はなお、タスク設計、ツール境界、コンテキスト戦略、権限制御、失敗復旧、評価体系に依存します。

Harness がない場合、Agent の失敗は多くの場合「モデルが賢くない」からではありません。重要な中間状態を記録していない、ツール出力を保存していない、読み取り専用ツールと書き込みツールを区別していない、検証結果をループに入れていない、長すぎるコンテキストを処理していない、失敗復旧点がない、権限承認がない、評価ベースラインがない、ということです。

これらの問題は、より強いモデルに替えても根本的には解決しません。モデルの外側で起きているからです。

したがって Harness の第一のエンジニアリング規律は次です。

Runtime システムが担うべき責任を、モデルに肩代わりさせようとしないこと。

モデルは判断します。Harness は判断が起きる環境をホストします。

五、四者の対照:能力等級ではなく制御境界

Workflow の制御権はコードにあり、Agent の制御権は一部 Runtime のモデル判断へ渡されることを強調する

ここまで来ると、四つの概念を一つの対照表に置けます。

ただし、この表は定義を暗記するためではありません。エンジニアリング選定のためです。

システム / 制御形態核心問題誰が次の一手を決めるか外部環境に触れるか主なリスク適した場面
ChatBotどう回答し説明するかユーザーとモデルの対話通常は能動的に触れない幻覚、コンテキスト誤解質問応答、要約、説明、書き換え
Workflow確定したフローをどう安定実行するか事前定義されたフロー触れられるが経路は固定フロー分岐の漏れ、外部システム失敗CI、承認、レポート、同期
Agent不確定なタスクをどう前進させるかモデルが動的判断ツール経由で制御された形で触れる経路不安定、権限と状態の複雑化障害調査、コード変更、リサーチ、開いたタスク
HarnessAgent をどう安定ホストするかAgent がタスクの次の一手を判断し、Harness が実行境界を制御する制御された形で触れる監査、復旧、評価、ガバナンスの欠落チーム向け Agent、自動化、長期タスク

この表の核心は「LLM があるか」ではなく、「意思決定の自由度とエンジニアリング制御需要がどれくらい高いか」です。

ChatBot の自由度は対話の中にあります。

Workflow の自由度はフローによって収束されます。

Agent は次の一手の選択の一部をモデルへ渡します。

Harness は Agent の自由度を、より大きな制御システムへ入れます。

したがってシステム形態を判断するとき、次を聞かないでください。

賢そうに見えるか?

次を聞いてください。

次の一手は誰が決めているか?
外部アクションは誰が実行しているか?
状態は誰が記録しているか?
リスクは誰が受け止めているか?
失敗は誰が説明しているか?
完了は誰が検証しているか?

これらの問いは、概念ラベルより信頼できます。

あるシステムには Agent があっても Harness が弱いかもしれません。この種のシステムは demo は美しいですが、長期運用は苦しくなります。

別のシステムには強い Workflow と強い Harness があり、Agent はほとんどないかもしれません。たとえば高度に標準化された CI プラットフォームは、モデルに次の一手を決めさせる必要はありませんが、極めて強いスケジューリング、ログ、権限、復旧を必要とします。

つまり四者は等級ではなく境界です。

境界の選択を誤ると、後続のすべての技術選択が歪みます。

パターンは身分ではない。制御権こそが身分である

ここでもう一つ、より見えにくい誤解をほどく必要があります。システムが多くの “Agentic Design Patterns” を使っているからといって、それが Agent であるとは限りません。

たとえば Prompt Chaining は Agent によく似ています。大きなタスクを複数ステップに分けます。

ユーザー入力
-> 第一ステップで構造化情報を抽出
-> 第二步でコンテキストを補う
-> 第三步で回答を生成
-> 第四步で形式をチェック

ここには複数回のモデル呼び出しがあり、中間状態もあります。かなり「賢く」見えます。しかし各ステップをプログラムが事前に書いており、モデルが各ノード内の実行器にすぎないなら、それは依然として Workflow に近いです。重要な制御権はモデルではなくフローチャートにあります。

Routing も同じです。システムがまずモデルに、ユーザーの問題がどの分類かを判断させることがあります。

bug 調査 -> 調査フローへ入る
ドキュメント要約 -> 要約フローへ入る
コード説明 -> 説明フローへ入る
雑談問題 -> ChatBot へ入る

これは単一路径より柔軟です。しかし候補経路が事前定義されており、モデルが分類だけを担当するなら、依然として Workflow の動的分岐です。本当に Agent 境界に入るのは、モデルが実行中に新しい観測に基づいて次の一手を継続的に選び、その次の一手集合が固定された小メニューではなく、現在目標、ツール結果、状態、予算を組み合わせてリアルタイムに決まる場合です。

Parallelization も Agent ではありません。複数モデル、複数ツール、複数分析器を並行呼び出しすることは、システムの実行構造が fan-out / fan-in であることを示すだけです。鍵は依然として次です。

どのタスクを並行するかは誰が決めるのか?
結果をどう集約するかは誰が決めるのか?
失敗後の次の一手は誰が決めるのか?
状態と証拠は誰が保存するのか?

これらをプログラムフローが決めるなら、それは複雑な Workflow です。モデルが観測結果に基づいてタスクを書き換え、ツールを選び、計画を更新するなら、Agent に入り始めます。

したがって、より専門的な境界判断は次ではありません。

LLM があるか?
複数ステップがあるか?
ツールがあるか?
並行があるか?

そうではなく、次です。

次の一手の制御権はどこにあるか?
外部副作用は誰が制約するか?
状態事実は誰が保存するか?
完了証拠は誰が判断するか?

これが、多くのシステムが見た目は Agent でも、コードを読むと実は Workflow である理由です。逆に CLI ツールにしか見えないシステムでも、モデルが Runtime の次の一手を握っているなら、すでに Agent Runtime の制御機構が必要になっています。

不確実性予算:いつモデルに次の一手を決めさせる価値があるか

不確実性、リスク、制御コストが Harness へ向かう圧力として描かれている

Workflow と Agent を分けると、より実用的な設計原則が得られます。

Agent は不確実性予算の一種です。

つまり「Agent の方が高度だから」使うのではありません。タスク内に、フローとして事前に書けない不確実性が本当にあるから使うのです。

「失敗したテストを修正する」場面で、失敗種別がいつも固定、たとえば moduleNameMapper に path alias が欠けているかだけを確認するなら、Workflow で十分です。安定して次を実行できます。

テストを実行する
Cannot find module にマッチする
tsconfig を読む
test config を読む
修正案を生成する

しかし失敗が依存関係バージョン、非同期レース、DB 状態、mock 設定、時間境界、プラットフォーム差異、テスト順序汚染、キャッシュ問題、型コンパイル出力不一致などから起こり得るなら、固定フローは急速に膨張します。分岐を補い続けることはできますが、それは終わらない障害対応マニュアルのようになります。

ここが Agent の出番です。すべてのフローを置き換えるためではなく、フローでは事前に列挙しきれない判断を扱うためです。

次にどのファイルを読むべきか?
このログのどの信号が重要か?
まず仮説を検証するべきか、それとも先にコードを変えるべきか?
直前の修正が失敗したあと、ロールバックするべきか、方向を変えるべきか?
今回の失敗はモデル判断の誤りか、それともツール結果が不完全なのか?

ただし不確実性は無料ではありません。次の一手をモデルに渡すなら、いくつかのコストを支払う必要があります。

状態コスト:なぜそれをしたのか記録しなければならない。
権限コスト:何ができるかを制限しなければならない。
検証コスト:完了したという発言を信じてはいけない。
観測コスト:失敗後、どこで失敗したかを知れなければならない。
評価コスト:prompt、ツール、モデルを変えたあと退化していないか知れなければならない。

したがって良い Agent 設計とは「モデルへより多く自由を与える」ことではありません。必要な不確実性だけをモデルへ残し、確定できる部分を Workflow、Tool Runtime、Policy、Verification へ回収することです。

一文で言えば:

Workflow は不確実性を事前にフローへ消化する。
Agent は不確実性の一部を実行時処理へ残す。
Harness は実行時不確実性をガバナンス可能な境界へ閉じ込める。

六、同じ CLI シーンで境界を判断する

境界をより確かなものにするため、同じ「テスト失敗の修正」シーンを四つのエンジニアリング形態へ圧縮します。

ユーザーがログを貼り付けます。

FAIL src/user.test.ts
Cannot find module '@/lib/db'

システムが「これは通常、テストランナーがパスエイリアスを認識していないことを意味します。tsconfig pathsjest moduleNameMappervitest alias を確認できます」と答えるだけなら、それは ChatBot です。入力はユーザーが与えたテキストで、出力は説明と助言です。副作用はゼロです。役に立ちますが、「修正しました」とは言えません。

システムが固定フローで実行するなら:

npm test
-> 失敗ログを保存
-> 失敗ファイル名を解析
-> grep moduleNameMapper
-> ログと検索結果をモデルへ渡して要約

それは Workflow です。プロジェクトには実際に触れていますが、次の一手はフローが決めています。失敗原因がちょうどフローのカバー範囲にあるとき、とても安定します。失敗原因がフローの外にあるときは、有限のレポートで止まります。これは欠陥ではなく Workflow の境界です。

システムが第一ラウンドでテストを実行し、第二ラウンドで設定を検索し、第三ラウンドで vitest.config.ts を読み、第四ラウンドで alias を修正し、第五ラウンドでテストを再実行し、失敗したら新しいログに基づいてさらに調整するなら、それは Agent です。各ステップは事前に書き固められておらず、モデルが現在の証拠に基づいて次の一手を決めています。だからこそ、ツールプロトコル、権限、状態、検証がなければなりません。そうでないとファイルを誤って変更したり、一回の失敗を成功と解釈したりする可能性があります。

ここでもう一度境界を押さえます。モデルが「設定を読む」「alias を修正する」「テストを再実行する」と提案しても、それらはすべて行動意図にすぎません。本当にファイルを読み、ファイルを変更し、コマンドを走らせるのは Tool Runtime と Execution 層です。この分離がないと、システムはモデルの記述をすでに起きた事実として誤認しやすくなります。

この Agent をチームが毎日複数リポジトリに対して自動チェックするなら、Harness 境界に入ります。システムは各 run に session を作成し、sandbox でリポジトリを checkout し、利用可能ツールを制限し、モデル出力とツール意図を記録し、高リスク書き込み操作の前に確認またはポリシーを通し、各変更後に検証コマンドを実行し、diff、ログ、最終状態を保存し、失敗時にリプレイでき、成功率と失敗種別を集計する必要があります。

これが完全な進化です。

ChatBot:ログを説明するが、実行しない。
Workflow:固定フローで調査するが、動的判断しない。
Agent:証拠に基づいて次の一手を選ぶが、各ステップは runtime 制約を通らなければならない。
Harness:Agent をホストし、動的プロセスを制御可能、監査可能、復旧可能、検証可能にする。

したがってエンジニアリング上で最も役に立つのは定義ではなく、決定木です。

1. ユーザーが必要としているのは、説明、要約、書き換え、質問応答だけか?
   はい -> ChatBot

2. 実行手順を事前に安定したフローとして書けるか?
   はい -> Workflow

3. 次の一手は、実行中に観測された新しい事実に基づいて決める必要があるか?
   はい -> Agent

4. この Agent は長期稼働し、副作用を生み、複数ユーザーに提供され、復旧と監査が必要か?
   はい -> Harness

この決定木では、意図的に Workflow を Agent より前に置いています。多くのシステムで足りないのは Agent ではなく、よく書かれた Workflow だからです。

たとえば「日報の自動生成」は、データソース、形式、送信先が固定なら、まず Workflow です。LLM はデータを自然言語へ書き換える役割を担えますが、今日データベースを調べるべきか、メールを送るべきか、ある部署をスキップすべきかを決めるべきではありません。

また「PR チェック」も、ルールが明確なら:

テストを走らせる
lint を走らせる
changelog を確認する
セキュリティスキャンを確認する
要約を生成する

これも Workflow です。diff 内容に基づいてどのファイルを読み、どのコンテキストを尋ね、どの専門テストを実行し、複雑な回帰をどう特定するかを動的に決める必要があるときにだけ、Agent に価値があります。

早すぎる Agent 化には典型的な匂いがあります。

prompt に大量の固定手順を書いているのに、なおモデルに毎ラウンド判断させている。
ツールが万能 shell 一つだけで、構造化プロトコルがない。
モデルが tool call を出したあと、システムが直接実行する。
明確な停止条件がない。
ツール結果がイベントストリームとして記録されていない。
提案と実行済みアクションを区別していない。
検証ステップがないのに、モデルに完了宣言を許している。
システム失敗後、最終回答しか見られず、プロセスをリプレイできない。

これらの匂いに共通するのは次です。

システムがエンジニアリング制御をモデルの言語へ譲り渡している。

モデルの言語が得意なのは表現と推論であって、Runtime 責任を担うことではありません。だから Agent システムを設計するとき、最初の反応は「どうすればより自律的になるか」ではなく、次であるべきです。

本当に必要な自由度はどれか?
フロー、プロトコル、戦略へ回収すべき自由度はどれか?

これが Harness 思考の出発点です。

七、耐荷重経路:一つの要求からホスト可能な実行へ

最後に、本文の核心境界を一つの耐荷重経路へ圧縮します。ユーザーが一文を入力します。

このプロジェクトで失敗しているテストを修正してください。

システムには四つの処理方式があります。

ChatBot:
ユーザー入力 -> メッセージ履歴 -> Model -> 自然言語の助言

Workflow:
ユーザー入力 -> 固定フロー -> コマンド/読み取り/LLM ノード -> レポート

Agent:
ユーザー入力 -> Agent Runtime -> Model 判断 -> Tool Intent -> Tool Runtime -> Observation -> State -> 次ラウンド

Harness:
ユーザー入力/スケジュール -> Session -> Agent Runtime -> Policy/Tools/Execution/Context/Event Log/Verification -> 復旧可能な結果

この四つの経路の違いがシステム能力を決め、同時にシステムコストも決めます。第一の経路だけで足りるのに第四の経路を実装すると、システムは過度に複雑になります。第四の経路が必要なのに第一の経路しか実装しないと、システムは幻覚を作ります。第三の経路が必要なのに第四の一部能力がないと、システムは動くが安定しないものになります。

これが、このチュートリアルが段階的に進む理由です。いきなり完全な Harness を作るのではなく、最小 Agent loop から始めます。

Model -> Loop -> Tools -> State

その後、Provider Runtime、Tool Runtime、Context Engineering、Memory、Permission、Session、Observability、Verification、Multi-Agent、Hosted Harness を少しずつ足していきます。各層を足すのは、アーキテクチャを美しく見せるためではなく、前の層が実タスクで新しい失敗形態を露出するからです。

今日の記事の役割は、まず境界を固定することです。後で Tool Runtime を議論するときは覚えておいてください。ツールは ChatBot の飾りではなく、Agent が現実世界に触れるためのプロトコル境界です。Context Engineering を議論するときは覚えておいてください。コンテキストは多ければ多いほどよいものではなく、Agent の各ラウンド判断に必要な現場投影です。Harness を議論するときは覚えておいてください。Harness はより賢い Agent ではなく、Agent を制御可能に動かす外部システムです。

まとめ:技術より先に境界を選ぶ

この記事は四文に圧縮できます。

ChatBot:対話問題。モデルで回答を生成する。
Workflow:確定フロー。プログラムで安定実行する。
Agent:不確定タスク。モデルに loop 内で動的判断させる。
Harness:Agent をホストする。外部システムで実行、状態、権限、観測、検証をガバナンスする。

さらに短くすると:

会話できることは、実行できることではない。
実行できることは、動的判断が必要ということではない。
動的判断できることは、安定ホストできることではない。
安定ホストするには、Harness がモデル外部の責任を受け止める必要がある。

新しい要求に向き合うとき、急いで「Agent を作ろう」と言わないでください。まず、この不確実性は結局どこにあるのか、と問います。

不確実性が表現にあるなら ChatBot。不確実性がフローへ消化済みなら Workflow。不確実性を実行時に扱う必要があるなら Agent。その Agent が実利用環境へ入るなら Harness です。

次の記事では、さらに深い問いを正式に扱います。Harness とは結局何か?なぜフレームワーク名でも、より大きな Agent でもないのか?モデル外部の何を担っているのか?Harness を Execution、Tools、Context、Lifecycle、Observability、Verification、Governance へ分解し、この先のチュートリアル全体の制御システム地図を描きます。

教学 Harness への落とし込み

教学プロジェクトでは、境界を見せるために二つの入口を残せます。POST /api/prompt は単純な debug path、/api/runs と event stream は Harness に近い run path です。決まった workflow は API や test に置き、観測によって次の行動が変わる場合だけ model を loop に入れます。すべての自動化を Agent にする必要はなく、次の一手が observation に依存するときに Agent Loop が必要になります。


GitHub ソース: 00-03-chatbot-workflow-agent-harness.md