返回文章

Thinking Notes

一个请求的一生:DeepSeek Harness 主流程图解

把 DeepSeek Harness 想成一家「只认病历」的医院:挂号、接诊、分诊、调档、问诊、送检、循环、结诊,八个站点串成一条链。配一册双语动画,讲清主流程每一步解决什么问题,以及那条铁律——凡进模型的,必在病历。

DeepSeek Harness 主流程The DeepSeek Harness main loop

一个请求的一生:
挂号结诊
The life of a request:
from check-in to check-out

上一篇拆的是理念,这一篇拆机器。我把 DeepSeek Harness 的主流程,想成一家「只认病历」的医院,拆成八站:申请怎么递进来、就诊怎么登记、哪道分诊能拦、病历怎么调、话怎么记、检查怎么送、就诊怎么结。每一站都回答同一个问题:它替「可靠」挡掉了什么风险。 The last piece covered the philosophy; this one opens the machine. The DeepSeek Harness main loop becomes a hospital that trusts only its charts, with eight stations: how requests arrive, how visits open, which triage can stop everything, how history is pulled, how every word is recorded, how tests are sent out, and how the visit finally closes. Each station answers one question: what risk does it remove from "reliable"?

一条铁律先放在这里:凡进模型的,必在病历。模型看到的每一样东西,都必须能从会话日志里重建——整条主流程,都是为这条铁律服务的。 One iron rule up front: nothing reaches the model that isn't in the chart. Everything the model sees must be reconstructable from the session log — the entire main flow exists to serve that rule.
挂号处desk 登记admit 分诊triage 病历chart 问诊consult 送检order 循环loop 结诊close 还欠结果,或来了新申请 → 回到分诊,再来一步results owed or fresh requests → back to triage, another step

这张图就是全文的地图:点任意站点直达对应章节;后面每章读的就是图上的一站。 This picture is the map for the whole piece: click any station to jump to its chapter — each chapter that follows is one stop on this line.

病历 · 只增不改chart · append-only 口头交班 · 说过就散verbal handoff · not kept 交班时说定的事,写回病历才算发生a handoff decision counts once it is charted SESSION LOG · APPEND-ONLYSESSION LOG · APPEND-ONLY
图 00 · 病历与口头交班Fig. 00 — The chart & the handoffDHF / PROLOGUE

00PROLOGUE · 两条记录线PROLOGUE · TWO RECORD LINES

病历,和口头交班The chart, and the handoff

先看懂这家医院的规矩,后面八站才看得懂。院里有两种记录:一种是病历——一支笔从头写到尾,只增不改,每一笔都留着;另一种是走廊里的口头交班——医生们商量病情、拦下申请、临时调度,说过就散。病历是给「事后」用的:第二天新来的医生照着接手,靠的全是它。口头交班是给「当下」用的:商量归商量,最终定了什么,必须写回病历才算数 Understand the house rules first, and the eight stations read themselves. There are two kinds of records: the chart — one pen, append-only, every entry kept; and the hallway handoff — where doctors argue, intercept, and coordinate, spoken and gone by evening. The chart serves "afterwards": the next doctor picks up the exact same case tomorrow from it alone. The handoff serves "now": whatever it settles must be written into the chart to count.

