果卡片 Markdown 渲染:generic-card 與專用卡片的呈現(xiàn)邊界)
DeepSeek Harness TUI 工具結(jié)果卡片 Markdown 渲染generic-card 與專用卡片的呈現(xiàn)邊界【免費(fèi)下載鏈接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.項(xiàng)目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness工具在向用戶呈現(xiàn)結(jié)果時(shí)往往會(huì)攜帶 Markdown 內(nèi)容——包括用 fencedconsole代碼塊表達(dá)的“后臺(tái)任務(wù)已受理”確認(rèn)信息與執(zhí)行錯(cuò)誤。DeepSeek Harness 的 TUI 通過引入“共享 Markdown 主題 先渲染后截?cái)唷钡臎Q策讓 generic 工具卡片的渲染詞匯與對(duì)話內(nèi)容保持一致同時(shí)為終端與 diff 卡片保留專用純文本渲染路徑。本文以 2026-07-23-tui-generic-card-markdown Agent Note 為骨架結(jié)合倉庫中的工具呈現(xiàn)意圖定義、Markdown 渲染管線與卡片模型源碼說明這一決策的問題背景、實(shí)現(xiàn)方式、邊界劃分與驗(yàn)證手段。一、背景工具結(jié)果的“呈現(xiàn)意圖”詞匯表DeepSeek Harness 的 TUI 如何渲染一次工具調(diào)用并不是由 UI 逐個(gè)工具名特判決定的而是由工具自身通過ToolDefinition.presentCall/ToolDefinition.presentResult聲明一個(gè)“呈現(xiàn)意圖”render intent。這套 provider-neutral 詞匯表定義在 presentation.tsToolCallView GenericCallView | TerminalCallView | DiffCallView即一次調(diào)用要么是默認(rèn)的通用卡片generic要么是終端卡片terminal要么是 diff 卡片ToolResultView在此基礎(chǔ)上增加了SearchResultView、ReadResultView、WebResultView并在generic、terminal、diff三種結(jié)果視圖上鏡像同一套card判別字段。其中GenericResultViewpresentation.ts只攜帶可選的替換標(biāo)題title和 UI 面向的結(jié)果內(nèi)容塊content?: ContentBlock[]是“其余一切工具”的默認(rèn)落點(diǎn)。而TerminalResultViewpresentation.ts則攜帶output、exitCode、signal三個(gè)結(jié)構(gòu)化字段并明確寫了一條回退規(guī)則不具備終端卡片能力的 UI會(huì)得到由bridge 從output派生出的 fenced console 回退工具本身不做雙重編碼。正是這條回退規(guī)則與 generic 卡片的內(nèi)容模型組合在一起構(gòu)成了本文討論的問題generic 卡片允許攜帶任意 Markdown 內(nèi)容塊其中就包括這些 fencedconsole輸出。二、問題純文本渲染把 fence 標(biāo)記暴露給用戶當(dāng)工具 presenter 在 generic-card 內(nèi)容中放入 Markdown例如后臺(tái)任務(wù)確認(rèn)、執(zhí)行錯(cuò)誤信息里嵌入的 fencedconsole塊時(shí)如果 TUI 把這些內(nèi)容當(dāng)作純文本逐字渲染用戶就會(huì)直接看到 圍欄標(biāo)記和可選的console語言標(biāo)簽。這在同一個(gè) transcript 里會(huì)產(chǎn)生明顯的不一致同樣是 Markdown助手assistant與用戶user的消息走的是完整 Markdown 渲染管線而工具卡片的結(jié)果區(qū)域卻把 Markdown 源文本原樣攤開。從倉庫的代碼可以看到這條“對(duì)話內(nèi)容與工具內(nèi)容”的渲染能力落差對(duì)話內(nèi)容由 AssistantMarkdown.tsx 經(jīng)MarkdownText走共享 Markdown 渲染器而工具卡片的結(jié)果文本在 tool-call-model.ts 的resultText()中只是簡單展平text塊逐字拼接、非文本塊按 JSON 格式化再作為output字段交給卡片。于是“fence 標(biāo)記可見”“語言標(biāo)簽可見”“fenced 正文沒有代碼著色”成為 TUI 中 generic 工具卡片的三個(gè)觀感缺陷這與該 Agent Note 記錄的 Problem 完全對(duì)應(yīng)。三、決策共享 Markdown 主題渲染渲染先于截?cái)噌槍?duì)上述問題決策分為兩個(gè)互補(bǔ)的部分見 Agent Note。3.1 用共享 Markdown 主題渲染 generic-card 結(jié)果TUI 不再對(duì) generic-card 的結(jié)果內(nèi)容做純文本渲染而是先復(fù)用與對(duì)話內(nèi)容相同的共享 Markdown 主題渲染一遍再應(yīng)用卡片自身的 head-and-tail 行數(shù)限制。共享主題要完成三件事隱藏 fence 語法——圍欄標(biāo)記本身不顯示保留可選語言標(biāo)簽——console、ts、bash等信息串繼續(xù)以語言標(biāo)簽形式呈現(xiàn)fenced 正文著色為代碼——代碼塊內(nèi)的內(nèi)容按語法高亮展示。倉庫中的共享渲染管線正是這樣實(shí)現(xiàn)的。位于 render.tsx 的是一個(gè)直接 mdast→React 的 Markdown 渲染器其中的renderCoderender.tsx把 fenced code 節(jié)點(diǎn)交給CodeBlock組件而CodeBlockCodeBlock.tsx負(fù)責(zé)渲染一個(gè)帶infostring 語言標(biāo)簽橫幅、shiki 語法高亮與復(fù)制按鈕的代碼塊。也就是說fence 標(biāo)記不會(huì)進(jìn)入最終 DOM語言信息則從node.lang落到代碼塊頂部的標(biāo)簽欄。這一渲染路徑同時(shí)也是對(duì)話內(nèi)容的 sanitize 路徑render.tsx頭部明確聲明了“不可信輸出策略”——鏈接與圖片目標(biāo)經(jīng)過協(xié)議白名單僅http:、https:、mailto:圖片額外要求絕對(duì) HTTP(S) 地址raw HTML 一律作為字面文本渲染、不進(jìn)入 DOMKaTeX 不啟用受信命令render.tsx。generic 工具卡片復(fù)用這條路徑意味著工具結(jié)果里的 Markdown 也自動(dòng)獲得了同樣的安全護(hù)欄。3.2 渲染先于截?cái)嘈袛?shù)統(tǒng)計(jì)基于可見行決策的另一半是順序先做 Markdown 渲染再應(yīng)用折疊卡片的 head-and-tail 行數(shù)限制。原因在于如果先按 Markdown源行截?cái)嗫赡芮『冒岩欢?fenced block 攔腰切斷留下殘缺的代碼塊折疊卡片的行數(shù)統(tǒng)計(jì)與邊界描述的是用戶最終看到的終端行而不是 Markdown 源行——渲染前后行數(shù)并不一致例如一段多行 fenced 代碼被折疊成“語言標(biāo)簽 高亮正文”后可見行數(shù)與源碼行數(shù)不同。因此必須先渲染、再按可見輸出計(jì)算頭部與尾部行數(shù)。這個(gè)“按行數(shù)/字節(jié)預(yù)算保留頭部與尾部”的思路在倉庫中并非孤例工具端對(duì)模型返回內(nèi)容的截?cái)嗾怯蒬sh-output-retention的TextRetainer以head/tail/headTail三種窗口完成的見 output-retention 包說明卡片端的折疊行數(shù)限制與之語義一致只是作用在渲染后的可見行上。四、邊界劃分generic、terminal、diff 與 raw input 各司其職“給 generic 卡片開啟 Markdown 渲染”并不意味著所有工具卡片都變成 Markdown。決策明確保留了三條邊界Terminal 卡片保留專用純文本渲染器。終端卡片有自己的一整套語義化渲染以 cwd 為頭部、命令為標(biāo)題、輸出區(qū)展示捕獲的 stdout/stderr并把退出狀態(tài)渲染為獨(dú)立的 pill。其數(shù)據(jù)模型在 terminal-card-model.ts 中定義parseExitStatusL249-L255從 shell 工具渲染器追加的[exit code: N]/[killed by signal: X]標(biāo)記契約見 shell/render.ts中恢復(fù)退出碼與信號(hào)terminalCardModelL267-L307負(fù)責(zé)把 raw Tool 塊派生為終端卡片 props。在 ToolRow.tsx 中終端卡片的maxLines被設(shè)為Infinity即不做折疊截?cái)唷驗(yàn)榻K端輸出本身具有專用格式與滾動(dòng)語義。Diff 卡片保留專用渲染。文件變更走DiffBlock擁有自己的 diff 行著色與上下文行號(hào)語義不能被通用 Markdown 的標(biāo)點(diǎn)解釋所污染。generic-card 的 raw input 保持字面量。決策特別強(qiáng)調(diào)generic 卡片的原始輸入raw input仍然逐字顯示因?yàn)樗淼氖枪ぞ邊?shù)而不是 presenter 創(chuàng)作的散文。對(duì)應(yīng)到代碼中deriveBodytool-call-model.ts對(duì)參數(shù)做JSON.stringify展平展示這部分內(nèi)容刻意不走 Markdown 解釋。這些邊界最終體現(xiàn)在分發(fā)組件 GenericToolCard.tsx 中它同時(shí)派生toolRowModel、terminalCardModel、readCardModel、diffCardModel、searchCardModel、webCardModel把不同意圖交給ToolRow里對(duì)應(yīng)的專用渲染分支只有落在 generic 輸出槽位的內(nèi)容才走共享 Markdown 主題。五、備選方案與取舍Agent Note 記錄了三個(gè)被否決的備選方案它們分別從“生產(chǎn)者側(cè)修復(fù)”“渲染范圍”“截?cái)囗樞颉比齻€(gè)角度嘗試過解決各有其不可取之處在 Bash presenter 中剝離 fence 標(biāo)記。這只修復(fù)了一個(gè)生產(chǎn)者——其他工具放入 generic 卡片的 Markdown 仍然不渲染而且會(huì)讓 presenter 反向依賴 TUI 的渲染行為職責(zé)倒掛。把所有工具卡片一律渲染為 Markdown。終端輸出與 diff 有專用格式化且其中可能包含必須保持字面量的 Markdown 標(biāo)點(diǎn)例如終端輸出里的*、#、反引號(hào)一旦被解釋就會(huì)失真。在 Markdown 渲染前先應(yīng)用折疊卡片限制。按源行截?cái)鄷?huì)切斷 fenced block且折疊計(jì)數(shù)與可見行數(shù)不一致行數(shù)提示會(huì)與實(shí)際顯示錯(cuò)位。這三個(gè)被否定的方向從反面印證了最終決策按意圖intent區(qū)分渲染路徑而不是按內(nèi)容格式一刀切順序上讓“渲染”始終先于“截?cái)唷?。六、后果與驗(yàn)證決策落地后的行為約束同樣記錄在 Agent Note 的 Consequences 一節(jié)詞匯與安全路徑統(tǒng)一generic 工具卡片與對(duì)話內(nèi)容使用同一套 Markdown 詞匯與 sanitize 路徑。工具結(jié)果中的鏈接、圖片、代碼塊獲得與聊天內(nèi)容相同的白名單與字面 HTML 處理標(biāo)點(diǎn)語義變化generic 卡片中的 Markdown 標(biāo)點(diǎn)會(huì)被解釋而非總是字面顯示需要逐字呈現(xiàn)終端輸出避免標(biāo)點(diǎn)被誤讀的工具應(yīng)當(dāng)改用terminal card 意圖——這正是ToolCallView詞匯表存在的意義測(cè)試固定行為聚焦的 TUI 測(cè)試固定了三條行為——fence 被隱藏、語言標(biāo)簽被保留、正文文本正確呈現(xiàn)同時(shí)keyless 終端狀態(tài)快照通過一組組裝的 TUI transcript 覆蓋該行為。關(guān)于快照測(cè)試方法本身倉庫中有更完整的方法論記錄2026-07-18-tui-terminal-state-snapshots Agent Note 說明了為何不直接固定Terminal.write()片段差分渲染可能改變寫邊界而不改變畫面、為何不用組件行快照ANSI 解析、光標(biāo)、視圖port 行為無法覆蓋而是讓生產(chǎn) TUI 掛載在 headless 終端模擬器上、等同步幀穩(wěn)定后讀取語義化終端狀態(tài)行列、緩沖區(qū)坐標(biāo)、生命周期、樣式范圍并強(qiáng)調(diào)所有檢查點(diǎn)強(qiáng)制主題無關(guān)性僅使用 ANSI 0–15 調(diào)色板、無背景色——這正是“keyless”快照能夠穩(wěn)定描述卡片渲染結(jié)果的原因。七、總結(jié)DeepSeek Harness TUI 對(duì)工具結(jié)果卡片的處理遵循一條清晰的主線以工具聲明的呈現(xiàn)意圖generic / terminal / diff為分發(fā)依據(jù)以共享 Markdown 渲染管線為 generic 內(nèi)容的標(biāo)準(zhǔn)答案以“渲染先于截?cái)唷北WC折疊行數(shù)的語義正確以專用的 raw input 呈現(xiàn)保住工具參數(shù)的逐字保真。對(duì)使用者而言這條決策的實(shí)操含義很直接在工具 presenter 中面向 UI 書寫 generic 結(jié)果內(nèi)容時(shí)可以放心使用與助手消息一致的 Markdown 詞匯包括 fencedconsole代碼塊、鏈接、強(qiáng)調(diào)它們會(huì)獲得同樣安全、同樣美觀的呈現(xiàn)需要輸出保持逐字保真的場(chǎng)景終端輸出、diff應(yīng)聲明對(duì)應(yīng)的card: terminal/card: diff意圖而不是依賴 generic 卡片的內(nèi)容格式折疊卡片的行數(shù)限制作用于渲染后的可見行因此任何基于“行數(shù)”的展示預(yù)算都應(yīng)放在渲染語義上理解。理解這一決策就能在擴(kuò)展新工具或調(diào)試卡片呈現(xiàn)時(shí)準(zhǔn)確判斷“為什么這段內(nèi)容這樣顯示”“應(yīng)該改 presenter 還是改 UI”從而遵循倉庫既有的呈現(xiàn)邊界行事?!久赓M(fèi)下載鏈接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.項(xiàng)目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考