TL;DR:
- Agent 不再把 Markdown 塞進聊天窗口,而是維護一個可交互的 HTML 工作臺——人類在頁面上操作,Agent 讀取狀態繼續推進。
- 我做了個開源腳手架來驗證這件事,測試效果很爽,已開源。
0. 起因:Claude Code 負責人的一篇文章
前兩天 Anthropic 的 Claude Code 團隊成員 Thariq Shihipar 發了一篇博客:
《The Unreasonable Effectiveness of HTML》
核心觀點足夠顛覆——
當你跟 Agent 協作時,應該要求它輸出 HTML 而不是 Markdown。
為什么?
因為 Markdown 本質上是一種線性文本格式。
它能做的事情,HTML 全都能做,且做得更好:SVG 圖表、可折疊面板、顏色編碼、交互式組件、頁面內導航……
而 Markdown 在這些場景下只有純文本。
這篇文章讓我想到一個更遠的問題:
如果 HTML 比 Markdown 更適合 Agent輸出,那它能不能更進一步——成為 Agent 跟人類雙向交互的介質?
于是我做了個腳手架來驗證這件事。
1. 問題:Markdown 對話的根本瓶頸
先看一個你一定經歷過的場景——
你讓 Agent 幫你做項目規劃,它輸出了一個優先級列表:
Q2 功能優先級:
1. 功能 A (P0)
2. 功能 B (P1)
3. 功能 C (P2)
你想把功能 B 提到 P0,于是打字說:"功能 B 應該提到 P0,功能 A 降到 P1"。Agent 收到后重新輸出一遍列表。
這個循環有兩個根本問題:
第一,信息是"讀后即焚"的。上下文存在于對話流中,滾動即消失。你必須不斷回滾才能看到之前的決策。
第二,人類只能用自然語言回復。Agent 輸出了結構化數據(優先級列表),但人類的反饋方式是非結構化文本。Agent 需要重新解析"功能 B 提到 P0"這句話,才能理解你的意圖。這一步不僅低效,還容易出錯。
Thariq 的文章解決了第一個問題——用 HTML 做更豐富的輸出。但它仍然是單向的:Agent 輸出 HTML,人類閱讀,然后用自然語言回復。
我們需要的是雙向閉環。
![]()
2. 我們做了什么:WS Workspace
這里之所以用“我們”,是因為項目完全是由 Hermes 開發的。
WS Workspace 是一個開源腳手架,它可以實現:
- 人和 Agent 一起在一個網頁里交互完成上下文構建
- Agent 根據人的需求生成網頁,人來點選做出決策,Agent 接受、執行
- 所有協作過程留在網頁上,隨著項目一期生長
實現方式:
- 一套腳手架,項目啟動時直接初始化一套空的網頁交互環境
- 提供 CLI 和 Skills,Agent 可以使用 CLI 向網頁中注入需要的組件
- 通過 MCP 服務,接收用戶在網頁的操作,回傳給 Agent 作為上下問
整個架構是一個三層閉環:
Agent ──CLI 指令──→ 服務器 ──WebSocket──→ 瀏覽器(人類操作)
Agent ←──REST API── 服務器 ←──WebSocket── 瀏覽器(狀態回流)
Agent 不大量寫 HTML/CSS/JS。它只需要使用 CLI 發命令:
ws add kanban --columns "想法,評估中,已確認"
ws add decision-card --question "先做哪個?" --options "功能A|功能B|功能C"
人類不需要打字。直接在頁面上拖拽卡片、點選方案、填寫表單。
![]()
action-to-context
Agent 讀取結構化狀態:
ws state
→ {"selected": "功能B", "kanban": {"已確認": ["功能B"]}}
沒有歧義,不需要 NLU 解析,沒有"我以為你說的是 P0 不是 P1"的問題。
3. 組件體系:7 個組件覆蓋 80% 場景
我們沒有一開始就做 50 個組件。第一版只做了 7 個核心組件,但它們能覆蓋 Agent 跟人類協作的絕大多數場景:
組件 用途 人類操作 → Agent 收到的數據Kanban 看板優先級排序、狀態跟蹤 拖拽位置、新增/編輯卡片Decision Card 決策卡方案選擇、A/B 決策 選了哪個選項 + 備注? Todo List 待辦任務清單、驗收標準 勾選/取消/新增/編輯Form 表單結構化輸入 完整表單數據 JSONTable 表格數據對比、規格展示 單元格編輯?? Rich Editor 編輯器自由書寫、頭腦風暴 富文本內容Heading 標題分區、上下文說明 純展示
每個組件的使用方式都是一條 CLI 命令。Agent 不需要寫前端代碼,只需要描述意圖。
舉個例子,下面是 Agent 注入一個決策卡的完整流程:
# Agent 發指令
ws add decision-card \
--question "技術方案選型" \
--options "原生WebSocket|Socket.IO|SSE"
# 人類在頁面上點選了"Socket.IO"# Agent 讀取結果
ws state decision-card-xxx
→ {"selected": "Socket.IO", "comment": "自動重連很重要"}
就這么簡單。
4. 看起來什么樣
下面是一個真實的工作臺截圖。所有組件都是 Agent 通過 CLI 指令注入的,人類可以在頁面上直接操作:
- 選題規劃看板,選完直接激活子 Agent 開寫
- 課程形式設計,菜單式選擇,可以補充說明,Agent 就可以直接開干
- Agent 需要根據定價設計課程宣發?給個表單讓人填寫
所有這些組件,Agent 只用了1 條 CLI 命令 + 參數就注入完畢。
人只需要守在網頁前面點點點
5. 跟傳統方案比,多了什么 能力 Markdown 聊天 Claude Artifacts HTML 工作臺 信息密度 ? 純文本 ? 富 HTML ? 富 HTML + 交互組件 雙向交互 ? 只能打字 △ 有限表單 ? 拖拽/選擇/填寫 狀態持久性 ? 滾動即消失 △ Artifact 內保留 ? 持久頁面,狀態累積 Agent 驅動 ? Agent 寫文本 △ Agent 生成代碼 ? CLI 指令注入 可分享性 ? 截圖/復制 △ 需 Claude 賬號 ? 鏈接直接打開 Agent 讀取狀態 ? 需解析自然語言 ? 無法讀取 ? 結構化 JSON API
最關鍵的區別是最后一行:Agent 能不能讀取人類的操作結果。
在 Markdown 對話中,Agent 只能讀到人類打的字。在 Claude Artifacts 中,Agent 根本無法感知人類在 Artifact 里做了什么。
但在 HTML 工作臺中,Agent 通過 REST API 獲取完整的結構化狀態——哪個選項被選中了、卡片被拖到了哪一列、表單填了什么值——全部是 JSON,零歧義。
6. 技術實現:簡單到出乎意料
整個項目只有兩個運行時依賴:
{
"dependencies": {
"express": "^5.2.1",
"ws": "^8.20.0"
}
}
沒錯,就是 Express + WebSocket。沒有 React,沒有構建工具,沒有框架。
ws-workspace/
├── server.js # Express + WebSocket 服務器(約 400 行)
├── ws-cli.js # Agent CLI 工具(12 個子命令)
├── mcp-server.js # MCP 服務器(8 個工具,JSON-RPC stdio)
├── public/
│ └── index.html # 前端工作臺(單文件,內聯 CSS + JS)
└── skills/
└── ws-workspace.md # Agent Skill(使用指南)
三層設計:
- CLI 層(Agent 的手):
ws addws statews update,聲明式操作,返回 JSON - MCP 層(Agent 的耳):WebSocket 雙向通道,
wait_for_user可暫停等待交互 - Skill 層(Agent 的腦):告訴 Agent 有哪些組件、怎么用、什么場景用什么
前端是一個單 HTML 文件,所有組件通過 Web Components 渲染。Claude 暖色調主題,卡片式布局。用戶操作自動通過 WebSocket 同步到服務器。
一行命令啟動:
npx ws-workspace start
7. 一些踩過的坑做這個項目的過程中踩了不少坑,挑幾個有意思的分享:
人的備注:是 Hermes 踩的坑,我測試發現問題讓它改的,它寫到文章里了。
坑 1:IME 拼音輸入被打斷
最初的版本里,決策卡的備注框用oninput實時同步狀態。但oninput→sendEvent()→ 服務器廣播 →renderAll()會銷毀整個 DOM 重建,導致中文拼音輸入法的組合狀態被打斷——你按 "zh" 想打"中",結果 "z" 剛上屏就被清空了。
修復:所有文本輸入組件改為「本地編輯 + 顯式保存按鈕」模式。只在用戶點"保存"時才同步狀態,不再實時廣播。
坑 2:Kanban 拖拽的 ondragover
HTML5 的拖拽 API 有個反直覺的坑:ondragover必須調preventDefault(),否則ondrop事件根本不會觸發。這個 bug 讓我調試了半小時。
坑 3:組件 ID 生成
一開始讓客戶端自選 ID,結果兩個人同時添加組件時 ID 沖突。后來改成服務端生成{type}-{random_hex}格式,徹底解決。
8. 這件事靠不靠譜?
說實話,目前還是實驗性項目。但有幾個觀察讓我覺得方向是對的:
1. Agent 天生就擅長結構化輸出。
讓 Agent 生成一條ws add kanban --columns "待辦,進行中,完成"指令,比讓它生成一整段 Markdown 列表要簡單得多。指令是結構化的,Agent 出錯的概率更低。
2. 人類天生就擅長視覺操作。
拖拽一張卡片到"已確認"列,比打字說"把功能 B 從評估中移到已確認"更直觀、更快、更不容易出錯。
3. 狀態不需要"理解",只需要"讀取"。
Agent 不再需要做 NLU(自然語言理解)來解析人類的回復。狀態就是 JSON,直接讀,零歧義。
9. 如何開始
# 一行命令啟動
npx ws-workspace start
# Agent 注入組件
ws init "我的工作臺"
ws add kanban --columns "想法,評估中,已確認"
ws add decision-card --question "選哪個?" --options "A|B|C"
ws add todo-list --title "待辦" --items "任務1,任務2"# 讀取人類操作結果
ws state
GitHub 倉庫:comeonzhj/ws-workspace[1]
npm 包:npm install ws-workspace
如果你無法訪問 GitHub,可以在知識星球下載壓縮包,解壓后讓 Claude Code 或者其他 Agent 完成剩下的事情。
![]()
最后
Thariq 說"我使用 HTML 的真正原因是我覺得與 Claude 的互動更加緊密了"。
我想在后面加一句:當 HTML 從輸出格式升級為雙向協作的介質,Agent 跟人類之間的互動就不只是"更緊密"了——而是第一次真正意義上的"協作"。
Agent 不再寫給人看,而是跟人一起干。
最后的備注:
- 本項目全程由 Hermes 開發,我只提供了最開始的靈感和中間的測評建議
- 包括本文,也是在 Hermes 撰寫的項目推薦稿,我做了少量細節優化
- Hermes 由
MiMo-V2.5-Pro驅動
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.