概念 · 持久狀態

對話與回合

Heddle Conversation 是持久的產品狀態,不是 Host 每次收到 Request 時重新拼裝的 Prompt Array。Conversation Engine 負責 Session 與 Turn 如何演進;你的產品負責誰能存取這些資料,以及正式環境的 Record 要存放在哪裡。

客製化深度

深度 4 — 接管對話生命週期與呈現方式

託管深度

Process 內的 Node.js Engine;Hosted Run 底層使用相同語意

文件狀態

權威參考

前置假設

  • 你正在使用 Node.js 20+ TypeScript Host。
  • 你的產品能為每段產品對話指派或保存穩定的 Heddle Session ID。
  • 你已選擇持久的 stateRoot,或注入 ChatSessionRepository。
Heddle 負責
  • Session Record、Message、Turn Summary、Queued Prompt、Continuation State、Compaction Metadata 與 Lease。
  • Turn Preflight、Model 與 Tool Execution、Approval Callback、Trace 持久化、Artifact,以及最終 Turn Summary。
  • Session 的樂觀版本控制,以及建立、更新、壓縮與延續 Session 的規則。
產品端負責
  • 驗證、Tenant 與 User 對應、授權,以及產品 Conversation 與 Heddle Session 的關聯。
  • Model 與 Credential 選擇、產品 Tool、System Context、UI State,以及產品專屬的結果呈現。
  • 正式環境 Repository 實作、保留期限、加密、備份、刪除政策與資料落地區域。

三個層級的狀態

整合時,請把以下三個彼此相關、但生命週期不同的概念分開:

概念生命週期代表內容
Conversation Engine通常與 Host Process 或 Composition Root 同壽命已完成設定的 Runtime 邊界,提供 Session、Turn 與 Artifact Service。
Session跨 Process 與 Request 持久保存一段完整的多輪對話,包含 History、Message、Turn、設定、Queue、Compaction State 與 Lease State。
TurnSession 內的一次 Agent Execution一個 Prompt 或 Continuation,執行 Model/Tool Loop,並把結構化結果寫回 Session。

Engine 由產品設定建立。它不代表單一 User 或單一 Chat。同一個 Engine 可以處理多個 Session,讓它們共用 Workspace、State Root、預設 Model、Tool 與 Repository Adapter。

TypeScript
import { createConversationEngine } from '@roackb2/heddle'

const engine = createConversationEngine({
  workspaceRoot: process.cwd(),
  stateRoot: '/var/lib/my-product/agent-state',
  model: 'gpt-5.4',
  reasoningEffort: 'medium',
})

回傳的 Engine 有三個 Public Service:

  • engine.sessions 負責持久 Session 的讀取與生命週期操作;
  • engine.turns 負責提交或延續 Agent Turn;
  • engine.artifacts 透過 Engine 最終解析出的 Artifact Repository 列出與讀取 Artifact。

不要自行重建 Artifact Path,也不要繞過這些 Service。Engine Configuration 可以替換預設的 Storage Root 與 Repository。

Session 生命週期

提交第一個 Turn 前,先建立可重複使用的 Session:

TypeScript
const session = await engine.sessions.create({
  name: 'Support investigation',
  model: 'gpt-5.4',
  reasoningEffort: 'medium',
})

當產品明確需要一次性保留語意時,使用 createOneOff。Session Service 也支援 List、Catalog Pagination、Read、Require、Latest、Rename、Pin、Archive、更新設定、Reset 與 Delete。

以下兩種持久讀取有一個重要差異:

  • readExisting(id) 只會回傳真正已保存的 Session,不會在空 Repository 中具現化 Heddle 的 Host-facing Fallback Session;
  • read(id) 遵循一般 Host-facing 行為,在空 Repository 中可能包含該 Fallback。

API 的存在檢查與授權敏感查詢應使用 readExisting。當找不到 Session 應視為例外並中止操作時,使用 require

你的產品應把回傳的 Heddle Session ID 保存於自己的 Conversation Record 旁。Heddle 刻意不把 Tenant、Account、Membership 或 Row-level Security Field 放進 ChatSession。Repository 應由可信任的 Server-side Identity 建立 Scope,不要信任 Browser 傳入的 Tenant ID。

提交新的 Turn

既有 Session 收到新的 User Instruction 時,使用 engine.turns.submit(...)

TypeScript
const result = await engine.turns.submit({
  sessionId: session.id,
  prompt: 'Summarize the incident and propose the next safe action.',
  host: {
    events: {
      onActivity(activity) {
        renderActivity(activity)
      },
    },
    approvals: {
      requestToolApproval(request) {
        return decideWithProductPolicy(request)
      },
    },
  },
})

一個 Turn 不只是一個 Model Call。Heddle 會準備持久 History、取得 Session Lease、在需要時執行 Compaction、解析選定的 Model 與 Credential、組合 Tool 與 Host Extension、執行 Model/Tool Loop、記錄 Trace Evidence、保存結果,最後釋放 Lease。