代码对照病历 = 会话日志(session log,turn / step / message / tool 全是追加事件);口头交班 = 实时扩展点(agent/*tools/* 事件,不落盘)。铁律「模型可见 ⟺ 已记录」就是把口头交班钉死在病历上。 In the harnessThe chart = the session log (turn / step / message / tool are appended events); the verbal handoff = live extension points (agent/* and tools/* events, never persisted). The rule "model-visible ⟺ logged" nails the handoff to the chart.

朱批以为循环里藏着什么内存状态、隐藏上下文。没有。历史不在谁脑子里,只在病历上——这也是后面每一站的起点。 Mythassuming the loop keeps hidden state or secret context somewhere. It doesn't. History lives in nobody's head — only in the chart. Every station below starts from this.

followup · 挂号并叫号followup · check-in & call steer · 诊间补充steer · mid-visit inject · 只递材料inject · materials only 四条通道,一个窗口:send / followup / steer / injectfour channels, one window: send / followup / steer / inject 打断有代价,不是每条输入都配得上interruption has a cost — not every input deserves it
图 01 · 挂号处Fig. 01 — The registration deskDHF / STATION 01

01WAKE · 挂号处WAKE · THE REGISTRATION DESK

申请怎么递进来How requests arrive

一切从挂号处开始。递申请有四种方式:followup 是正式挂号并叫号——医生立刻接;steer 是就诊过半时补进来的话,在最近的节点生效;inject 是只递材料、不叫号的背景资料,静静躺在窗口里等下一次登记取走;send 是前三者的总原语,连递进哪个窗口、叫不叫号,都由它定。分这么细,是因为「打断」是有代价的——不是每条输入都配得上立刻打断医生手上的活 Everything starts at the registration desk. Requests arrive four ways: followup is a formal check-in that gets called — the doctor takes it at once; steer is added mid-visit and lands at the nearest node boundary; inject drops off background materials without a call, waiting quietly to be taken at the next registration; send is the primitive beneath all three — it decides which window a request lands in and whether it gets called. Four ways, because interruption has a cost — not every input deserves to interrupt the doctor's live work.

代码对照Agent 句柄上的 followup / steer / inject / send 四个方法。唤醒型输入把驱动器从 idle 拉起(状态灯变 running);inject 攒着,等下一趟 pre-step 一起取走。 In the harnessThe followup / steer / inject / send methods on the Agent handle. Waking input flips the driver from idle to running; inject accumulates until the next claim.

朱批以为 inject 会立刻生效。它不叫号——没人再来挂号的话,它就一直躺在窗口里。 Mythexpecting inject to take effect immediately. It never gets called — if nobody checks in again, it just keeps waiting at the window.

已取走CLAIMED 登记即取走:被拒不回窗口,但拒收留痕registration removes: rejections never return, but are recorded TURN/START · AGENT/INBOX/CLAIMEDTURN/START · AGENT/INBOX/CLAIMED
图 02 · 登记即取走Fig. 02 — Registration removesDHF / STATION 02

02OPEN · 登记即取走OPEN · REGISTRATION REMOVES

登记就诊,把申请从窗口取走Open a visit, take the requests out

医生被叫到,第一件事是登记就诊:turn/start 落档,然后把窗口里「欠下一段活的输入 + 一条正式消息」取走。注意「取走」两个字——登记是删除性的,申请离开窗口就不回去。这样设计是为了不重不漏:一份申请要么还没被取、要么正在处理、要么已经进了病历,永远不会出现「窗口里还在、事也办了」的双重记录。哪怕这次就诊后来一步都没走到(比如申请被分诊全拒了),结档时病历里仍留着「登记过这么一次」的记录——尝试本身就是事实 Called, the doctor first registers the visit: turn/start lands in the chart, then the driver takes the pending next-step inputs plus one queued message. Note the word take — registration is deletion. A taken request never returns to the window, so a request is always in exactly one of three states: waiting, in progress, or in the chart — the nightmare of "still at the window but already handled" cannot happen. Even a visit that ends with zero steps still leaves a record that it was registered — the attempt itself is a fact.

代码对照turn/start 落档;claim 发 agent/inbox/claimed;被拒时关闭一个零 step 的 turn,日志照样留痕。 In the harnessturn/start persists; claims emit agent/inbox/claimed; a rejected batch closes a zero-step turn that the log still records.

朱批把 turn 等同于「一次问答」。turn 是「一段不欠工作的时间」——它可以包含很多段 step,也可以一段都没有。 Mythequating a turn with one Q&A. A turn is "a span that owes nothing" — it can hold many steps, or none at all.

放行enter 改写rewrite 拒绝reject 病历太厚?先剪枝、再摘要点,尾巴上留几笔chart too thick? prune, summarize, keep the tail AGENT/PRE-STEP · DSH-COMPACTION-BASICAGENT/PRE-STEP · DSH-COMPACTION-BASIC
图 03 · 分诊台Fig. 03 — The triage deskDHF / STATION 03

03GATE · 分诊台GATE · PRE-STEP TRIAGE

每一步之前,先过分诊台Before every step, one triage

申请取走后不直接见医生,先过分诊台:pre-step。这是全流程权限最大的一站——监听者可以原样放行,可以改写申请上的内容,也可以整批拒收;还能声明「从这里开始换一个病区」,让后面的请求另起一套序列。压缩也挂在这道关上:病历厚过警戒线(默认八成)时,先把旧的检查报告剪一剪,再请医生摘一页要点,尾巴上留最后几条原文。为什么把关口放在「调档之前」?因为只有在调档之前动手,省下来的才是真省——等调完再精简,钱已经花出去了 Taken requests don't see the doctor directly; they pass triage first: pre-step. This is the most powerful station in the flow — a listener may pass the batch as-is, rewrite the requests, reject the whole batch, or declare a fresh ward from here. Compaction hangs on this gate too: when the chart crosses the pressure line (80% by default), old test reports are pruned, the doctor writes a summary page, and the last few entries stay verbatim. Why triage before pulling the chart? Because savings only count before the pull — compact after, and the tokens are already spent.

代码对照agent/pre-step waterfall(监听者必须调 next() 才算放行)。压缩是 dsh-compaction-basic,阈值 0.8、保留尾部约 16%;被整批拒绝时关一张零 step 的 turn。 In the harnessThe agent/pre-step waterfall (listeners must call next() to pass). Compaction is dsh-compaction-basic at threshold 0.8 with a ~16% retained tail; a fully rejected batch closes a zero-step turn.

朱批把上下文溢出和这道闸混为一谈。常规压力在分诊台处理;真正的「病历爆了」是另一条失败通道(请求错误)接管的。 Mythconflating routine pressure with overflow. Triage handles pressure; a genuinely blown context is handled on a separate failure path (request-error).

本次请求 · 连参数一起登记this request · parameters logged too request/headerrequest/header 病史 = 从病历现场调,不靠记性history = pulled from the chart, never from memory DERIVEMESSAGES() · SYSTEM-PROMPT/ASSEMBLEDERIVEMESSAGES() · SYSTEM-PROMPT/ASSEMBLE
图 04 · 调档Fig. 04 — Pulling the chartDHF / STATION 04

04DERIVE · 病史从病历来DERIVE · HISTORY FROM THE CHART

医生从不靠记性The doctor never relies on memory

过了分诊,开始调档:把提示词片段、工具清单组装好,然后从病历里「现场调」出本次请求需要的全部病史——不是从某个内存变量里拿,是沿病历从头投影一遍。为什么这么绕?因为医生(模型)本来就没有记性:每一轮请求都是独立的,所谓「上下文」完全是送过去的东西。既然必须调,那就只认病历这一个来源——重启了、分叉了、换台机器了,只要病历在,同一个病史就调得出来。连「这次请求用了什么参数」也登记成一条档(request/header),以后审计时能对上每一笔。 Past triage, the pulling begins: assemble prompt sections and the tool list, then project the request history straight from the chart — not from some in-memory variable, but by folding the record from the top. Why so roundabout? Because the doctor has no memory: every request is independent, and "context" is entirely whatever gets sent. Since pulling is unavoidable, there is exactly one source to pull from — the chart. Restart, fork, move machines: the chart alone reproduces the same history. Even the request's own parameters are logged (request/header) so every audit reconciles later.

代码对照deriveMessages() 从会话日志投影模型历史;system-prompt/assemble 组装片段与工具 schema;request/header 事件记录 initial / resume / change / series 四种缘由。 In the harnessderiveMessages() projects model history from the session log; system-prompt/assemble gathers sections and tool schemas; request/header logs why — initial / resume / change / series.

朱批以为会话状态存在程序里,重启就丢。状态在病历里;程序只是个调档的。 Myththinking session state lives in the process and dies on restart. State lives in the chart; the process just pulls it back up.

空也记empty → logged 逐句落档 + 对外播报;没说话也记,注明缘由every chunk charted and broadcast; empty replies recorded ASSISTANT/CHUNK · ASSISTANT/MESSAGEASSISTANT/CHUNK · ASSISTANT/MESSAGE
图 05 · 逐句录入Fig. 05 — Recorded as spokenDHF / STATION 05

05STREAM · 逐句录入STREAM · RECORDED AS SPOKEN

医生开口,病案员同时做两件事The doctor speaks; the records clerk does two things

医生开口说话,话是一段一段传回来的。病案员(驱动器)同时做两件事:把每一句立刻写进病历,并且对外播报——界面上的打字机效果,就是听播报来的。全部说完,再写一笔正式的「本次记录」。哪怕医生这次一个字没说,或者说到一半被长度截断,这笔记录也照样写,写明用量、关联到对应的每一句。这么做的原因只有一个:「模型这次到底回了什么、花了多少」,必须是病历里能查到的事实,而不是界面刷新后就没影的东西 The doctor speaks, and the words stream back piece by piece. The records clerk (the driver) does two things at once: every sentence goes into the chart immediately, and it is announced outside — the typing effect you see is just listening to the announcements. When the reply ends, one formal summary entry lands. Even if the doctor said nothing at all, or was cut off mid-sentence, the entry still lands — with usage, linked to the exact sentences. One reason only: what the model actually returned, and what it cost, must be a fact findable in the chart, not something that vanishes when the UI refreshes.

代码对照assistant/chunk 逐条落档并经 session/event 广播;assistant/message 记录每次成功调用,空内容与 max-tokens 也记,带用量与 sourceEventSeqs In the harnessassistant/chunk events persist and broadcast via session/event; assistant/message lands per successful call — empty and max-tokens replies included, with usage and sourceEventSeqs.

朱批以为「空回复」是异常状态不用记。空回复是事实,记了才能分清「模型没说话」和「系统没送到」。 Mythtreating empty replies as non-events. An empty reply is a fact; recording it is what distinguishes "the model said nothing" from "the system failed to deliver".

急查 · 屏障exclusive · barrier 常规 · 排队池(最多 N 个在外)parallel · queue pool (N out) 1 2 3 并发求快,顺序求稳 — 报告按开单次序归档concurrency buys speed; reports file in order issued EXECUTIONMODE · MAXPARALLELTOOLCALLSEXECUTIONMODE · MAXPARALLELTOOLCALLS
图 06 · 屏障与排队池Fig. 06 — Barriers & the queue poolDHF / STATION 06

06DISPATCH · 屏障与排队池DISPATCH · BARRIERS & THE QUEUE POOL

检查送出去,报告按序归Orders go out; reports file in order

医生在答话里开了几张检查单(工具调用)。院里按缓急分两路:急查(独占类)成屏障——它不出结果,别的谁都别动;常规(并行类)进排队池——最多 N 个科室同时在做,出一个结果立刻补位,位子永远不空着。每个科室的流程是一样的:先登记开单(tool/call),执行前有核对(pre-execute),做的时候可以自己记笔记,出结果后有核验(post-execute),最后报告归档(tool/result)。最讲究的一处:科室们完成有先有后,但报告按开单时的顺序归档——医生下一轮看到的次序,和他开单时的次序一模一样。并发是为了快,顺序是为了不乱 Inside its reply, the doctor writes out test orders (tool calls). The hospital routes them by urgency: exclusive orders become a barrier — nothing else moves until it's done; parallel orders enter a queue pool — at most N departments working at once, a slot refills the moment one finishes. Every department follows the same round: the order is logged (tool/call), a check before starting (pre-execute), notes taken en route, a check on completion (post-execute), and the report filed to the chart (tool/result). The finest detail: departments finish in any order, but reports file in the order they were ordered — what the doctor sees next round matches exactly what was ordered. Concurrency buys speed; order keeps it sane.

代码对照executionMode 分类,exclusive 成屏障、parallel 走 maxParallelToolCalls 排队池,开跑前还会重新分类;tool/calltool/result 成对落档,结果按模型顺序提交。 In the harnessCalls classify by executionMode — exclusive barriers, a rolling pool capped by maxParallelToolCalls, reclassified before start; tool/call and tool/result persist as pairs, committed in model order.

朱批以为并行执行等于乱序提交。并发只发生在「办事」那一段;写回病历那一刻,顺序是死的。 Mythassuming parallel execution means out-of-order results. Concurrency exists only during execution; at filing time, order is absolute.

STEP/N 结诊前核查final check 结诊理由close reason completed max-tokens blocked error aborted CLOSED 先核查,再结诊 — 理由写在病历,事后可查final check, then close — every ending recorded AGENT/TURN-STOPPING · TURN/ENDAGENT/TURN-STOPPING · TURN/END
图 07 · 循环与结诊Fig. 07 — Closing the visitDHF / STATION 07

07LOOP · 循环与结诊LOOP · CLOSING THE VISIT

一段接一段,直到不欠任何工作Step after step, until nothing is owed

一步收尾(step/end)之后,只剩一道判断题:还欠结果吗?欠,就再来一步——要么医生开了新的检查单要回填报告,要么窗口里来了新申请(比如就诊过半时的补充交代)。不欠了,也不立刻结诊:先做结诊前的最后一次核查(turn-stopping),这是任何人最后能喊停的地方。核查通过,结诊:turn/end 落档,结诊理由写得明明白白——正常看完、说到一半被长度截断、被分诊拦下、请求出错、还是被人叫停。写理由,是因为翻病历的人不该靠猜——事后复盘时,每一种收场都是查得到的 When a step ends (step/end), one question remains: are results still owed? If yes, another step — either the doctor's new test orders need their reports fed back, or fresh input arrived (a mid-visit steer, say). If no, the visit still doesn't close immediately: first a final check (turn-stopping), the last place anyone may halt the visit. Past it, check-out: turn/end lands with its reason written plainly — completed, max-tokens, blocked, error, or aborted. The reason is written because no one reading the chart should have to guess — every ending is lookable-up.

代码对照step/end → 判断「欠回填或有新输入」→ 下一个 step;自然停止且 inbox 空时走 agent/turn-stopping(serial,无 next())→ turn/end 带 reason → 状态灯回 idle。 In the harnessstep/end → "results owed or input queued?" → next step; a natural stop with an empty inbox passes agent/turn-stopping (serial, no next()) → turn/end with its reason → status back to idle.

朱批把 turn/end 的 max-tokens 当成系统出错。它是一种被如实记录的收场——错的是「没记录」。 Mythreading a max-tokens close as a malfunction. It's an honestly recorded ending — the malfunction is not recording it.

把八站串成一条链Eight stations, one chain

八站回头再串一遍:申请进窗,登记取走,过分诊,调病历,问诊逐句录入,送检报告归档,一步接一步,核查结诊。八站走完,每一站挡掉的风险都不一样—— Run the chain once more: requests into the window, taken not copied, through triage, history pulled from the chart, speech recorded as spoken, orders sent and reports filed in order, step after step, final check then close. Each station kills a different risk —

00病历与交班Chart事后可重建RECONSTRUCTIBLEreconstructable afterwardsRECONSTRUCTIBLE
01挂号处Check-in打断的代价INTERRUPTION COSTinterruption costINTERRUPTION COST
02登记Register重复处理DOUBLE HANDLINGdouble handlingDOUBLE HANDLING
03分诊Triage浪费与捣乱WASTE & MISCHIEFwaste & mischiefWASTE & MISCHIEF
04调档Pull重启即失忆AMNESIA ON RESTARTamnesia on restartAMNESIA ON RESTART
05录入Record回放失真UNFAITHFUL REPLAYunfaithful replayUNFAITHFUL REPLAY
06送检Order并发致乱CONCURRENCY CHAOSconcurrency chaosCONCURRENCY CHAOS
07结诊Close事后靠猜GUESSWORK AFTERguesswork after the factGUESSWORK AFTER
判断:这条主流程没有一步靠「聪明」取胜,全部靠「诚实」取胜:记录比记忆可靠,病历比口头交班可靠,写明理由比保持沉默可靠。所谓 harness,就是把这个偏好写成了流程。 Verdict: no station in this loop wins by being clever; all of them win by being honest: records over memory, the chart over the hallway, stated reasons over silence. A harness is simply this preference, written as a procedure.
  1. 01两条线:哪些事进病历(持久),哪些事只在口头交班里(实时)?分清了吗?Two lines: what goes to the chart (persistent) versus the hallway handoff (live)?
  2. 02通道:你的输入该用哪种递法——打断(followup/steer),还是只递材料、不叫号(inject)?Channels: does your input deserve a call (followup/steer) or a silent drop-off (inject)?
  3. 03分诊:进模型之前,有没有一道能改写、能拒绝、能压缩的关?Triage: before the model, is there a checkpoint that can rewrite, reject, and compact?
  4. 04投影:模型历史是从唯一真源调出来的,还是散落在内存里?Derive: is model history projected from one source of truth, or scattered in memory?
  5. 05录入:流式片段、空回复、请求参数——这些「不起眼的事实」有记录可查吗?Record: are humble facts — chunks, empty replies, request parameters — findable in a log?
  6. 06送检:并发的工具调用,结果有没有按模型看到的顺序提交?Orders: do parallel tool results commit in the order the model issued them?
  7. 07结诊:每一轮的结束方式,事后能查到理由吗?Close: can every turn's ending be looked up with its reason afterwards?
边界:这篇拆的是 DeepSeek Harness 的循环设计,不是「唯一的正确做法」。但那条铁律值得搬走:无论你自己怎么搭 agent 系统,「凡进模型的,必在病历」——做不到这一点的系统,出问题时你连现场都没有。 Boundary: this is the DeepSeek Harness loop, not the only correct design. But the iron rule travels: whatever you build, "nothing reaches the model that isn't in the chart" — without it, when things break, you won't even have a scene to inspect.

DEEPSEEK HARNESS · MAIN LOOPDEEPSEEK HARNESS · MAIN LOOP