我寫了一個系統,每天上朝批奏折:把 Agent 做成「文武百官」是什麼體驗

我寫了一個系統,每天上朝批奏折:把 Agent 做成「文武百官」是什麼體驗

當小人被關進天牢的那一刻,就是朕決定掏錢的那一刻。

寫在前面

09_副本.png

先說結論:當你用「擬聖旨」的方式給 Agent(代理人)下指令,看著丞相帶著六部尚書在 2D 像素宮殿裡跑來跑去給你辦差——這玩意兒比你想像的更有成就感。

我叫它 Syntropy(太和),一個基於古代朝廷隱喻的可視化多智能代理操作系統。但不是那種換個皮就完事的「國潮包裝」,而是真的把 Agent 的執行過程「具象化」了:

  • Agent 不再是日誌裡的一串 tool_call_pending,而是坐在工位上的像素小人
  • 任務調度不再是抽象的 Orchestrator(協調器),而是丞相在廷議上發號施令
  • 風險攔截不再是冷冰冰的 await human_approval(),而是把 Agent 關進天牢,等你御批

你可能會問:為什麼要搞這麼複雜?直接寫程式不行嗎?

因為現有的 Multi-Agent 框架(LangChain、AutoGen、CrewAI……)有一個共同的問題:它們是黑盒。你能看見輸入和輸出,但中間的思考、決策、調度、執行,全在一團混沌的日誌裡。你調了半天 prompt,還是不知道 Agent 卡在哪一步。

所以我的思路很簡單:既然 Agent 的執行過程看不見,那就讓它「看得見」。

08_副本.png

這篇文章會拆解這套系統的技術架構,但不會堆砌術語。咱們邊「上朝」邊聊:怎麼把狀態機映射成像素動畫、怎麼實現前後端即時同步、怎麼設計「御批」機制,以及——為什麼「當皇帝」這個隱喻,反而讓 Agent 系統更好用了。


1. 問題:Agent 系統的「三大弊政」

在動手之前,我先總結了當前 Multi-Agent 系統的三個「弊政」:

1.1 黑盒化(Black Box)——「愛卿,你到底在想什麼?」

Agent 的 Chain-of-Thought(思維鏈)是一串巢狀的 JSON,工具調用是一條條 log 記錄。你很難從這些資訊裡快速判斷:

  • Agent 現在在做什麼?是在思考,還是在等你審批?
  • 它在等誰?丞相在等戶部尚書查帳,還是兵部尚書在調兵?
  • 它卡在哪一步了?是大型語言模型(LLM)沒返回,還是工具調用超時?
  • 它的決策依據是什麼?為什麼它選了這個方案而不是另一個?

01_副本.png

現狀:你只能盯著終端滾屏的日誌,祈禱 Agent 別卡死。

1.2 失控風險(Uncontrollable)——「愛卿,這奏折朕沒批你就敢執行?」

Agent 自主調用工具是一件很危險的事。刪除檔案、轉帳、調用外部 API——這些操作一旦失控,後果不可逆。

現有的「人機協同」方案要麼是簡單的 input() 阻塞(體驗極差),要麼需要入侵式地修改整條執行鏈路(開發成本高)。

現狀:你要麼相信 Agent 不會亂來,要麼就得自己寫一套審核機制。

1.3 記憶遺忘(Amnesia)——「愛卿,昨天說的事你怎麼忘了?」

大多數 Agent 框架的記憶系統基於關鍵詞匹配或純向量檢索。關鍵詞匹配漏掉語意相關的信息,純向量檢索又容易在專有名詞上翻車。長對話場景下,Agent 往往會「忘記」幾輪之前說過的關鍵資訊。

現狀:你只能不斷「提示」Agent,把上下文塞給它,直到 Token 爆掉。


2. 方案:把 Agent 變成「可觀測的臣子」

Syntropy 的核心思路只有一句話:

所見即所思(What you see is what they think)。

具體拆解為三個「治國方略」:

2.1 可視化運行時(Visualized Runtime)——「愛卿們,都動起來」

