我按下“发送”之后:DeepSeek Harness 如何把一次请求变成 1,177 个增量片段
ææä¸âåéâä¹åï¼DeepSeek Harness å¦ä½æä¸æ¬¡è¯·æ±åæ 1,177 个å¢éçæ®µ
ovO 2026-08-15 0 é 读13åéå¾å¤äººæâæµå¼åçâæ¦æ¬æä¸å¥è¯ï¼å端åèµ· SSEï¼è¯·æ±ä¿æè¿æ¥ï¼æ¨¡åä¸è¾¹çæä¸è¾¹è¿åã
æåæ¥ä¹ä¼è¿æ ·è§£éãä½å½ææ²¿ç DeepSeek Harness ççå®ä»£ç èµ°å®ä¸æ¬¡è¯·æ±ï¼å亲æå¯å¨ Web profileãåéæ¶æ¯ãå¯¼åº Session æ¥å¿åï¼æåç°è¿ä¸ªè¯´æ³åªæè¿°äºä¸é´ä¸æ®µï¼èä¸ä¼æ©çç³»ç»æéè¦ç设计ï¼DeepSeek Harness çæµå¼åç䏿¯ä¸æ¡è´¯ç©¿æµè§å¨å模åçé¿è¿æ¥ï¼èæ¯ä¸æ®µä¸åèè´£çééã
- æµè§å¨ç¨ä¸æ¬¡ççå½å¨æç HTTP POSTï¼æç¨æ·æå¾äº¤ç» Harnessã
- Harness ç¨ SSE ä» DeepSeek API æ¥æ¶æ¨¡åå¢éã
- Harness å ææ¯ä¸ªå¢éåæ Session äºä»¶ï¼åéè¿ WebSocket æ¨ç»æµè§å¨ã
è¿ä¸æ®µä¹é´ä¸æ¯ç®å转åãä¸é´ç Session äºä»¶æ¥å¿æ¢æ¯å®æ¶å¹¿ææºï¼ä¹æ¯æ¢å¤ã轨迹ãç»è®¡åæç»æ¶æ¯çå ±åäºå®æ¥æºãçè§£è¿ä¸ç¹ï¼æç®çæ£çè§£ DeepSeek Harness çâæµå¼âã
ä¸ãå å䏿¬¡çå®è¿è¡ï¼è䏿¯åªç代ç ç
æä»æºç å¯å¨ Web profileï¼è®©ç³»ç»èªå·±éæ©ç©ºé²ç«¯å£ï¼
node --import tsx/esm apps/cli/src/bin.ts web --host 127.0.0.1 --port 0
è¿ç¨ç»åºçå¯ä¸å¯å¨æ¥å¿å¾å å¶ï¼
dsh web: http://127.0.0.1:57960
éåæå¨çå®é¡µé¢ä¸å建ä¼è¯ï¼è¾å ¥ä¸é¢è¿æ¡æ¶æ¯ï¼
请ç¨ä¸æ®µè¯è§£é DeepSeek Harness ä¸ä¸æ¬¡æµå¼åçä»è¯·æ±ååºå°å¢éå±ç¤ºçè¿ç¨ãä¸è¦è°ç¨å·¥å ·ï¼æ¯æ®µä»¥ãé¶æ®µä¸ããé¶æ®µäºããé¶æ®µä¸ãå¼å¤´ï¼æåè¾åºãæµå¼æ¼ç¤ºå®æãã
请æ±å®æåï¼é¡µé¢ç»åºç宿µææ æ¯ï¼
| ææ | 宿µå¼ |
|---|---|
| 模å | DeepSeek-V4-Flashï¼High |
| åå / æ¥æ° | 1 è½® / 1 æ¥ |
| LLM ç¨æ¶ | 15.5 s |
| é¦ token | 0.8 s |
| çæéç | 80 tok/s |
| è¾å ¥ token | 9.2K |
| è¾åº token | 1.2K |
| ç¼åå½ä¸ | 0% |
è¿éæä¸ä¸ªå¿ é¡»å 说æçç»èï¼æªå¾ä¸ç模ååçææµè§å¨é¾è·¯æ¦æ¬æäº SSEãè¿æ®µåçæ¯å®éªè¾åºï¼ä¸æ¯æ¶æè¯æ®ã æºç åæµè§å¨ç½ç»è®°å½é½è¡¨æï¼æµè§å¨å Harness åéæ¶æ¯ä½¿ç¨ HTTP POSTï¼åçå¢éä» Harness å°æµè§å¨ä½¿ç¨ WebSocketï¼åªæ Harness å° DeepSeek API çæ¨¡åååºä½¿ç¨ SSEã让模åè§£éèªå·±ç宿主并ä¸çäºå®ææºç éªè¯ã
äºãçæ£çç»æï¼ä¸æ¬¡ä¸è¡ï¼ä¸¤æ¡ä¸è¡
æå®æ´é¾è·¯ç»åºæ¥åï¼ä¸ä¸ªééçè¾¹çéå¸¸æ¸ æ¥ï¼
| éé | æ¹å | è½½ä½ | è´è´£ä»ä¹ |
|---|---|---|---|
| ç¨æ·å½ä»¤ä¸è¡ | Browser â Harness | POST /api/session.prompt | æäº¤æ¶æ¯å¹¶è·å¾âå·²æ¥æ¶âç»æ |
| 模åå¢éä¸è¡ | DeepSeek API â Harness | SSEï¼text/event-stream | è¿å reasoningãæ£æãå·¥å ·åæ°ãusage å finish |
| Session äºä»¶ä¸è¡ | Harness â Browser | ws://.../api/events.mux | æ¨éå·²ç»è¿å ¥ Session çäºä»¶ |
æµè§å¨ä¸ä¼æä¸ä¸ª HTTP è¯·æ±æ 15 ç§ççæ¡ãå®å 宿䏿¬¡æ®é RPCï¼Harness æ¥ç®¡åç»æ§è¡ï¼åæäºä»¶éè¿å·²åå¨ç WebSocket ä¸è¡è¿æ¥æ¨åæ¥ã
æä»æµè§å¨è®°å½ä¸çå°çå®é è¯·æ±æ¯ï¼
POST /api/session.create -> 200 OK
POST /api/session.history -> 200 OK
POST /api/session.prompt -> 200 OK
å·æ°é¡µé¢å¹¶ç嬿°å»ºè¿æ¥æ¶ï¼æµè§å¨æå¼äºï¼
ws://127.0.0.1:57960/api/events.mux
ws://127.0.0.1:57960/api/events.host
å
¶ä¸ events.mux æ¿è½½å Session çäºä»¶ï¼events.host æ¿è½½ Session å建ã鿝ãè¿è¡ç¶æç Host 级信æ¯ãåç token èµ°çæ¯åè
ã
ä¸ãç¬¬ä¸æ®µï¼æç¹å»åéï¼æµè§å¨åªè´è´£â交æ£â
åéæé®èå并䏿¯âå¼å§è¯»åæ¨¡åæµâï¼èæ¯è°ç¨å®¢æ·ç«¯ Session ç prompt()ã
客æ·ç«¯å
忥æ promptAttempted åé¦è½® pending ç¶æåè¿æ¬å°ç¶æï¼åè¿è¡ç¬¬ä¸æ¬¡ awaitãè¿è®©çé¢å¯ä»¥ç«å³è¿å
¥âæ£å¨å¤çâç¶æï¼ä¸å¿
çç½ç»å¾è¿ãéåå®è°ç¨ api.sessions.prompt()ï¼æºå¸¦ï¼
sessionIdmodeï¼queueæsteer- ææ¬æå¾çå 容
- æµè§å¨è§£æåºçæ¶åº
åºå± callUnary() 为请æ±çæ rpcIdï¼æå®å°è£
æ client-requestï¼ååéï¼
POST /api/session.prompt
Content-Type: application/json
ååºå¿
须忾åä¸ä¸ª rpcIdï¼å¦å客æ·ç«¯ç´æ¥æå®è§ä¸ºåè®®é误ã
Host æ¶å°è¯·æ±åï¼ä¼è§£ææ¶åºãæ¾å°ææ¢å¤ç®æ Agentãæå¾ç转æ¢ä¸ºå¯æä¹
åéä»¶ãå建 UserMessageï¼æåæ ¹æ®æ¨¡å¼è°ç¨ agent.followup() æ agent.steer()ãæåååºåªæ¯ï¼
{ "accepted": true }
è¿ä¸ª 200 OK è¡¨ç¤ºâæ¶æ¯å·²ç± Agent æ¥ç®¡âï¼ä¸è¡¨ç¤ºâ模åå·²ç»åçå®âãè¿ä¸æ¥ææµè§å¨äº¤äºä¸å¯è½æç»å åç§ãå¯è½å å«å·¥å ·è°ç¨åå¤ step ç Agent æ§è¡è§£è¦äºã
åãç¬¬äºæ®µï¼Agent Loop ç»è£ 请æ±ï¼DeepSeek Adapter æå¼ SSE
Agent å¼å§ step åï¼å
ä»å½å Session æ¨å¯¼å岿¶æ¯ï¼åç»è£
ç³»ç»æç¤ºè¯ãå·¥å
·å®ä¹ã模åéæ©åéæ ·åæ°ãæç»å¾å°ç»ä¸ç GenerateOptionsï¼äº¤ç» LLM Runtime æ provider éæ©éé
å¨ã
DeepSeek Adapter æç»ä¸è¯·æ±åºåå为 Chat Completions 请æ±ãä¸¤ä¸ªåæ®µå³å®äºååºä¸æ¯ä¸æ¬¡æ§ JSONï¼
{
"stream": true,
"stream_options": {
"include_usage": true
}
}
ç¶å Host ç´æ¥è¯·æ±ï¼
POST {baseURL}/chat/completions
Authorization: Bearer ...
Content-Type: application/json
Accept: text/event-stream
API Key åªå¨ Host ä¾§è§£æå使ç¨ï¼ä¸éè¦ä¸åç»æµè§å¨ã
è¿éçååºææ¯æ åæä¹ä¸ç SSEãDeepSeek Adapter 没æèªå·±æåå符串ååï¼èæ¯è®© eventsource-parser è´è´£ï¼
- ä»»æç½ç»ååä¸çäºä»¶éç»
- UTF-8ãCRLF å BOM å¤ç
- å¤ä¸ª
data:è¡æ¼æ¥ - comment ä¸é data åæ®µè¿æ»¤
- 空è¡ç»æ¢ä¸ä¸ª SSE event
è§£æå¨éä¸ªäº§åº data å
容ï¼å¹¶æåé¢é [DONE] ä½ä¸ºç»æ¢å¨å
µãå¦æè¿æ¥å¨ [DONE] åç»æï¼Harness ä¸ä¼æåæªåçåå
æåï¼èæ¯æåº STREAM_CLOSEDã
äºãSSE JSON 䏿¯ç´æ¥æç»å端ï¼èæ¯å ç¿»è¯æç»ä¸äºä»¶
DeepSeek è¿åçæ¯ä¸ª SSE data æ¯ provider åè®®ãHarness è¿è¦æå®ç¿»è¯æ provider æ å
³ç StreamChunkï¼
| DeepSeek å¢é | Harness StreamChunk |
|---|---|
| 馿¬¡åºç° reasoning | block-start(reasoning) |
reasoning_content | reasoning-delta |
| 馿¬¡åºç°æ£æ | block-start(text) |
content | text-delta |
| å·¥å ·è°ç¨åæ°ç段 | tool-call-delta |
| å宿 | block-end |
| token ç»è®¡ | usage |
| 宿åå | finish |
è¿ç§âå + å¢éâçæ¨¡åæ¯ä¸ä¸²çº¯ææ¬æ´éè¦ãå®å 许 reasoningãæ£æåå¤ä¸ªå·¥å ·è°ç¨åæ¶åå¨ï¼å¹¶è®©å端ç¥éæ¯ä¸ªç段åºè¯¥è¿½å å°åªä¸ª blockï¼è䏿¯é çæµææ¬æ ¼å¼ã
finish_reason å usage ä¸ä¼ä¸åºç°å°±ç«å»å°å£ãAdapter ä¼çå° [DONE]ï¼ä¾æ¬¡è¡¥åºææ block-endãææ° usage åå¯ä¸ç finishï¼ä¿è¯ finish åä¸ååºç°æ° chunkã
å ãæå ³é®çä¸è¡ï¼æ¯ä¸ª chunk å è¿å ¥ Session
Agent Loop æ¶è´¹æ¨¡åæµæ¶ï¼æ ¸å¿é¡ºåºå¯ä»¥æ¦æ¬æï¼
for await (const chunk of stream) {
chunkSeqs.push(session.append('assistant/chunk', { turn, step, chunk }).seq)
assembler.push(chunk)
}
æè®¤ä¸ºè¿æ¯æ´æ¡é¾è·¯æå¼å¾è®°ä½ç设计ã
å®ä¸æ¯å
æ´æ° UIãç»æååè¡¥æ¥å¿ï¼ä¹ä¸æ¯å
æå®æ´çæ¡æå¨å
åä¸ãæ¯ä¸ªæ¨¡åå¢éå
æä¸º assistant/chunk Session äºä»¶ã Session.append() 忥宿åä»¶äºï¼
- 为äºä»¶åé
è¿ç»ç
seqã - åå
¥æ¯«ç§çº§
timeã - æäºä»¶å å ¥å åä¸çè§èæ¥å¿ã
- 忥éç¥
session/eventè§å¯è ã
æä¹
åæä»¶çå¬åä¸ä¸ª session/eventï¼æå»ç»åçäºä»¶æ¾è¿å¼æ¥åéåï¼çè·¯å¾ä¸ä¼çå¾
ç£ç I/OãAPI Proxy 乿¯è§å¯è
ï¼å®æäºä»¶å°è£
æï¼
{
type: 'session/event',
sessionId,
event
}
ç¶ååå
¥ events.mux ä¸è¡éåã
è¿å¸¦æ¥ä¸ä¸ªéå¸¸å¼ºçæ§è´¨ï¼å®æ¶ UIãæä¹ æ¥å¿ã轨迹è§å¾åæç»æ¶æ¯é½è§å¯å䏿¹äºä»¶ï¼æ²¡æä¸å¥âç»å端ççæµâåå¦ä¸å¥âäºåæ¼åºæ¥çæ¥å¿âã
ä¸ãç¬¬ä¸æ®µï¼WebSocket æ¶äºä»¶ï¼æµè§å¨æå¨ç»å¸§åå¸
Web 客æ·ç«¯ä¸º events.mux 建ç«ä¸è¡ WebSocketãæ¯ä¸ªææ¬ frame å°è¾¾åï¼å®å
è§£æ RPC envelopeï¼åæ ¡éª MuxFrameãç¸å½¢ frame ä¼è¢«ä¸¢å¼å¹¶è¾åºè¯æï¼ä¸ä¼æ±¡æå®¢æ·ç«¯ç¶æã
å¯¹è¯æå½±æ¶å° assistant/chunk åï¼æ chunk ç±»åæ´æ° blockï¼
text-deltaï¼è¿½å å°æ£æ blockãreasoning-deltaï¼è¿½å å° reasoning blockãtool-call-deltaï¼è¿½å å·¥å ·åæ°ï¼å¹¶ä¿ç call id åå·¥å ·åãblock-endï¼ç¨å®æ´ block å°å£ãusageï¼æ´æ° token ç»è®¡ã
å¼å¾æ³¨æçæ¯ï¼Harness 没æå¼ºè¿« React 为æ¯ä¸ª token åç¬æ¸²æãæ®é chunk çåå¸çç¥æ¯ animation-frameï¼äºä»¶ä»ç¶é个è¿å
¥ç¶æï¼ä½åä¸å¸§å
çå¤ä¸ªæ´æ°å¯ä»¥åå¹¶ååç»å¶ãè¿æ ·æ¢ä¿ç精确äºä»¶é¡ºåºï¼åé¿å
é« token éçæä¸»çº¿ç¨æè¿æ æä¹çé夿¸²æã
轨迹è§å¾æ SystemãUserãContext å Assistant å弿¾ç¤ºï¼ä¸æ¹æ¶é´æ¡å±ç¤ºæ¬è½®ä¸åé¶æ®µï¼å®ä¸æ¯å¦ä¸ä»½é¥æµæ°æ®ï¼èæ¯ Session äºä»¶çå¦ä¸ç§æå½±ã
å «ãç宿¥å¿éå°åºåçäºå¤å°æ¬¡âå¢éâ
æå¯¼åºäºè¿æ¬¡ä¼è¯ç Session ZIPï¼å¹¶åªç»è®¡äºä»¶ç±»åãåºå·ãæ¶é´å·®å token æ°ï¼ä¸è¯»åæå ¬å¼å®æ´ç³»ç»ä¸ä¸æã
ç»è®ºæ¯é¡µé¢ä¸çâ1.2K è¾åº tokenâæ´å ·ä½ï¼
546个reasoning-delta631个text-delta- å计
1,177个模åå¢é - 宿´é»è¾äºä»¶åºå·ä¸º
0..1201ï¼å ±1,202个äºä»¶
以 request/header 为æ¶é´é¶ç¹ï¼å°¾é¨æ¶é´çº¿å¦ä¸ï¼
seq=12 +0 ms request/header
seq=13 +7 ms request/context
seq=15 +783 ms assistant/chunk block-start(reasoning)
seq=16â¦562 546 个 reasoning-deltaï¼seq=23 ç©¿æ session/titleï¼
seq=563 +6,373 ms assistant/chunk block-start(text)
seq=564â¦1194 631 个 text-delta
seq=1195 +15,465 ms assistant/chunk block-end(reasoning)
seq=1196 +15,465 ms assistant/chunk block-end(text)
seq=1197 +15,466 ms assistant/chunk usage
seq=1198 +15,466 ms assistant/chunk finish(stop)
seq=1199 +15,477 ms assistant/message
seq=1200 +15,479 ms step/end
seq=1201 +15,479 ms turn/end(completed)
è¿ç»æ°æ®è§£éäºé¡µé¢ä¸ç两个æ°åï¼
- é¦ä¸ª
reasoning-deltaä¸ reasoning block çå¼å§äºä»¶åå¨783 mså°è¾¾ï¼æä»¥ UI æ¾ç¤ºé¦ token0.8 sã - æ£æ block å¨
6.373 sæåºç°ï¼å 为å颿¯ 546 个 reasoning å¢éã
å æ¤ï¼é¦ token å»¶è¿ä¸çäºé¦ä¸ªå¯è§æ£æå符延è¿ã 对 thinking 模ååä½éªåææ¶ï¼è³å°åºè¯¥åºåâ馿¨¡åå¢éââé¦å¯è§å 容âåâ宿´åçç»æâä¸ä¸ªæ¶é´ç¹ã
usage äºä»¶ä¹ä¸é¡µé¢ç»è®¡å»åï¼
{
"inputTokens": 9242,
"outputTokens": 1178,
"cacheReadTokens": 0,
"reasoningTokens": 546
}
ä¹ã为ä»ä¹ 101 è¡ JSONL è½è£ ä¸ 1,202 个äºä»¶
导åºç session.jsonl åªæ 101 个ç©çè®°å½ï¼1 个 Session headerï¼å 100 个äºä»¶æåå¨è®°å½ã妿åªç¨ wc -l 夿äºä»¶æ°éï¼ä¼å¾å°å®å
¨é误çç»è®ºã
åå æ¯ JSONL åç«¯ä¼æè¿ç»ãå block ç delta æ ææå æï¼
{
"type": "text-chunks",
"seq0": 564,
"time0": 1786777166089,
"data": {
"turn": 1,
"step": 1,
"index": 1,
"texts": ["...", "...", "..."],
"dt": [12, 0, 8]
}
}
æ¬æ¬¡æ¥å¿ä¸æï¼
- 27 个
reasoning-chunksåå¨è®°å½ - 42 个
text-chunksåå¨è®°å½ - å ± 69 个æå è®°å½
texts ä¿çæ¯ä¸ªåå§ç段ï¼ä¸ä¼æå®ä»¬è¿æ¥æä¸ä¸ªå¤§å符串ï¼dt ä¿çç¸é»äºä»¶çæ¶é´å·®ï¼seq0 å time0 éå®é¦ä¸ªäºä»¶ãè¯»åæ¶ï¼Harness ä¼å±å¼åºåå§ç assistant/chunkï¼æ¢å¤å®å
¨ç¸åç seqãtimeãçæ®µè¾¹çå顺åºã
è¿æ¯ä¸ç§å¾å¡å®çåèï¼é»è¾å±åæâä¸å¢éä¸äºä»¶âï¼ç£çå±ä¸å¿ 为æ¯ä¸ªä¸¤ä¸ä¸ªåç token éå¤åä¸å¤§æ®µ JSON envelopeãæºç æ³¨éç»åºççå® DeepSeek ä¼è¯æµéä¸ï¼æªæå envelope çå¼é约为 payload ç 56 åã
åã[DONE] ä¹å为ä»ä¹è¿è¦æ assistant/message
æ¨¡åæµç»æå¹¶ä¸æå³ç Session åªä¿çå ç¾ä¸ªç¢çã
Agent Loop ä¸è¾¹è®°å½ chunkï¼ä¸è¾¹ç¨ BlockAssembler ç»è£
宿´å
å®¹ãæ¶å° finish åï¼å®å建æç» AssistantMessageï¼å追å ä¸ä¸ªå¸¦æ sourceEventSeqs ç assistant/messageï¼
1,177 个å¢é chunk
â
BlockAssembler å½¢æå®æ´ reasoning / text / tool-call blocks
â
assistant/message å¼ç¨çæå®ç chunk seq
è¿ä¸æ¯éå¤ä¿ååä¸äºå®ï¼èæ¯åºå两ç§ç¨éï¼
assistant/chunkæè¿°çæè¿ç¨ï¼æ¯æç´æã轨迹å精确æ¢å¤ãassistant/messageæ¯å®æåçè§èæ¶æ¯ï¼æ¯æåå²å±ç¤ºåä¸ä¸è½®æ¨¡åè¾å ¥ã
妿åçå
å«å·¥å
·è°ç¨ï¼Agent 伿§è¡å·¥å
·å¹¶å¼å¯ä¸ä¸ stepï¼å¦ææ²¡æå·¥å
·è°ç¨ï¼æ¬è½®å¨ step/end å turn/end(completed) å¤éåã
åä¸ã失败åéè¿ä¸ºä»ä¹ä¸ä¼è¢«âæµå¼âæ©ç
æµå¼ç³»ç»æå±é©çéè¯¯ï¼æ¯æåæªååºå½æåãDeepSeek Harness å¨å 个ä½ç½®æç¡®æç»è¿ç§æ¨¡ç³ç¶æï¼
- SSE å¨
[DONE]åæå¼ï¼STREAM_CLOSED data䏿¯åæ³ JSONï¼MALFORMED_RESPONSE- HTTP é 2xxï¼æ å° provider é误ã
requestIdåRetry-After - caller åæ¶ï¼ä¸æ¢ fetch åæµæ¶è´¹
- WebSocket frame ä¸ç¬¦å RPC schemaï¼å®¢æ·ç«¯ä¸¢å¼å¹¶è®°å½è¯æ
æµè§å¨ä¸è¡æå¼åçæ¢å¤çç¥ä¹ä¸æ¯âç䏿¬¡çå°åªä¸ä¸ª tokenâãå½åçæ¬éæ°æå¼æµï¼å¹¶éæ°è·å Session historyãå 为è§èäºä»¶å·²ç»è¿å ¥ Sessionï¼é¡µé¢å¯ä»¥ä»åå²éå»ºå®æç¶æã
æçå®éªéè¿åºç°äºä¸ä¸ªæå¤ï¼å¯¼åº Session ZIP åï¼å端æ§å¶å°è®°å½äºï¼
Error: web boot: appShell service missing
页é¢ä¸åº¦ç©ºç½ï¼ä½ Harness è¿ç¨ä»ç¶åæ´»ãå·æ°åï¼åä¸ä¸ªä¼è¯ã宿´åçå 15.5 s / 0.8 s / 80 tok/s ææ å
¨é¨æ¢å¤ãè¿ä¸ªç°è±¡ä¸è½è¯æå¼å¸¸æ ¹å ï¼ä¹ä¸è½æ¿ä»£ä¸é¨çå´©æºæ¢å¤æµè¯ï¼å®è³å°è¯æï¼æ¬æ¬¡å·²æäº¤ç Session 䏿¯åªåå¨äºæä¸ª React ç»ä»¶ç临æ¶ç¶æä¸ã
åäºãææ´æ¡æ¶åºå缩æä¸å¼ å¾
æç°å¨ä¼ç¨ä¸é¢è¿å¥è¯æ¦æ¬ DeepSeek Harness çæµå¼åçï¼
æµè§å¨åé䏿¬¡å½ä»¤ï¼æ¨¡åéè¿ SSE æ¨é夿¬¡å¢éï¼æ¯ä¸ªå¢éå è¿å ¥ Sessionï¼åéè¿ WebSocket 广æï¼æµè§å¨æå¨ç»å¸§å并渲æï¼æåç±
assistant/messageå°å£ã
è¿éçæ£æä»·å¼ç䏿¯âç¨äº SSEâæâç¨äº WebSocketâï¼èæ¯äºä»¶æ¥å¿ä½äºä¸¤æ¡ä¸è¡æµä¹é´ã宿æéçç½ç»åè转æäºæåºãå¯éªè¯ãå¯æä¹ åãå¯éæ¾ç产åäºå®ã
åä¸ãæè®¤ä¸ºæå¼å¾åé´çå个设计
1. ä¸è®©æµè§å¨ç´æ¥æ¥ææ¨¡åæµ
模ååæ®ãéè¯ãå·¥å ·è°ç¨ãå¤ step åæä¹ åé½çå¨ Hostï¼æµè§å¨åªæäº¤æå¾ãæ¶è´¹äº§åäºä»¶ãè¿æ · Agent çæ§è¡ç¶æä¸ä¼ç»å®å¨é¡µé¢ç»ä»¶ä¸ï¼å端ä¹ä¸éè¦çè§£ provider åè®®ã
2. å è®°å½ï¼å广æ
妿å
æ¨ UIãåè¡¥æ¥å¿ï¼å®æ¶æ¾ç¤ºä¸æ¢å¤åå²è¿æ©ä¼ååãHarness 让 assistant/chunk 忶驱卿ä¹
åå广æï¼ä»ç»æä¸åå°äºè¿ç§æ¼ç§»ã
3. é»è¾ç²åº¦ååå¨ç²åº¦å离
é»è¾å±ä¿ç 1,177 个å¢éï¼ç£çå±åªå 69 个æå è¡ï¼å缩没æä¾µå ¥ Session APIï¼ä¹æ²¡æçºç²æ¶é´åè¾¹çä¿¡æ¯ã
4. éäºä»¶æ¥æ¶ï¼ä¸çäºéäºä»¶éç»
å端ä¿ç精确å¢éï¼å´æ animation frame åå¸è§å¾ãè¿æ¯âæ¯æ¥ä¸ä¸ª token å°± setState 䏿¬¡âæ´ç¬¦åæµè§å¨çå·¥ä½èå¥ã
æºç ç´¢å¼
| ä¸»é¢ | 代ç ä½ç½® |
|---|---|
| æµè§å¨æäº¤ prompt | packages/client/runtime/src/client/sessions/session.ts |
| HTTP RPC å°è£ | packages/host/apiproxy/src/fetch/client.ts |
Host æ¥æ¶ session.prompt | packages/host/apiproxy/src/api-proxy.ts |
| DeepSeek 请æ±åºåå | packages/llm/llm-deepseek/src/serialize.ts |
| DeepSeek HTTP è¯·æ± | packages/llm/llm-deepseek/src/adapter.ts |
| SSE è§£æ | packages/llm/llm-deepseek/src/sse.ts |
| Provider å¢éç¿»è¯ | packages/llm/llm-deepseek/src/translate.ts |
| Agent è®°å½ chunkãçææç»æ¶æ¯ | packages/core/agent-loop/src/agent.ts |
Session åé
seq/time å¹¶åå¸ | packages/core/session/src/index.ts |
| Session äºä»¶è½¬ä¸º mux frame | packages/host/apiproxy/src/api-proxy.ts |
| æµè§å¨ WebSocket carrier | packages/client/connection/src/client/web-api-client.ts |
| 对è¯å¢éæå½±ä¸å¨ç»å¸§åå¸ | packages/client/ui-conversation/src/client/conversation-nodes/assistant.ts |
| JSONL å¢éæ ææå | packages/core/session/src/chunk-rows.ts |
Aitishiku.com