首页 > 教程 > DeepSeek Harness 教程 > DeepSeek Harness 会话日志
DeepSeek Harness 教程

DeepSeek Harness 会话日志

第 32 / 35 节 2026-11-07

会话日志:唯一真源

模型看到的对话历史存在哪里?dsh 的答案很特别:不来自任何专门的地方,而是从一份会话日志派生。Session 是由类型化 SessionEvent 组成的仅追加日志,是完整交互历史的唯一真源;LLM 消息历史从日志派生、从不单独存储,回放就是从同一组事件重新派生。

每个事件携带单调递增的 seq(等于 log.length)与 epoch 毫秒的 time;事件基于 type 做可辨识联合,switch (event.type) 即可收窄 event.data。所有 event.data 必须能无损序列化为 JSON,Session.append 在源头强制这一点。核心不变量是“模型可见即已记录”:新增一项模型可见输入,就要新增一个会话事件(扩展 SessionEventMap 并从日志渲染);需要可回放 transcript 的 SDK 用户应消费 session/event 事件流。

deriveMessages:从日志投影模型历史

Session.deriveMessages() 把事件日志投影成模型看到的 Message[]。它是缓存的(surface 节点首次出现时投影,重写时重建),返回冻结数组,从类型上杜绝了通过投影篡改历史。投影规则:

事件 投影结果
user/message 一条 user 消息,携带确切 content
assistant/message 一条 assistant 消息,含提供方、模型与可选回放状态
assistant/chunk 跳过(回放/UI 数据,组装后的消息才是权威)
tool/result 一条带 tool-result 块的 user 消息
turn/*、step/* 跳过(结构信息,不投影为消息)

细节:内容为空的 assistant/message 也会被跳过,但因 max-tokens 截断且无内容的步骤仍会记录一条 assistant/message 以保存用量,并通过 sourceEventSeqs 精确列出对应的 chunk 事件(包括显式空列表)。

事件三域与轮次生命周期

事件分三域,选对是大多数改动的第一个决定:会话事件(turn/start、user/message、tool/result 等)追加进日志并广播,是持久事实;Agent 事件(agent/pre-step、agent/request 等)携带活跃 Agent,用于实时控制与观察;能力事件(tools/*、fs/*、llm/stream)给能力 seam 挂策略。其中 agent/pre-step、llm/stream 与三个 tools/* 事件是 waterfall,必须调 next() 委托;agent/turn-stopping 是 serial 事件,没有 next()。

一个步骤(step)是一次模型请求加上它调用的工具;一个轮次(turn)包含零个或多个步骤,在领取首条输入前打开、不再欠工作时关闭——它包围一次模型循环执行,而非整个会话日志。完整时序:turn/start → agent/pre-step → step/start → llm/stream → 工具 → step/end → turn/end;turn/end 的 reason 为 TurnEndReason(completed / aborted / blocked / error / max-tokens / interrupted)。首次领取被拒绝或改写为空时,仍会关闭一个不含步骤的持久轮次。

动手示例:解析一份 JSONL 会话日志

规范打包形态下每行是一个 SessionEvent,surface 事件(user/message、assistant/message、tool/result)带 surfaceOp 标记,边界事件不带:

{"type":"turn/start","seq":0,"time":1755000000000,"data":{"turn":1}}
{"type":"step/start","seq":1,"time":1755000000010,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":2,"data":{"role":"user","content":[{"type":"text","text":"Fix the typo in the runoob README."}]},"surfaceOp":"append","sourceEventSeqs":[0]}
{"type":"tool/call","seq":6,"data":{"turn":1,"step":1,"callId":"call_1","name":"bash","arguments":"{\"command\":\"grep runoob README.md\"}"}}
{"type":"turn/end","seq":9,"data":{"turn":1,"reason":{"kind":"completed"}}}

用 Python 重放这份日志(deriveMessages 的简化教学模型):

import json, sys

def derive_messages(events):
    messages = []
    for ev in events:
        t, d = ev["type"], ev["data"]
        if t == "user/message":
            messages.append({"role": "user", "content": d["content"]})
        elif t == "assistant/message":
            messages.append({"role": "assistant", "content": d["message"]["content"]})
        elif t == "tool/result":
            messages.append({"role": "tool", "name": d["message"]["toolName"],
                             "content": d["message"]["content"]})
    return messages

原始 assistant/chunk 在派生时被跳过,组装后的 assistant/message 才是权威——模型看到的每一条消息都能从这份日志原样重建。

 

©️版权声明:
本网站(https://aigc.izzi.cn)刊载的所有内容,包括文字、图片、音频、视频等均在网上搜集。
访问者可将本网站提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本网站及相关权利人的合法权利。除此以外,将本网站任何内容或服务用于其他用途时,须征得本网站及相关权利人的书面许可,并支付报酬。
本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站,予以删除。

继续学习其他教程