Agent 的內部狀態機(THINKINGACTINGWAITINGERROR)不只在日誌裡列印,而是即時映射為 2D 像素小人的行為動畫

  • THINKING → 小人在原地徘徊,頭頂冒出氣泡(正在思考)
  • ACTING → 小人移動到對應的「工位」(戶部查帳、兵部調兵、工部造器械……)
  • WAITING_FOR_HUMAN → 小人被「關進天牢」,等待你的御批
  • ERROR → 小人倒地,頭頂冒叉(出錯了,得查日誌)

這套映射讓 Agent 的執行狀態變得一眼可見。你不再需要翻幾百行日誌,只需要看一眼沙盤,就知道哪個 Agent 卡住了、它在幹什麼、它在等誰。

2.2 內核級狀態機(Kernel-Level State Machine)——「朝堂規矩,不可亂」

後端 Agent 的生命週期被標準化為一個有限狀態機(FSM):

agent-state-machine.png

這個 FSM 是「內核級」的,意味著:

  • LLM 推理(Reasoning)和工具執行(Execution)完全解耦
  • 每個狀態的進入和離開都會觸發標準化的事件(可觀測)
  • 風險攔截、記憶壓縮、日誌追蹤都可以作為「狀態鉤子」無痕插入

簡單說:Agent 不再是「自由發揮」,而是按「朝堂規矩」辦事。

2.3 人機協同「御批」協議(Human-in-the-loop)——「這道奏折,朕要親自批」

每個工具調用都有 riskLevel(low / medium / high)。當 Agent 試圖執行高風險操作時:

  1. 內核掛起當前執行流,狀態切換為 WAITING_FOR_HUMAN
  2. 前端收到 approval_request 事件,彈出「御批」視窗
  3. 使用者點擊「准奏」或「駁回」,透過 Socket 發回指令
  4. 內核恢復執行流(或回滾)

這套機制讓「人審」不再是事後補救,而是執行流程的有機組成部分


3. 核心功能:奏折閣與決策樹

講完架構,聊聊實際用起來是什麼感覺。

3.1 奏折閣(Imperial Archives)——「每道聖旨,都有跡可循」

在 Syntropy 裡,每一次使用者指令都被封裝為一份「奏折」。奏折閣是系統的任務管理中心,完整記錄了從擬旨 → 受理 → 分發 → 覆命的全過程。

奏折的核心特性

  • 折疊/展開:每份奏折預設折疊,僅顯示摘要(如「查 Q1 稅收」);展開後可查看完整的對話鏈路與決策樹
  • 多視角敘事:左側展示百官的回覆與思考過程,右側展示皇帝(使用者)的指令,清楚還原對話脈絡
  • 狀態追蹤:每份奏折都有明確的狀態標籤(待處理 / 進行中 / 已完成 / 已駁回),方便追溯

3.2 決策樹可視化——「丞相的思考,一目了然」

當丞相收到一道複雜指令(如「查一下上季度的營收,並對比去年同期」),它不會直接給出答案,而是會拆解任務、調度六部、匯總結果。整個過程形成一棵決策樹

decision-tree.png

在奏折閣中,這棵決策樹以可視化流程圖的形式呈現。你可以清楚地看到:

  • 丞相調度了哪些 Agent(代理人)
  • 每個 Agent 執行了什麼操作
  • 每一步的輸入和輸出是什麼
  • 如果某一步出錯,具體卡在哪裡

02_副本.png

這對除錯和優化至關重要。你不再需要猜「Agent 為什麼沒按我的預期做事」,而是可以直接看到它的決策路徑,找出問題所在。

3.3 記憶庫(Memory Vault)——「史官的起居注」

記憶庫展示 Agent 主動儲存的重要資訊(個人偏好、專案決策、關鍵事實),支援:

  • 搜尋與過濾:按關鍵字搜尋,或按類別(personal / preference / project / decision)篩選
  • 線上編輯:直接在前端編輯或刪除記憶條目,實時同步到後端
  • 語意分類:LLM 根據上下文自動選擇記憶類別,無需人工標註

4. 技術實現:朝堂是如何運轉的

4.1 前端:React + Phaser 的「雙引擎」架構

