dsh agent loop 详解:一次对话是怎么跑起来的
从输入领取到 turn/end:turn 生命周期、插件拦截点、loop 与 subagent/Agent Teams 的辨析,以及防止失控的 loop guard。
最近更新: 2026-10-03
不管你在终端、网页还是桌面应用里用 dsh,背后驱动对话的都是同一台引擎:agent loop。它拿到你的消息,问模型下一步做什么,执行模型选中的工具,再循环往复,直到这个 turn 没有欠着的事情为止。看过模式、插件、子代理的教程之后,如果你还想弄明白「输入和回答之间到底发生了什么」,这一页就是缺的那层原理。
本文全部内容以官方架构文档和公开讨论帖为依据,来源在正文和页尾都给出了链接;文档里用什么事件名,我们就用什么事件名,不做逆向猜测。涉及版本的行为一律以官方 changelog 为准。
太长不看
- ▸step 是一次模型请求加上它调用的工具;turn 是零个或多个 step,从领取输入开始,到「没有欠账」时关闭。
- ▸agent loop 本身也是一个插件(core/agent-loop 是默认驱动),每个阶段都是事件,其他插件都可以拦截。
- ▸瀑布类事件(agent/pre-step、agent/request、llm/stream、tools/*)要求监听器调用 next() 放行;agent/turn-stopping 可以终止一个 turn。
- ▸subagent 和 Agent Teams 在 loop 之上而不是里面;loop 失控几乎都是插件设计问题,社区已有成熟的 guard 套路。
agent loop 是什么,不是什么
DeepSeek Harness 构建在 Cordis 插件框架上:服务、类型化事件、可回滚的副作用都挂在一个共享上下文里。官方文档把话说得很直白——这个产品的每一个部分都是插件,模型适配器、工具注册表、会话日志,连同 agent loop 本身。core/agent-loop 是实现 Agent 接口的默认驱动,并且可以通过配置替换。
这一页只需要记住两个定义,均出自官方 turn flow 文档:
Turn(轮)
零个或多个 step。turn 在领取第一条输入之前开启,在没有欠账时关闭。
Step(步)
一次模型请求加上它调用的工具。同一场对话里工具往返三次,就是同一个 turn 里的三个 step。
Agent loop
重复 step 循环的驱动器:领取输入、组装提示词、调用模型、执行工具、判断是否还欠下一个 step。
一个 turn 的完整生命周期
官方文档直接公布了 turn 的事件序列。下图按顺序走一遍:等宽标签里的都是文档里的真实事件名,也就是你写插件时要监听的名字。
- 1
turn 开启
持久落盘turn/startturn/start 触发,循环从 inbox 领取下一步输入和一条排队消息。注入的上下文要等下一条「唤醒消息」才会生效。
- 2
组装提示词与工具
拼装提示词分区和工具 schema,投影运行时上下文。模型看到的历史来自会话日志,不是某个漂浮在内存里的数组。
- 3
输入闸门
agent/pre-stepagent/pre-step 的监听器可以改写或拒绝刚领取的消息。这是第一个瀑布:每个监听器都必须调用 next() 让链条继续。
- ↳
被拒绝就关闭
turn/end消息被拒绝,或首次 enter 被改写为空,这个 turn 会以「零 step」的形式落盘关闭。
- 4
一个 step:模型请求
step/start · llm/streamstep/start 之后,agent/request 解析真实路由,请求从日志推导并冻结,模型回复经 llm/stream 流式返回。
- 5
工具管线
持久落盘tools/pre-execute → execute → post-execute每次工具调用都走 tool/call → tools/pre-execute → tools/execute → tools/post-execute → tool/result 这条带护栏的链。失败的 step 会记录缺失的工具结果。
- 6
还有下一步吗?
step/endstep/end 之后循环做判断:工具还欠一次请求,或新输入到了,就领取下一个 step,循环继续。
- 7
停止检查
agent/turn-stoppingagent/turn-stopping 是没有 next() 的串行事件。监听器在这里可以让 turn 停下来——这是官方文档写明的 turn 拦截机制。
- 8
turn 关闭
持久落盘turn/end没有欠账时 turn/end 触发。turn/*、step/*、消息事件和 tool/* 是持久会话事件,重启后仍在;其余都是活扩展点。
两个细节值得记住:step 内的重试不会重新走提示词组装和 agent/pre-step;「模型可见即已落盘」是硬规则——每次模型请求都必须能从会话日志完整重建。
插件从哪里接入 loop
官方文档里没有「hook」这个词,官方说法是事件,其中大多数是瀑布(waterfall):监听器拿到上下文后必须调用 next() 放行,也可以在放行前改写载荷。最重要的是四族,而 agent.inject() 是从 loop 外部注入模型可见上下文的官方入口:
把守输入:agent/pre-step
改写或拒绝循环刚领取的消息,它决定这个 step 接受什么输入。
拦模型请求:agent/request、llm/stream
在模型调用流式输出前观察或改造它;这个阶段的取消不会提交系统提示词,也不会提交用户消息。
包住每个工具:tools/pre-execute、tools/execute、tools/post-execute
每次工具调用外层的带护栏执行管线——审批、日志、策略类插件的标准落点。
终止 turn:agent/turn-stopping
串行、没有 next()。在这里返回停止决策即可结束 turn——loop guard 类插件依赖的就是这个事件。
agent loop、subagent、Agent Teams 怎么分
「dsh agent swarm」这类搜索把三个不同层次混在一起。把每一层都锚定到 loop 上,就再也不会混:
agent loop
一个 agent 的引擎。本页讲的 turn、step、事件,都是单个 loop 驱动单个会话的故事。
Subagent(子代理)
由你的 agent 派生的第二层 agent 实例。官方把 subagent provider 定义在同一个接口后面——「从全新的子 agent 到另一个产品里委托的 turn」都算,每个 subagent 跑自己的 loop。
Agent Teams(实验性)
官方可选的协作 seam:持久花名册、任务板、邮箱,叠在可续跑的 subagent 之上。它是单个 loop 之上的协调层。
「Swarm」
社区对多 agent 编排的俗称,不是 dsh 官方功能名。读到它时,看协调是临时起意还是结构化,分别对应 subagent 和 Agent Teams。
loop 失控与 loop guard
loop 的设计目标就是会结束:turn 在没有欠账时关闭,agent/turn-stopping 的存在意义就是让监听器能叫停。所以失控几乎都出在「喂给 loop 的东西」上——通常是某个插件在不断注入新输入。
记录最完整的案例是官方讨论 #4819:社区插件 dsh-agent-loop 在模型只输出推理、没有可见文本时自动续跑——注入一条 notice 消息推着循环往前走。但注入的消息没有写 message id,冷恢复时会话日志校验失败(「session event lacks an identified message」),整个会话打不开。帖子里讨论出的修复思路就是标准的 guard 套路:合成消息走正常的 createUserMessage 通道,并且在 append 时就校验事件形状,而不是只靠恢复时兜底。
- ▸限制圈数——每个 turn 设轮次上限,是所有 loop guard 配方的第一条不变量。
- ▸检测无进展——连续两个 step 没有任何变化,就停下来,别再续了。
- ▸同错熔断——同一个错误出现两次就断路,比烧 token 的重试风暴便宜得多。
- ▸保留显式出口——loop 永远要有一个人类可见的退出方式。
常见问题
关于 dsh agent loop 的简短回答,细节都在上文。
dsh 的 agent loop 到底是什么?
就是默认驱动器 core/agent-loop:把你的输入变成模型请求和工具调用,一个 step 接一个 step 地循环,直到这个 turn 没有欠账为止。
turn 和 step 有什么区别?
step 是一次模型请求加它调用的工具;turn 是零个或多个 step——领取输入时开启,没有欠账时关闭。一个 turn 里可以有很多个 step。
插件是怎么接入 agent loop 的?
通过事件。agent/pre-step 把守输入,agent/request 和 llm/stream 贴着模型调用,三个 tools/* 事件包住每次工具执行,agent/turn-stopping 可以终止 turn。瀑布类监听器必须调用 next() 放行。
agent loop、subagent、Agent Teams,我需要哪个?
单个会话的活,只需要 loop。任务值得独立上下文时派 subagent(它自带 loop);多个 agent 要共享花名册、任务板和邮箱,才上 Agent Teams。
agent loop 会失控一直跑吗?怎么防?
loop 本身在没有欠账时会结束,但不断注入输入的插件能让它一直活着。官方讨论 #4819 记录了真实案例;标准 guard 是轮次上限、无进展检测、同错熔断和显式出口。
看懂 loop 之前要先学 Cordis 吗?
不用。turn 流程本身自洽,事件名和顺序够写大多数插件了。要替换服务(比如换掉 loop 驱动器本身)时才真正需要 Cordis。
继续阅读
和 loop 同层或直接使用它的指南:子代理、模式、预设与上下文压缩。
DeepSeek Harness 子代理与多智能体实战
一次拉起五个子代理并行干活:会话头部逐个监控、完成报告逐个读、按子代理指定模型,截图逐步对齐
阅读指南四种模式:Standard、PTC、Minimal、Creator
每种 agent preset 包含什么、什么场景用哪种,以及怎么在 Creator 模式里做自己的 preset。
阅读指南打造自定义 DSH Agent 预设
用创造模式设计专属的 DeepSeek Harness 预设:配插件、批准写入、一键切换工作台——逐帧核实的实战教程。
阅读指南dsh 系统提示词是怎么装配的
拆解 DeepSeek Harness 每轮注入的系统提示词:段落顺序、插件注入点、persona 挂钩,以及改造它的三类插件
阅读指南来源与证据
页面机制均以官方架构文档、讨论帖 #4819 和 v0.2.1-alpha.1 发布说明为依据;涉及版本的行为以官方 changelog 为准。Loop Guard 不变量来自社区协议。
