DeepSeek Harness: セッションログと Trajectory
最終更新:2026-08-31
すべての Agent 会話は再現不可能な旅——モデルにはランダム性があり、ツールには副作用があり、コンテキストは蓄積されます。DSH の Trajectory システムは「追記専用ログ」ですべてのステップを完全に記録し、任意の時点に遡って追跡、監査、フォーク、復元を可能にします。
📋 前提知識:07-python-sdk.md の完了、SDK の基礎に精通していること
1. 学習内容
- 追記専用ログの設計原理
- SessionEvent イベントストリームのタイプと構造
- Trajectory ビューの使用方法
- セッションフォークと復元機構
- ログの永続化とエクスポート
2. 追記専用ログの設計
(1) なぜ追記専用なのか
従来のログシステムは変更と削除を許可しますが、Agent セッションログは不変でなければなりません——フライトデータレコーダー(ブラックボックス)のように、一度記録が書き込まれたら変更できません:
graph LR
E1[Event 1] --> E2[Event 2] --> E3[Event 3] --> E4[Event 4] --> E5[Event 5]
E5 -.->|Append only| NEW[Event 6]
style E1 fill:#e8f5e9
style E2 fill:#e8f5e9
style E3 fill:#e8f5e9
style E4 fill:#e8f5e9
style E5 fill:#e8f5e9
style NEW fill:#fff3e0
追記専用設計の3つの原則:
| 原則 | 説明 | 利点 |
|---|---|---|
| 不変性 | 一度書き込まれたログは変更・削除不可 | 完全な監査証跡 |
| 順序性 | イベントはタイムスタンプで厳密に順序付け | 再生可能な再現 |
| 追記のみ | 新しいイベントの追加のみ、削除なし | 並行性の競合なし |
(2) 従来ログとの比較
| 次元 | 従来ログ | DSH 追記専用ログ |
|---|---|---|
| 変更可能 | ✅ 変更/削除可能 | ❌ 変更不可 |
| 並行安全性 | ロックが必要 | 本質的に安全(追記のみ) |
| ロールバック機能 | バックアップに依存 | 任意の時点から復元 |
| 監査能力 | 改ざんの可能性あり | 改ざん防止 |
| ストレージ効率 | 圧縮可能 | 継続的に増大(定期アーカイブが必要) |
(3) ログストレージ構造
.dsh/
└── sessions/
└── sess_abc123/
├── events.log # イベントログ(追記専用)
├── snapshots/ # 状態スナップショット
│ ├── snap_001.json
│ ├── snap_002.json
│ └── snap_003.json
└── metadata.json # セッションメタデータ
3. SessionEvent イベントストリーム
(1) イベントタイプ
DSH セッションのすべての操作は SessionEvent として記録されます:
type SessionEventType =
| 'session.created'
| 'session.config_changed'
| 'user.message'
| 'agent.message'
| 'agent.thinking'
| 'tool.call'
| 'tool.result'
| 'tool.approval.requested'
| 'tool.approval.resolved'
| 'session.forked'
| 'session.restored'
| 'error.occurred';
(2) イベント構造
各 SessionEvent は標準フィールドを含みます:
interface SessionEvent {
id: string; // Unique event ID
type: SessionEventType; // Event type
timestamp: number; // Unix timestamp (milliseconds)
sessionId: string; // Parent session ID
data: Record<string, unknown>; // Event payload data
parentId?: string; // Parent event ID (used for forks)
}
(3) イベントの詳細説明
ユーザーメッセージイベント:
▶ サンプル 1:user.message イベント
{
"id": "evt_001",
"type": "user.message",
"timestamp": 1724486400000,
"sessionId": "sess_abc123",
"data": {
"content": "Help me refactor the utils directory",
"attachments": []
}
}
ツール呼び出しイベント:
▶ サンプル 2:tool.call イベント
{
"id": "evt_002",
"type": "tool.call",
"timestamp": 1724486401500,
"sessionId": "sess_abc123",
"data": {
"tool": "search",
"params": {
"pattern": "utils/*",
"type": "file"
},
"mode": "standard"
}
}
ツール結果イベント:
▶ サンプル 3:tool.result イベント
{
"id": "evt_003",
"type": "tool.result",
"timestamp": 1724486402300,
"sessionId": "sess_abc123",
"data": {
"toolCallId": "evt_002",
"status": "success",
"result": {
"files": ["utils/format.ts", "utils/validate.ts", "utils/helpers.ts"]
},
"duration_ms": 800
}
}
承認イベント:
▶ サンプル 4:tool.approval イベント
{
"id": "evt_004",
"type": "tool.approval.requested",
"timestamp": 1724486403000,
"sessionId": "sess_abc123",
"data": {
"tool": "file_edit",
"params": {
"action": "edit",
"path": "utils/format.ts"
},
"riskLevel": "high"
}
}
{
"id": "evt_005",
"type": "tool.approval.resolved",
"timestamp": 1724486405000,
"sessionId": "sess_abc123",
"data": {
"approvalId": "evt_004",
"decision": "allowed",
"decidedBy": "user"
}
}
(4) 完全なイベントストリーム例
Timeline Event Type
─────────────────────────────────────────
10:00:00.000 session.created
10:00:05.120 user.message "Help me refactor the utils directory"
10:00:06.300 tool.call search → utils/*
10:00:07.100 tool.result Found 3 files
10:00:08.200 tool.call file_edit → read utils/format.ts
10:00:08.500 tool.result File content returned
10:00:10.800 agent.thinking Analyzing refactoring plan...
10:00:12.000 tool.approval.requested file_edit → edit
10:00:15.000 tool.approval.resolved → allowed
10:00:15.200 tool.call file_edit → edit utils/format.ts
10:00:15.600 tool.result Edit complete
10:00:17.000 agent.message "Refactoring complete!"
4. Trajectory ビュー
(1) Trajectory とは
Trajectory はセッションログの視覚的インターフェースで、Agent の完全な「軌跡」を表示します:
┌─ Trajectory View ──────────────────────────────────────┐
│ │
│ 10:00 👤 Help me refactor the utils directory │
│ 10:00 🔍 search(utils/*) → 3 files 0.8s │
│ 10:00 📄 file_edit(read) → utils/format.ts 0.3s │
│ 10:00 📄 file_edit(read) → utils/validate.ts 0.2s │
│ 10:00 🤔 Thinking... Analyzing refactoring plan │
│ 10:00 ⚠️ Approval: edit utils/format.ts │
│ 10:00 → ✅ Allowed │
│ 10:00 📝 file_edit(edit) → utils/format.ts 0.4s │
│ 10:00 ⚠️ Approval: edit utils/validate.ts │
│ 10:00 → ✅ Allowed │
│ 10:00 📝 file_edit(edit) → utils/validate.ts 0.3s │
│ 10:00 🤖 Refactoring complete! Extracted shared type definitions... │
│ │
│ [Fork from here] [Restore to here] [Export] │
└─────────────────────────────────────────────────────────┘
(2) Web UI での Trajectory アクセス
Web UI で、上部コントロールバーのログアイコンをクリックして Trajectory ビューを開きます:
上部コントロールバー → 📋 → Trajectory
(3) Trajectory のフィルタリングと検索
▶ サンプル 5:イベントタイプでフィルタリング
Trajectory View Filters:
┌──────────────────────────────────────────┐
│ Filters: │
│ ☑ user.message ☑ agent.message │
│ ☑ tool.call ☑ tool.result │
│ ☐ agent.thinking ☐ approval events │
│ │
│ Search: [Enter keywords...] │
└──────────────────────────────────────────┘
(4) SDK での Trajectory アクセス
▶ サンプル 6:SDK でイベントストリームを取得
from dsh import DSHClient
client = DSHClient(base_url="http://127.0.0.1:3080")
session = client.get_session("sess_abc123")
# すべてのイベントを取得
events = session.get_trajectory()
for event in events:
print(f"[{event.timestamp}] {event.type}: {event.data}")
# タイプでフィルタリング
tool_events = session.get_trajectory(event_type="tool.call")
for event in tool_events:
print(f"Tool: {event.data['tool']}")
print(f"Params: {event.data['params']}")
5. セッションフォークと復元
(1) フォークの概念
フォークは特定の時点からセッションブランチを作成します——メインラインは前進し続け、ブランチは独立して発展します:
graph LR
E1[Event 1] --> E2[Event 2] --> E3[Event 3] --> E4[Event 4]
E3 -->|fork| F1[Fork Event 1] --> F2[Fork Event 2]
E4 --> E5[Event 5]
style E1 fill:#e8f5e9
style E2 fill:#e8f5e9
style E3 fill:#e8f5e9
style E4 fill:#e8f5e9
style E5 fill:#e8f5e9
style F1 fill:#e3f2fd
style F2 fill:#e3f2fd
(2) フォークのユースケース
| シナリオ | 説明 |
|---|---|
| ソリューション探索 | 同じノードから異なるアプローチを試し、結果を比較 |
| 安全なフォールバック | 破壊的操作の前にフォーク;失敗したらメインラインに戻る |
| A/B テスト | 同じタスクを異なるモデル/モードで比較 |
| 実験的変更 | 結果が不確かな場合、まずブランチで試す |
(3) Web UI でのフォーク
Trajectory ビューで、任意のイベントの横にある「Fork from here」ボタンをクリック:
10:00 📝 file_edit(edit) → utils/format.ts [Fork from here]
10:00 ⚠️ Approval: edit utils/validate.ts [Fork from here]
10:00 🤖 Refactoring complete! [Fork from here]
フォークは選択したイベントポイントから新しいセッションを作成し、その時点より前のすべてのコンテキストをコピーします。
(4) SDK でのフォーク
▶ サンプル 7:SDK フォーク操作
client = DSHClient(base_url="http://127.0.0.1:3080")
session = client.get_session("sess_abc123")
# 5番目のイベントからフォーク
forked = session.fork(after_event="evt_005")
print(f"Forked session: {forked.id}")
print(f"Parent: {forked.parent_id}")
print(f"Fork point: evt_005")
# フォークブランチで会話を継続
response = forked.send("Try a different refactoring approach, split by function")
(5) 復元
復元はフォークとは異なります——現在のセッションを指定したイベントポイントまでロールバックし、それ以降のイベントを破棄します:
▶ サンプル 8:SDK 復元操作
# 3番目のイベントポイントに復元
session.restore(to_event="evt_003")
# evt_004, evt_005 等のイベントは「restored-away」とマークされる
# 新しいイベントは evt_003 の後に追記される
注意:復元はイベントを削除しません(追記専用の原則)。代わりに、以降のイベントを無効としてマークし、
session.restoredイベントを追加します。
6. ログの永続化
(1) デフォルトストレージ
DSH セッションログはデフォルトでプロジェクトディレクトリの .dsh/sessions/ に保存されます:
.dsh/
├── sessions/
│ ├── sess_abc123/
│ │ ├── events.log # イベントログ
│ │ ├── snapshots/ # 状態スナップショット
│ │ └── metadata.json # メタデータ
│ └── sess_def456/
│ ├── events.log
│ └── ...
├── config.yaml # DSH 設定
└── plugins/ # プラグインディレクトリ
(2) 永続化設定
# dsh.config.yaml
storage:
# ストレージパス
base_path: ".dsh/sessions"
# スナップショット戦略
snapshots:
enabled: true
interval: 10 # 10イベントごとにスナップショットを保存
max_snapshots: 5 # 最大5つのスナップショットを保持
# ログローテーション
rotation:
max_size_mb: 100 # ログファイルあたり最大100MB
max_files: 50 # 最大50セッションを保持
# アーカイブ
archive:
enabled: true
path: ".dsh/archive/"
after_days: 30 # 30日後に自動アーカイブ
(3) セッションログのエクスポート
▶ サンプル 9:JSON でエクスポート
# 完全なセッションログをエクスポート
session = client.get_session("sess_abc123")
events = session.get_trajectory()
import json
with open("session_export.json", "w") as f:
json.dump([e.to_dict() for e in events], f, indent=2)
▶ サンプル 10:Markdown でエクスポート
# CLI エクスポート
dsh session export sess_abc123 --format markdown --output session.md
# Output format
# # Session: sess_abc123
# ## 10:00 - User
# Help me refactor the utils directory
# ## 10:00 - Tool: search
# Pattern: utils/* → 3 files found
# ...
(4) ログのクリーンアップ
# すべてのセッションを一覧(サイズ順)
dsh session list --sort size
# 古いセッションをアーカイブ
dsh session archive --older-than 30d
# アーカイブ済みセッションを削除(不可逆)
dsh session clean --archived-only
7. Trajectory と監査
(1) 操作監査
Trajectory はすべての Agent 操作を記録し、監査に自然に適しています:
graph TB
AUDIT[Audit Need] --> T1[Who executed it?]
AUDIT --> T2[When?]
AUDIT --> T3[What was done?]
AUDIT --> T4[What was the result?]
T1 --> TRAJ[Trajectory Event Stream]
T2 --> TRAJ
T3 --> TRAJ
T4 --> TRAJ
(2) コンプライアンスシナリオ
| コンプライアンス要件 | Trajectory による対応 |
|---|---|
| 操作のトレーサビリティ | すべてのイベントに ID、タイムスタンプ、実行者あり |
| 改ざん防止の変更 | 追記専用ログ、履歴の変更不可 |
| 承認記録 | approval.requested + approval.resolved の完全な記録 |
| ロールバック機能 | 任意のチェックポイントから復元 |
(3) 監査レポートの生成
▶ サンプル 11:監査レポートの生成
session = client.get_session("sess_abc123")
events = session.get_trajectory()
report = {
"session_id": session.id,
"duration": events[-1].timestamp - events[0].timestamp,
"user_messages": len([e for e in events if e.type == "user.message"]),
"tool_calls": len([e for e in events if e.type == "tool.call"]),
"approvals_requested": len([e for e in events if e.type == "tool.approval.requested"]),
"approvals_denied": len([e for e in events if e.type == "tool.approval.resolved" and e.data.get("decision") == "denied"]),
"files_modified": list(set([
e.data.get("path") for e in events
if e.type == "tool.call" and e.data.get("tool") == "file_edit"
])),
"errors": len([e for e in events if e.type == "error.occurred"])
}
import json
print(json.dumps(report, indent=2))
❓ よくある質問
rotation.max_size_mb と archive.after_days で自動ストレージ管理を設定してください。dsh session export;SDK では get_trajectory() メソッドを使用します。📖 まとめ
- DSH セッションログは追記専用設計:不変、順序付き、追記のみ成長
- SessionEvent は13のイベントタイプを含み、会話、ツール、承認、エラー等をカバー
- Trajectory ビューは Agent の完全な実行軌跡を可視化
- フォークは任意のノードからブランチを作成し、メインラインに影響なし;復元は指定ノードにロールバック
- ログの永続化はスナップショット、ローテーション、アーカイブ、エクスポートをサポート
- Trajectory は操作監査とコンプライアンス要件を自然にサポート
- SDK と CLI の両方で Trajectory データにアクセス・操作可能
📝 練習問題
1. ⭐ 基礎:Agent 会話を完了させ(少なくとも2つのツール呼び出しを含む)、Trajectory ビューを開いて、すべてのイベントのタイプとタイムスタンプをリストしてください。
2. ⭐⭐ 応用:セッションの3番目のイベントからフォークブランチを作成し、ブランチで異なるアプローチを試してください。メインラインとブランチの最終結果を比較すること。
3. ⭐⭐⭐ チャレンジ:Python SDK を使って Trajectory 分析ツールを書いてください——セッション ID を入力として、監査レポート(操作統計、承認記録、変更ファイル一覧、エラーサマリーを含む)を自動生成し、JSON 形式で出力すること。