前端是 Syntropy 最複雜的部分。我們需要同時處理兩類需求:

  • UI 層:奏折面板、記憶庫、官員狀態 HUD、御批彈窗……這些是典型的 React 元件
  • 渲染層:2D 像素沙盤、角色動畫、尋路、碰撞檢測……這些是遊戲引擎的領域

我們的方案是 React-Phaser Bridge

frontend-architecture.png

  • 使用 Zustand 作為單一資料來源(Single Source of Truth),儲存所有 Agent 的狀態
  • React 元件訂閱 Zustand Store,渲染 UI 面板
  • Phaser 3 在 update() 迴圈中讀取 Store,同步小人動畫

這套架構的好處是:UI 和渲染完全解耦。React 不用關心像素座標,Phaser 不用關心業務邏輯,兩者透過 Zustand 的狀態橋接。

關鍵程式碼片段:狀態同步
// store/agentStore.ts
export const useAgentStore = createAgentStore((set) => ({
  agents: {},
  updateAgent: (id, updates) =>
    set((state) => ({
      agents: {
        ...state.agents,
        [id]: { ...state.agents[id], ...updates },
      },
    })),
}));
// game/MainScene.ts
export class MainScene extends Phaser.Scene {
  update() {
    const agents = useAgentStore.getState().agents;
    Object.values(agents).forEach((agent) => {
      const sprite = this.sprites[agent.id];
      if (sprite) {
        sprite.updateState(agent.status, agent.targetPosition);
      }
    });
  }
}

4.2 後端:自研 Agent 框架

Syntropy 的後端完全自研,不依賴任何現有的 Agent 框架(LangChain、AutoGen 等)。原因很簡單:現有框架的狀態機模型和我們需要的不完全匹配

後端整體架構

backend-architecture.png

後端核心模組:

  • Kernel:Agent 生命週期管理、狀態機調度
  • Agent:單個 Agent 的 LLM 調用、工具執行、狀態流轉
  • LLM Provider:統一的 LLM API 抽象(支援 OpenAI / DeepSeek)
  • MemoryManager:記憶儲存與檢索(FTS5 + Vector + RRF)
  • SocketGateway:前後端即時通訊
  • Tracer:全鏈路追蹤與結構化日誌
Agent 核心狀態機
// server/core/Agent.ts
class Agent {
  private state: AgentState = 'IDLE';

  async processMessage(message: string) {
    this.setState('THINKING');
    const response = await this.llm.chat(message);

    if (response.toolCalls) {
      for (const toolCall of response.toolCalls) {
        const risk = this.assessRisk(toolCall);
        if (risk === 'high') {
          this.setState('WAITING_FOR_HUMAN');
          await this.waitForApproval(toolCall);
        }
        await this.executeTool(toolCall);
      }
    }

    this.setState('IDLE');
    return response.content;
  }
}

4.3 記憶系統:RRF 混合檢索引擎

Syntropy 的記憶系統不是純向量檢索,而是三位一體的混合架構:

  1. SQLite:結構化的索引元資料(時間、類別、Agent ID)
  2. FTS5:全文倒排索引,精準匹配關鍵字
  3. Vector:語意向量,模糊語意檢索

檢索時,我們使用 Reciprocal Rank Fusion (RRF) 演算法合併 FTS 和 Vector 的結果:

RRF_score = Σ (1 / (k + rank_i))

其中 k 是平滑常數(通常取 60),rank_i 是某條記憶在第 i 個檢索引擎中的排名。

為什麼需要 RRF?
  • 關鍵字場景:使用者問「昨天的稅收是多少」,FTS 能精準命中包含「稅收」的記錄,Vector 可能因為語意漂移而漏掉
  • 語意場景:使用者問「最近有什麼異常嗎」,FTS 因為沒有一個明確的關鍵字而失效,Vector 能理解「異常」的語意

RRF 讓兩者互補,召回率顯著提升。

記憶壓縮(Memory Compression)

當 Agent 進入 SLEEPING 狀態時,系統會自動呼叫 LLM 對當日未處理的對話進行摘要,生成 daily_summary 並持久化。這解決了長對話場景下的 Token 溢出問題。

