Agent Runtimeでは、Session、Run、Turn、Stepが同時に登場することがよくあります。定義を単純に丸暗記すると境界がつかなくなります。フレームワークごとに使う名称が少しずつ異なり、あるフレームワークでは、いずれかの層を明示的なオブジェクトとして作っていない甚至もあります。
より安定した理解には、システム内に同時に存在する複数の時間スケールを見るとよいです。
1回のProviderリクエストが終了しても、現在のAgentの作業が終わったことを意味しません。Tool Loopが止まったとも、このセッションが終わったことを意味しません。ブラウザ接続が切れたからといって、バックエンドの実行が終ったとは直ちに判断できません。
これらの時間スケールをすべてmessages[] + isLoadingに押し込めば、当初は動作しても、キャンセル、再開、承認、同時入力、永続化の扱いは次第に暗黙の規約に依存してしまいます。
複数層のライフサイクルは、これらの制約から次第に生まれます。
1回のModel Callから始める
最も単純なモデル呼び出しには、1つのライフサイクルしかありません。
Input
↓
Model Request
↓
Output
リクエスト開始時に入力は確定し、リクエスト終了時に結果は確定します。
この呼び出しだけを行うシステムなら、Provider Request自体が最も主要な実行境界になります。タイムアウト、キャンセル、エラーも、このリクエストに紐づけられます。
Tool Callingを使うと、この点が変わります。
モデルは最初にTool Callを返すことがあります。
Input
↓
Model Request #1
↓
Tool Call
↓
Tool Execution
↓
Tool Result
↓
Model Request #2
↓
Final Output
Model Request #1はすでに終了していますが、現在の作業がまだ終わっているわけではありません。
つまりProvider Requestで表せるのは、1回のモデル計算までです。Agent全体の推進過程は表せません。
Step:1回のモデル判断に安定した境界を与える
DeepSeek HarnessはStepを使い、1回のモデル判断に近い境界を表現します。[1]
Stepは次のように抽象化できます。
ここで1点明確にしておく価値があります。Stepは通常、単一のHTTP Provider Requestと同一視できません。
モデルがTool Callを生成すると、そのTool Callもこのモデル判断の直接的な結果だからです。Stepをモデルストリームの終了時に直ちに閉じてしまうと、Tool Executionが归属先のない中間状態になります。ツール結果を後続のStepに含めれば、「1回のモデル判断がどの効果を生み出したか」を観測する力が弱まります。
したがって、Model Decisionとそこから直接生じるTool Workを同じStepに入れることで、より安定した実行境界を作れます。
Context N
↓
Model Decision N
↓
Tool Effects N
↓
Step N ends
↓
Context N+1
この境界はRuntimeにとってかなり有用です。
次のStepを開始する前なら、次を安全に処理できます。
- 新しいSteering Message
- Context Compaction
- Hook
- Model/Toolの構成変更
- キャンセル状態
- 実行時に注入される情報
すでに開始済みのModel/Toolの処理を、これらの変更によって途中で書き換える必要はありません。
したがって、StepはRuntimeにおける最小で安定した進行単位といえます。
Assistant Messageを直接Stepとして扱えない理由
UI上で最も直感的な単位は通常、1つのAssistant Messageですが、Runtimeの実行境界とは完全には一致しません。
1つのAssistant Messageには、複数のTool Callが含まれることがあります。
Assistant Message
├── text
├── Tool Call A
└── Tool Call B
RuntimeはAとBの結果を待ったうえで、次のモデルリクエストに進むかどうかを決めなければなりません。
Messageだけを観察すると、次を判断するのは難しいです。
这次模型决策是否已经完整结束
所有 Tool 是否都已经完成
下一次 Context 是否已经可以构造
Steering 是否可以被消费
したがって、Messageは会話の内容を表すのに適し、Stepは実行ライフサイクルを表すのに適しています。
両者は強く関係し合いますが、扱う問題は異なります。
Turn:複数のStepが同じ一つの作業に属する理由
Stepが加わっても、まだ2つめの課題が残ります。
モデルは1回目の応答でTool Callを返し、ツールの実行が完了してからモデルをもう一度呼び出すことがあります。2回目のモデル応答ではさらにツールを呼び出すこともあり、3回目にようやく最終的なテキストが得られます。
これらのStepを、それぞれ別々のタスクとして扱うべきでしょうか。通常はそうではありません。
それでも、これらは同じ一続きの作業によって駆動されています。
Turnが表すのは、この一続きの進行区間です。
DeepSeek Harnessはstep/endのあとでnext-step Inboxを確認します。現在の作業で消費すべき入力が残っていれば、次のStepに進みます。自然に停止する前にはagent/turn-stoppingも通り、Turnが実際に終了する前にHookが作業を挿入し直せます。残っている作業が完全になくなったときだけturn/endが書き込まれます。[1][2]
PiはStepを同名の第一級オブジェクトとして公開していませんが、runLoop()の構造には同じ時間階層が表れています。内側のループがTool CallsとSteering Messagesを処理し続け、現在の連続作業が終わると、外側のループがFollow-upを処理します。[3]
Turnの終了条件は、「モデルがすでにテキストを返した」という事実だけでは判断できません。より正確な条件は次のとおりです。
当前连续工作已经没有必须立即推进的内容
そのため、SteerとFollow-upには別の境界が必要です。
Steer
进入当前 Turn 后续最近的 Step
Follow-up
等待当前 Turn 到达停止边界
第3記事と第4記事では、Piのソースコードからこの関係を検証します。
Session:Turnの終了後に何を残す必要があるか
Turnが終わるとAgentはすでにidleになっているかもしれませんが、セッションは消えません。
次のユーザー入力には、既存の履歴、branch、Memory、またはセッション設定を引き継ぐ必要があります。
そのため、Turnより明確に長いライフサイクルが必要です。
Session
│
├── Turn A
├── Turn B
├── Turn C
└── ...
Sessionは長期的なセッション境界に近いものといえます。
通常は次を保持する役割を担います。
历史 Message / Event
会话元数据
当前分支或 lineage
持久化状态
长期 Memory 引用
多次执行产生的结果
Pi Coding AgentはSessionをJSONLとして保存し、id / parentIdによって木構造の履歴を表します。1つのSessionファイルには複数のbranchを持てます。[4]
DeepSeek HarnessのSessionはappend-only Event Logを採用し、Session Logをlive Agentのdurable source of truthとして扱います。[2]
永続化の構造はまったく異なる一方、ライフサイクル上の位置は同じです。Sessionは1回のAgent Loopより上位にあります。
実行中の状態をすべてSessionに入れるべきではない理由
Sessionのライフサイクルは長いため、プロセスをまたぎ、再開後も存在する事実の保存に向いています。
現在実行中の一時的なオブジェクトは別です。たとえば次のようなものです。
AbortController
正在 Streaming 的半条 Assistant Message
当前网络连接
某个 Tool 的内存句柄
当前 active promise
これらのオブジェクトには通常、長期的には永続化する意味がなく、シリアライズすらできないものもあります。
Sessionが長期的な事実とプロセス内の一時状態を同時に担うと、再開ロジックは非常に難しくなります。どのフィールドを再構成でき、どのフィールドはすでに失効しており、どのフィールドは古いプロセスが残した参照なのかを、システムが判断する必要があるからです。
より安定した境界は次のとおりです。
Session
保存 durable facts
Runtime State
保存当前进程内活动状态
永続化の際は、Runtime内で価値のある状態をEvent、Checkpoint、Run Statusへ変換できます。Runtimeオブジェクト全体を永続化する必要はありません。
なぜRun还需要あるのか
ここまででSession、Turn、Stepが整いました。多くのAgent Coreは、この段階までで十分です。
Web製品やタスク型アプリケーションでは、產品層の実行管理としてRunが導入されることがよくあります。
ユーザーが操作してAgentを起動すると、システムは通常、照会可能な実行エンティティを必要とします。
QUEUED
RUNNING
WAITING_APPROVAL
PAUSED
COMPLETED
FAILED
CANCELLED
また、次のような記録も必要になるかもしれません。
runId
startedAt / endedAt
triggerMessageId
cost / tokens
error
approval state
parentRunId
childRunIds
resume cursor
これらの状態は、明らかに単一のStepには属しません。
Session全体に直接紐づけるのも不適切です。1つのSessionは、独立した実行を何度も経る可能性があるからです。
Session S1
│
├── Run R1 completed
├── Run R2 failed
└── Run R3 running
Sessionに全局的なstatusを1つしか持たない場合、各実行の独立したライフサイクルは保持できません。
したがって、Runの主な役割は、管理、観測、再開が可能なアプリケーション層の実行を、長期Sessionから切り離すことです。
Runが必ずしもAgent Coreに属さない理由
Runが有用であることを理由に、そのままAgent Loopへ取り込んでしまうという誤解は避けなければなりません。
这样做 would bring大量な製品セマンティクスを実行カーネルへ持ち込むことになります。
审批
计费
任务队列
重试策略
父子任务
SLA
页面恢复
审计
これらの機能は、モデルがToolをどう呼び出すかとは直接関係しません。
DeepSeek Harnessの文書は、Northwesternに価値のある境界を示しています。whenIdle()が監視するのは、Agentが現在の活動からquiescenceに達するまでの区間です。この区間を呼び出し側が明確に保有している場合にのみ、runとしてモデル化する意義があります。[2]
PiのAgent内部にもActiveRunがあり、promise、resolve、AbortControllerを保持します。ただし、現在のactive processing intervalを管理するだけで、業務層の永続的なRunが持つ完全なセマンティクスは担いません。[5]
次のような階層分けが可能です。
Run自体は存在しうalthough、すべてのAgent CoreにRunの概念を強制的に理解させる必要はありません。
TurnとRunが最も混同されやすい点
どちらも「一度の作業」と理解できますが,关注点 are different.
TurnはAgent実行セマンティクスにおける連続した作業区間です。Runは、それより製品・スケジューリングのセマンティクスに寄っています。
例えば、Runは承認待ちによって一時停止することがあります。
Run R1
│
├── Turn A
│ └── 请求执行敏感操作
│
├── WAITING_APPROVAL
│
└── Turn B
└── 审批通过后继续
この場合、1つのRunは複数のTurnをまたぎます。
単純な製品では「1つのRun=1つのTurn」と選ぶこともできます。オブジェクトモデルを複雑にする必要はありません。重要なのは、概念境界が将来の停止、再開、スケジューリングの要求を収容できることです。
したがって、本シリーズでは次のように定義します。
Turn
Agent Core 中连续推进的工作边界
Run
应用层可持久管理的一次执行边界
この2つの定義のほうが、すべてのフレームワークに同じ名称を求めるより有用です。
Connectionは別の独立したライフサイクル
Web Agentには、さらに短く、不安定なライフサイクルも存在します。クライアント接続です。
Connectionはリアルタイム伝送だけを担当します。ページのリロード、ネットワーク切り替え、SSE再接続のたびにConnectionは置き換わりますが、RunとSessionはそのまま維持できます。
これらを結びつけると、次のようになります。
connection close
→ run cancel
→ session activity lost
这样的话,系统就很难支持真正的长任务和恢复。
そのため、Web Agentでは少なくとも次を明確に区別する必要があります。
Connection
通信生命周期
Run
执行生命周期
Session
长期会话生命周期
後続のWeb Runtimeの章では、Connectionが切り替わった後にRunのリアルタイム表示をどう再開するかを扱います。
AgentとSessionは1対1であるべきか
複数層のライフサイクルを整理すると、AgentとSessionの紐付け問題にも直面します。
DeepSeek Harnessは現在、1つのlive Agentが1つのSessionを駆動する設計を優先しています。公開されているAgent handleはsession、inbox、status、agent-scoped ctxを直接保持し、両者の結び付きが強くなります。[2]
この設計なら、再開までの経路が直接になります。
Session
↓
resume live Agent
AgentのPrompt、Tools、アダプター、Session履歴を、一貫した状態に保てます。
別の製品では、1つのSessionに複数のAgentDefinitionが参加する設計もあります。
Session S1
│
├── Run R1 → CodingAgent v3
├── Run R2 → ReviewAgent v2
└── Run R3 → CodingAgent v3
このモデルは、複数ロールの協働やorchestrationに向いています。ただし、各Runが実際に使用したAgent設定を記錄してお。否则、履歴を復元したあとに、当時のPrompt、Tools、Model Policyを判断できません。
どちらの構造も成立します。避けるべきなのは、バージョン情報がないままSessionが非互換なAgent設定へ自由に切り替わることです。
キャンセル、エラー、再開はそれぞれどの層に置くべきか
ライフサイクルモデルが妥当かどうかは、異常系の経路を逆にたどって確認できます。正常系では、かなり多くのオブジェクトをまとめて捉えられます。しかしキャンセル、エラー、再開を考えると、各境界がすぐ浮上します。
まずキャンセルを考えます。
ユーザーが現在のProviderリクエストだけを終了したい場合、キャンセル範囲はModel Callまでで十分です。現在のToolと後続のStepを続行しても意味がない場合、キャンセル範囲は現在のTurn全体を覆うべきです。製品がこの作業を一つの永続Runとして管理しているなら、Runの状態をCANCELLEDとして書き込む必要もあります。
同じユーザー操作ですべての操作Starts Premises、社区 internally state change is multiple layers.
エラーも同様です。Providerのタイムアウトなら、現在のStepだけを再試行すればよいかもしれません。Toolの失敗はTool Resultとしてモデルへ返し、処理を続けさせればよいかもしれません。回復不能なエラーならTurn directly ends. 製品側の再試行上限に達して初めて、RunはFAILED 进入します。Sessionは通常そのまま残っており、ユーザーは後から新しいRunを開始できます。
Provider error
↓ retry?
Step
↓ recoverable?
Turn
↓ unrecoverable?
Run = FAILED
Session remains
session.statusが全局的な1つだけだと、これらのエラーの階層を表現するのは難しいです。1つのToolの失敗でセッション全体を失敗扱いにしてしまう一方で、ユーザーは同じSessionを引き続き使えることもあります。
再開の経路も、境界を検証usanpemます。
ページのリロードで必要なのはConnectionの復元だけです。プロセス再起動ではRuntimeを再構築します。Runが承認待ちなら、Runの状態と保留中の作業を復元します。Sessionは長期的な履歴を提供します。
したがって、ライフサイクルオブジェクトの意味は正常系だけでなく、各層が異なる失敗、キャンセル、再開の条件を持つことに表れます。
誰が作り、誰が終え、誰が状態を持つのか
さらに3つの問いでオブジェクトの境界を確認できます。
谁创建它?
谁有权结束它?
它的状态由谁持久化?
Stepは通常、Agent Loopが作成し、自然に終わります。Turnも、Runtimeが連続した進行を制御します。Runはしばしばアプリケーション層またはスケジューリング層が作成し、Runtimeは実行と状態報告だけを担当します。Sessionは通常、製品セッション層が作成し、長期的に永続化します。
オブジェクトの作成者、終了者、状態所有者が全員異なるなら、そのオブジェクトは複数のアーキテクチャ層をまたいでいる可能性が高いです。特定の中核クラスに単純にまとめるのではなく、明示的なインターフェースでつなぐべきです。
Contextがこの永続ライフサイクルのツリーに含まれない理由
Session、Run、Turn、Stepなら、「いつ始まり、いつ終わるか」を議論できます。Contextの性質は異なります。
Contextは、あるStepがモデルリクエストを開始する前に構築する計算ビューに近いものです。
PiのAgent.prompt()はrunPromptMessages()へ入ると、まず次を呼び出します。
現在の実装は次を返します。
その後、AgentContextをrunAgentLoop()へ渡します。[5]
今回のPromptはさらにLoop Local Contextへ追加されます。実際にProviderへリクエストする前に、transformContext、convertToLlmなどの処理を通ります。
より正確な関係は次のとおりです。
Session / Agent State
│
▼
Context Snapshot
│
▼
Current Turn / Step
│
▼
Provider Context
Contextは実行に応じて変化し続けるため、Sessionのように長期の独立エンティティとして保存する必要はありません。
永続化システムで本当に必要なのは、Contextを再構築できる事実です。
これが第2单元の中心課題になります。
SessionとStepだけを残せない理由
抽象化の最小化という観点なら、中間の層が本当に必要かを問うこともできます。
Session
└── Step*
この構造は実装上は可能ですが、多くの制御セマンティクスが安定した帰属先を失います。
Follow-upは、どのグループのStepの完了を待ってから実行するのですか。
承認によって一時停止するのは、どの区間の作業ですか。
あるユーザー操作から始 Execution failed, error should attach to which?
How to query the duration, cost, and status of a long-running task?
システムがこれらが必要とし始めたら、TurnとRunは自然に現れます。
したがって、複数層のライフサイクルは固定の教条ではありません。より妥当な原則は次のとおりです。
2種類の状態で、生成・終了・再開・制御の条件が異なるなら、別のライフサイクルオブジェクトに分けることを検討すべきです。
製品に該当する需要がなければ、相应对象は省略できます。
ライフサイクルの概念をすべてデータベース表へ直接対応させる必要はない
Session、Run、Turn、Stepを理解したあと,另一个常见问题是 whether each layer needs a persistent entity.
答えは、产品在どの粒度で照会・再開する必要があるかによって決まります。
Sessionは長期セッションを運ぶため,几乎总是 need to be persisted. Runは、コネクションをまたぐ実行、承認、再試行、状態の独立した照会が必要なら、通常、永続IDを持たせる価値があります。
TurnとStepには、独立したテーブルが要るとは限りません。
単純なシステムでは、Messageだけを保存する場合もあります。
sessions
messages
Turn / Stepは、Runtimeのメモリ上にある実行上の概念にすぎません。
完全な監査、Replay、コスト集計、障害の特定が必要なら、ライフサイクルをEventとして書き込めます。
session_events
├── turn/start
├── step/start
├── assistant/message
├── tool/call
├── tool/result
├── step/end
└── turn/end
こうすれば、TurnとStepには明確なセマンティクスを保ったまま、可変の行状態をそれぞれ持つ必要がなくなります。DeepSeek HarnessのSession Event Logもこの方向に近いものです。[2]
別のタスクプラットフォームでは、次の情報が必要になるabre也确实です。
runs
run_steps
各Stepの状態を独立して表示したり、個別に再試行したり、個別に課金したりできるためです。この場合、Stepを実体化して初めてJes向西 produkts valueが生じます。
したがって、オブジェクトモデルとストレージモデルは分けて考えます。
Conceptual lifecycle
说明系统如何运行
Persistence model
说明哪些事实需要长期保存和查询
2者は1対1で対応,也可以通过Event Log、Projection或聚合字段来表現。为让架构图看起来“完整”而机械地为每个概念建立表,通常只会增加同步成本。
ライフサイクルが長いほど永続化の要求は高くなる
大まかな傾向として、次のようにまとめられます。
この傾向は、Agent CoreがStep / Turn rehearse、 Web製品がRun / Session rehearse. それは同じ実行システムの異なる時間スケールを見ているだけだ。
時間のスケールでモデルを統一する
最終的に、これらのオブジェクトを同じ時間図に載せられます。
それぞれが答える問いは次のとおりです。
- Model Call:1回のProvider計算はいつ終わるか
- Step:1回のモデル判断と、そこから直接生じるTool Effectsはいつ終わるか
- Turn:現在の連続したAgent作業は、いつ停止境界に到達するか
- Run:1回のアプリケーション層の実行は、いつ最終状態を持つか
- Session:この長期セッションは、いつ終了またはアーカイブされるか
- Connection:各クライアントのリアルタイム接続は、いつ切断されるか
この区分は、後続の設計に直接影響します。
Steerはどの境界に入るべきか、Follow-upはどの境界を待つか、Abortはどの層を終了させるべきか、Sessionを再開tls的时候にはどんな事実を保存すべきか、Web再接続時はRunを復元するのか新しいRunを作るのか。这类问题都依赖于上面的生命周期模型。
第2記事が残す設計判断は次のとおりです。
Agentシステムにおけるオブジェクトの境界は、まずライフサイクルの違いから決めるべきです。Session、Run、Turn、Stepは、それぞれ異なる開始・終了・制御・再開条件を持ち、名前はその境界を表すものにすぎません。
次の intimately sees Pi's real call chain enters. Agent.prompt()がどのように活動区間を作り、Contextを構築し、2層のLoopを進め、Eventが下層の実行結果を外側のAgent Stateへ同期するのか,观察します。
参考資料
[1] DeepSeek Harness Agent Lifecycle: https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/agent-lifecycle.md
[2] DeepSeek Harness Core Subsystem: https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/core.md
[3] Pi agent-loop.ts: https://github.com/badlogic/pi-mono/blob/main/packages/agent/src/agent-loop.ts
[4] Pi Coding Agent Sessions: https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/README.md
[5] Pi Agent source: https://github.com/badlogic/pi-mono/blob/main/packages/agent/src/agent.ts