DeepSeek Harness:一次 Prompt 如何变成 Turn、Step 与工具事件

一次 Prompt 如何变成 Turn、Step 与工具事件

用户输入一句"检查这个项目并修复测试",系统什么时候算接收,什么时候算开始,什么时候算完成?

如果把一次 Prompt 直接等同于一次模型请求,工具一出现,边界马上崩掉:模型先请求读文件,工具返回以后还要再调用模型;中途可能插入 steering,也可能取消;一个请求失败后,策略还可能决定重试。最后 SDK 看见 Agent 回到 idle,却不一定能把这次 idle 唯一归因给最早那条消息。

DeepSeek Harness 的固定源码没有用一个笼统的 run() 掩盖这些阶段,而是把 Inbox、Turn、Step、Session Event 和 Agent Status 分开。

本文只回答一个问题:一条进入 Agent 的消息,如何沿着 Turn、Step、模型流和工具批次变成可观测事实?

核心结论是:Turn 是 Agent 对一批待处理输入建立的执行边界,Step 是一次模型请求及其工具执行,Session Event 保存可重放事实,agent/* 负责实时协调。只有把这几层分开,失败、取消、重试和"完成"才不会被压成一个含糊状态。

先看一个最容易误判的案例

假设客户端调用:

text 复制代码
followup("检查项目并修复测试")

它并不会立即拿到一个"这条消息对应的最终回答句柄"。固定文档指出,followup() 只把带身份的消息放入 Inbox 并唤醒 Driver;消息 ID 能追踪插入、认领和丢弃,但不能天然指向后续某条 assistant 输出或某个 Turn end。

Agent 可能在这一活动中连续处理:

  1. 用户 Prompt;
  2. 模型要求读取文件;
  3. 工具结果进入日志;
  4. 模型要求修改文件;
  5. 又有 steering 在下一个 Step 边界被认领;
  6. 最后模型停止调用工具,Agent 继续清空已排队工作后才回到 idle。

因此,客户端至少面对五种不同"完成":消息已入队、Turn 已结束、某个 Step 已结束、工具批次已结算、整个 Agent 已静止。把它们合成一个布尔值,会让 UI、SDK、评测与恢复逻辑互相误解。

Inbox:输入先成为待处理事实

DeepSeek Harness 的 Agent 拥有两个有序输入边界:next-turnnext-step

  • 普通 followup 进入 next-turn,准备打开独立 Turn;
  • steering 和 injected context 面向最近的后续 Step,但是否唤醒 Agent、何时被认领并不相同。

输入先记录插入事实。Driver 被唤醒后,才从 Inbox 认领当前批次:所有可用的 next-step 输入,加上 Turn 边界的一条 next-turn 消息。认领与进入模型上下文仍不是同一步;agent/pre-step 可以重写批次,也可以拒绝进入。

这个拆分解决了一个常见误区:消息"发送成功"只证明它进入待处理边界,不证明模型已经看见,更不证明它会得到独占的一条回答。

Turn:先打开边界,再决定是否真的发生 Step

固定 Session 文档对 Turn 的顺序非常严格:turn/start 在 Driver 认领队列和执行 pre-step 之前就写入日志。

因此,Turn 可以合法地没有 Step:

  • pre-step 拒绝当前批次;
  • 输入被改写为空;
  • 在进入模型请求前发生取消或失败。

这类 Turn 仍有 turn/startturn/end,却没有 step/start。它记录的是"系统确实建立过这次执行边界,并以某种原因结束",而不是伪造一次没有发生的模型调用。

Turn 也不是 HTTP request。Web、Headless 和 Python SDK 可以通过不同传输驱动同一 Agent 语义;一个 Driver 的 running 状态还可能连续覆盖多个排队 Turn。传输请求返回、Turn 结束和 Agent idle 属于三个层次。

Step:一次模型请求,加上它要求的工具执行

Step 在 pre-step 决定 enter(messages) 后开始。固定事件时序大致是:

text 复制代码
step/start
  -> user/message(进入本 Step 的消息)
  -> system prompt 与 tool schemas 装配
  -> agent/request
  -> llm/stream
  -> assistant/chunk*
  -> assistant/message
  -> tool/call* / tool/result*
step/end

如果模型只返回文本并自然停止,一个 Turn 可能只有一个 Step。如果模型发出工具调用,工具结果写入后,Loop 可能再开一个 Step,让模型看到新事实并继续推理。只要工具仍要求下一轮请求,或者 next-step 又有输入,同一个 Turn 就可以继续。

所以,Step 的计数更接近"模型往返次数",Turn 的计数更接近"Agent 为一批工作建立了多少执行边界"。把两者混用,会让成本、延迟、失败率和工具使用统计全部失真。

durable Event 与 live event 不能混为一类

固定架构把事件分为不同职责。

Session 中的 turn/*step/*user/messageassistant/*tool/* 是可重放事实。它们回答:当时发生了什么、模型看到了什么、工具调用与结果如何配对。

agent/* 则承担实时控制和协调,例如状态变化、pre-step 拦截、请求构造、取消和错误恢复。它们适合 UI、Hook 或运行中策略观察,但不应被默认当作可重建对话的持久事实。

官方生命周期图因此给出一个清楚建议:需要可重放 transcript 的 SDK 消费者应读取 session/event;需要队列、状态、steering 和错误协调的消费者使用 agent/*

这不是"所有事件都应该持久化"。恰恰相反,只有影响恢复、审计或模型可见面的事实才需要进入 Session;进程内控制信号可以保留为 live event。正确的边界比日志越多越好更重要。

流式输出不是最终模型事实

模型 Adapter 产生一串 StreamChunk。Surface 可以即时渲染这些 chunk,Session 也保存 assistant/chunk 以维持回放精度;但下一 Step 使用的是组装后的 assistant/message

固定生命周期文档还指出,每次成功 Provider 调用都会记录 assistant/message,即使内容为空或以 max-tokens 结束。它会带上精确来源 chunk 的序号,以及 Provider 提供时的 usage。

因此,UI 看见 token 不代表 Step 已经形成最终事实。Adapter 还必须处理工具调用增量、结束原因、空内容、取消和流错误。只转发文本 token 的"兼容层"无法满足完整 Loop 契约。

工具调用是一个批次,不是一个函数

模型输出工具调用后,Loop 先记录 tool/call,再进入工具注册表。固定工具管线依次包含:

  1. tools/pre-execute waterfall;
  2. monotonic guards;
  3. tools/execute waterfall 与工具 body;
  4. tools/post-execute waterfall;
  5. 结果规范化与 finalizeContent
  6. tools/result 观察;
  7. Session 中唯一的模型可见 tool/result

同一 assistant message 可以包含多个工具调用。固定 Agent Lifecycle 图显示,Loop 会按 barrier 与有界滚动池调度开始,但结果仍按模型顺序写回。这是一个重要区别:执行可以并发,模型可见结果的记录顺序仍要稳定。

工具被拒绝也不等于 Runtime 崩溃。拒绝、审批失败、工具 body 错误或 post 阶段替换,最终都需要形成权威 outcome,并在适用时成为模型可见结果。具体 Sandbox、权限和副作用策略属于本系列第 5 篇,本篇只保留执行边界。

一个 Turn 为什么会继续,又为什么会结束

模型请求和工具批次完成后,Loop 判断是否还欠下一轮:

  • 有工具结果需要交回模型;
  • next-step 有新的 steering 或 injected context;
  • 策略在结束检查点要求继续。

如果没有 continuation,Loop 在自然停止时进入 agent/turn-stopping 终止检查点,然后结束 Turn。随后,Driver 还可能处理已经排队的下一个 Turn;只有整个活动真正静止,Agent Status 才回到 idle

这解释了为什么 whenIdle() 不能被误写成"等待这一条消息的回答"。固定 Core 文档明确说,它观察整个 Agent 的静止状态,并跟随后续替换工作与 maintenance task。只有调用者明确拥有从某次收件到下一次全 Agent idle 的区间,才可以把这个区间称为一次 run。

失败与取消必须按发生位置分类

一条 error 无法支撑恢复和评测。至少应区分以下位置:

失败位置 事实边界 不应该误写成什么
Inbox 插入后、认领前 消息仍是待处理或被丢弃的输入事实 模型请求失败
pre-step reject Turn 可以零 Step 结束 Runtime 崩溃
Provider 路由或凭据 模型请求无法完成;研究材料的 MISSING_CREDENTIAL 属此边界 真实模型 E2E 失败样本
流式请求中断 已有 chunk 可能存在,但 Step 没有成功结果 完整 assistant message
tool deny / approval refused 策略正常执行并阻止 body 系统异常或 Sandbox 崩溃
tool body / wrapper / post 失败 由工具管线规范化为权威 outcome 一定需要终止整个 Agent
Agent cancel 活动 signal 被终止,durable turn/end.reason 保存 aborted 及 user/parent/hook/disposed 原因 原因类别等于具体操作者身份或完整审计轨迹
Agent idle 当前没有活跃 Driver 或 maintenance task 某个 MessageId 的因果完成证明

固定 Commit 的源码类型在这里比概述文档更精确:

一张可执行的观测合同

团队接入 DeepSeek Harness 或设计类似 Runtime 时,可以用下表明确每项指标与状态到底绑定什么:

读者想知道 应观察的对象 能得出的结论
Prompt 是否进入系统 Inbox inserted / MessageId 已入队,不代表模型已见
输入是否进入一次执行 inbox claimed + turn/start 已建立 Turn 边界,仍可能零 Step
模型调用了几次 step/start / step/end 模型往返次数与每步边界
模型流是否形成稳定输出 chunks + assistant/message 区分实时渲染与最终模型事实
工具是否真正执行 tool/call、管线 outcome、tool/result 区分调用意图、策略拒绝与执行结果
本 Turn 为什么结束 turn/end.reason 本次 Turn 的粗粒度终点
整个 Agent 是否静止 agent/status / whenIdle() 全 Agent 当前无活动,不是单消息回执

这张表是本文的主要产物。它把"发送成功""模型完成""工具完成""Turn 完成"和"Agent idle"拆成不同合同,避免指标和 API 名称制造虚假的因果关系。

结论

DeepSeek Harness 的 Agent Loop 可以概括成一条迭代链:从 Inbox 认领输入,打开 Turn,通过 pre-step 决定是否进入 Step,装配请求并流式调用模型,把模型与工具的可见结果写成 Session 事实,再决定继续下一 Step、结束 Turn或处理下一个 Turn。

它值得借鉴的不是循环代码有多复杂,而是边界足够明确:Turn 可以零 Step,Step 才对应模型往返,工具调用是受控批次,Session Event 与 live event 职责不同,Agent idle 也不是单条消息的完成证明。

这套边界让失败和取消能够被准确命名,但不自动证明真实模型、工具副作用和长任务恢复已经可靠。那些结论仍需要凭据、故障注入和生产观测。

参考资料

  1. 固定 Commit Architecture:github.com/deepseek-ai...
  2. 固定 Commit Agent Lifecycle:github.com/deepseek-ai...
  3. 固定 Commit Core Subsystem:github.com/deepseek-ai...
  4. 固定 Commit Session Subsystem:github.com/deepseek-ai...
  5. 固定 Commit Tool Execution Pipeline:github.com/deepseek-ai...
  6. 固定 Commit LLM Streaming:github.com/deepseek-ai...

证据与推导边界

  • Turn、Step、Inbox、事件顺序与 whenIdle:固定 Commit 官方文档事实;取消持久字段以固定源码 session/src/types.ts 为准。
  • 工具管线的阶段和结果记录顺序:固定生成文档事实;不等于所有工具安全性已验证。
  • "五种完成"和观测合同:基于固定事件边界的作者工程归纳。
  • MISSING_CREDENTIAL:只证明研究环境触发凭据门禁,不是一次真实模型任务失败样本。
  • 真实模型 E2E、长任务恢复、工具幂等与生产稳定性:未验证,不作结论。
相关推荐
运维行者_1 小时前
预测性云监控怎么做?AI驱动的7大核心能力与落地路径
服务器·开发语言·网络·数据库·人工智能·python·php
赋创小助手1 小时前
机器人研发负载拆解:数据、仿真、训练与推理分别需要哪些计算资源?
服务器·人工智能·机器人·具身智能·gpu计算
小白的成长路程1 小时前
llms.txt:搜索不读,AI助手天天看
人工智能·geo
TechEdu2026061 小时前
[人工智能]DeepSeek、Qwen3.8-Max、ERNIE、Doubao、Hunyuan与Kimi:概念、架构、应用和评估
人工智能·ai
呆萌很1 小时前
PyTorch CosineAnnealingLR的T_max和eta_min参数设置
人工智能·pytorch·python
环境栈笔记1 小时前
指纹浏览器怎么用:从 Profile、代理到环境检测的完整上手流程
前端·人工智能·后端·自动化
光锥智能1 小时前
他山科技亮相WRC 2026:“机器人幼儿园”驱动具身智能迈向“经验时代”
人工智能·科技·机器人
Raas1001 小时前
AI网关能省多少钱?MAI Gateway (魔芋企业级AI网关)降本ROI实战案例
人工智能
jikemaoshiyanshi1 小时前
自建大模型推理服务如何优化算力成本与资源效率?—— 基于智能路由、PD 分离、缓存、弹性调度的云上基建选型
人工智能