// server/runtime/MemoryManager.ts
async compressMemories(agentId: string, conversations: Conversation[]) {
  const summary = await this.llm.chat(`
    請對以下對話進行摘要,提取關鍵決策和待辦事項:
    ${conversations.map(c => c.content).join('\n')}
  `);

  await this.db.insert('memories', {
    agentId,
    type: 'daily_summary',
    content: summary,
    timestamp: Date.now(),
  });
}

4.4 全鏈路追蹤(Tracer)

每個使用者指令產生唯一的 traceId,貫穿 Agent 調度、工具調用、LLM 推理全流程。Tracer 記錄 8 種診斷事件:

  • agent.turn:Agent 開始處理一輪對話
  • tool.call:工具調用
  • model.usage:LLM 調用及 Token 消耗
  • dispatch:任務分發
  • approval.wait:等待御批
  • approval.done:御批完成
  • agent.stuck:Agent 卡死檢測(3 分鐘無回應自動告警)
  • memory.save:記憶保存

所有事件自動脫敏(截斷 API Key 等敏感資訊),便於效能分析和故障排查。


5. 架構反思

5.1 為什麼選 Phaser 而不是 Canvas / SVG?

Phaser 是一個專業的 2D 遊戲引擎,提供了完整的場景管理、精靈動畫、碰撞檢測、尋路演算法。如果用 Canvas 手寫,這些都要從零實作;如果用 SVG,大量 DOM 元素會導致效能問題。

Phaser 的 WebGL 渲染讓我們可以輕鬆實現:

  • 像素小人的平滑移動動畫
  • 頭頂氣泡的動態效果
  • 大規模 Agent 同時活動時的效能保障

5.2 為什麼不直接用 LangChain / AutoGen?

LangChain 和 AutoGen 的狀態機模型是「扁平」的:Agent 順序執行 Thought → Action → Observation。但我們需要的是內核級的狀態機,能夠:

  • 在任意時刻掛起執行流(御批攔截)
  • 在任意時刻注入新狀態(外部干預)
  • 標準化所有狀態轉換事件(可觀測性)

現有的框架很難在不破壞封裝的前提下實現這些需求。

5.3 視覺隱喻的取捨

「古代朝廷」這套視覺系統是一把雙刃劍:

優點:

  • 降低了多 Agent 系統的認知門檻,非技術使用者也能理解「丞相調度六部」的概念
  • 增強了產品的辨識度和傳播性

缺點:

  • 增加了設計和開發成本(像素素材、動畫、場景佈局)
  • 對部分技術使用者來說可能顯得「花俏」

我們的判斷是:可視化的核心價值在於降低認知負擔,視覺隱喻只是手段。如果換一套視覺系統(如太空站、工廠流水線),只要核心架構不變,價值依然存在。


6. 開源與未來

Syntropy 目前處於 Beta 階段,程式碼已開源:GitHub - zabr1314/Syntropy

未來規劃:

  • 技能市集:允許開發者為 Agent 開發自訂技能(類似 VSCode 外掛)
  • 多場景範本:除了「朝廷」,提供「太空站」「工廠」等可選視覺主題
  • Agent 編排可視化:拖拉式建構多 Agent 協作鏈路
  • 分散式部署:支援將不同 Agent 部署在不同節點上

7. 結語:當皇帝的一天

Syntropy 本質上是一次將 AI 黑盒具象化的嘗試。我們相信:

當 Agent 的執行過程變得可見、可互動、可干預,多智能代理系統才能真正從實驗室走向生產環境。

但除此之外,它還有一個不那麼「技術」的價值:它讓管理 Agent 變成了一件有趣的事

每天早上打開系統,看到丞相已經在廷議上等你,六部尚書各就各位。你擬定一道聖旨,看著小人們在宮殿裡跑來跑去辦差。有時候他們會卡住,有時候他們會犯錯,有時候他們會把奏折遞到你面前等你御批——這一刻,你不是在 debug,你是在當皇帝。

如果你對這套架構感興趣,或者有自己的看法,歡迎在 GitHub 上交流。


相關資源


原文出處:https://juejin.cn/post/7624363823747711003


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝9   💬11   ❤️3
560
🥈
我愛JS
📝2   💬7   ❤️2
148
🥉
💬1  
4
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登