回傳的 Turn Result 是穩定的 Host-facing Summary:

TypeScript
result.outcome
result.summary
result.failure
result.session
result.traceFile
result.artifacts
result.toolResults

產品行為應依據 failure.code。它會以安全且結構化的方式區分 authenticationpermissionquotarate_limitrequesttransportempty_responseunknown 等 Model Failure。不要解析 Provider Error Text 或給人閱讀的 Summary。

延續中斷或先前的 Run

需要明確的延續語意時,使用 engine.turns.continue(...)

TypeScript
const result = await engine.turns.continue({
  sessionId: session.id,
  prompt: 'Continue from the saved state and finish the verification.',
  host,
})

如果提供 Prompt,Heddle 會針對同一個 Durable Session 提交該 Prompt。如果省略 Prompt,而且 Session 有既有 History,Heddle 會重用已保存的 Last Continuation Prompt。對沒有 History 且沒有記住 Continuation Prompt 的空 Session 呼叫 Continue 會失敗,不會憑空建立工作。

不要用「把產品 Transcript 重播進全新 Session」的方式實作延續。這會產生兩份 History、繞過 Heddle 的 Compaction 與 Turn Record,也會讓 Artifact 與 Trace 難以連回原始 Conversation。

Conversation History 與呈現

持久的 ChatSession 針對不同責任保留不同 View:

  • history 是 Runtime Execution 使用的 Model-facing Message History;
  • messages 是 User/Assistant Conversation 的呈現狀態;
  • turns 包含 Durable Turn Summary 與 Evidence Link;
  • queuedPrompts 保存已接受、等待執行的 Prompt;
  • contextarchives 保存 Token Estimate 與 Compaction State;
  • lease 協調目前由誰擁有該 Session 的執行權;
  • Model 與 Reasoning Setting 可以按 Session 保存。

請把這份 Record 視為 Heddle-owned Schema。Custom Repository 負責保存完整 Record 與 Revision;它不應自行改寫 Message、Lease、Queue 或 Compaction 的語意。

Compaction 會保留 Conversation Contract

長對話最終會超過實際可用的 Model Context。Heddle 可以壓縮較舊的 History,保存產生的 Archive 與 Summary Metadata,同時維持 Session ID 與產品關聯不變。

Host 可以透過 host.compaction Callback 觀察 Compaction,但不應再用第二套 Summarization System 與 Heddle 競爭。如果產品需要 User-facing Conversation Summary,請把它當作 Presentation Data 衍生;不要在 Engine 不知情的情況下替換 Model-facing History。

Lease 避免多個 Turn Owner 互相競爭

Session Lease 會記錄目前由哪個 Runtime 擁有該 Turn。Turn Execution 會在 Engine Lifecycle 中取得並釋放 Lease,以防止兩個 Host 同時悄悄修改同一段 Conversation。

如果 Host Crash 留下操作上已失效的 Lease,請使用明確的 Lease API 與 Owner Identity 檢查或清除。不要直接刪除 Storage 裡的 Lease Field。Custom Repository 必須保留 Optimistic Revision,確保兩個 Process 無法互相覆寫較新的 Session Change。

預設與自訂持久化

未提供 Repository 時,Heddle 會使用 stateRoot 下的 File-backed Session Adapter。它適合單機、本機產品,並使用 Immutable Revision Body 與 Atomic Catalog Replacement。

Hosted Product 可以注入 Async ChatSessionRepository

TypeScript
const engine = createConversationEngine({
  workspaceRoot,
  stateRoot,
  model,
  sessionRepository: {
    list: (input) => listAuthorizedSessions(input),
    read: (sessionId) => readAuthorizedSession(sessionId),
    create: (stored) => createAuthorizedSession(stored),
    update: (input) => compareAndSwapAuthorizedSession(input),
    delete: (input) => compareAndSwapDelete(input),
  },
})

Repository Update 與 Delete 使用 Expected Revision。正式環境 Adapter 必須區分 Missing Record、Already-existing Record 與 Revision Conflict,不可採用 Last-write-wins。

目前 Session 與 Artifact Repository 都可以注入。Trace 與 Memory 仍以 stateRoot 下的 Path 為主,因此 Hosted Deployment 仍需為這些 Domain 選擇持久且妥善隔離的 State Root。

上線前必須驗證的不變條件

  1. 第二個 Turn 使用相同 Heddle Session ID,且能看到第一個 Turn 保存的 History。
  2. 未授權的 User 無法讀取、修改、延續或刪除其他 Scope 的 Session。
  3. Concurrent Writer 會產生 Revision Conflict,而不是遺失較新的 Update。
  4. Process Restart 後可以重新開啟 Session,且 Message、Turn、Queue、Lease 與 Compaction State 都完整保留。
  5. Approval Denial 與 Model Failure 會留下誠實的 Terminal Result,不會顯示為成功。
  6. Product UI 從 Activity 與 Result Contract 衍生,而不會成為第二份 Conversation Truth。

權威來源