Lantide Data

AI Agent 架構:ReAct、狀態路由與 UICommand

配套②:自行實作 ReAct 串流迴圈,以換取對事件型別、狀態過濾與 IDE 整合的完全控制。操作見 使用者指南 §12


系列位置

完整導讀見 系列導讀與產品定位

順序 文章 本題
0 系列導讀與產品定位 系列導讀與產品定位
1–3 Agent 時代的數據分析工作流可治理的 Agent MemoryPrompt 與 Context 工程 工作流、Context
4 本文 Agent runtime
5 統一查詢層 查詢層

一、為何自研 ReAct

ReAct(Reasoning + Acting,推理—行動迴路)讓模型在單輪對話中交替「思考 → 呼叫工具 → 讀取觀察 → 再思考」。通用 Agent 框架擅長編排,但 Lantide Data 需要更細的控制:

  • SSE 事件型別細分:thought、tool_call、tool_result、message、ask_user、error…
  • 狀態依賴的工具白名單與 mid-turn 刷新
  • UICommand 與前端 interceptor 同步開 tab、寫 SQL、Source Run 完成
  • IDE 整合執行:工具產出須落在真實 SQL 分頁、TabCacheRegistry、UICommand——而非僅在遠端 sandbox 回傳圖表
  • 探索預算provider fallbacktoken 壓縮等產品級策略

因此採用 Python AsyncGenerator + 自有 stream_react_run,而非套用現成 Agent 抽象——我們要的是與 IDE 狀態、SSE 事件、工具白名單逐行對齊的執行迴路,而不是通用編排上的便利。


二、架構總覽

flowchart TB
  api[FastAPI_stream]
  runner[LantideDataAgent.stream_react_run]
  router[StateRouter]
  prompt[_build_system_prompt]
  loop[ReAct_iteration]
  llm[LLM_streaming]
  tools[tool_ask_user_UICommand]
  sse[SSE_events]

  api --> runner
  runner --> router
  runner --> prompt
  runner --> loop
  loop --> llm
  llm --> tools
  tools --> loop
  loop --> sse

每次迭代:解析狀態 → 過濾工具 schemas → 組裝 system prompt → LLM 串流 → 執行 tool / 暫停 ask_user / 推送 UICommand;狀態轉換工具執行後同輪刷新 prompt 與工具集。


三、工具註冊與 TOOL_AVAILABILITY_MATRIX

每個工具定義 Safety(safe / write)與可用 AgentState。矩陣在每次迭代開始過濾 schemas,例如:

狀態 典型差異
NoProject / Quick 探索工具 + focus_project
ProjectFocused open_planopen_report、HTML 條件工具
PlanPlanning 文件與探索;限量 run_query 探數允許; SQL Tab 工具群、統計 run_*(prompt 禁止 activate_analysis
PlanExecuting run_sql_tabsource_run_sql_tabadd_report…;不含 update_reportpatch_report(正式 closeout 須新增 Report)
ProjectFocused / PlanPlanning 除讀取/小修與授權匯入外,可用 lantide:reference-authoringadd_reference 原生建立正文+alias+必填 Intro
ProjectFocused / PlanPlanning / PlanExecuting read_referencepatch_reference;授權匯入時另有 prepare_external_refget_external_ref_importsave_external_refread_reference_sourcelantide:load-external-ref

State prompt 不列工具目錄 — 名稱與參數以當輪 function schemas 為準(見 Prompt 與 Context 工程)。

3.1 Ask mode(內建 capability ceiling)

內建聊天 composer 可在 AgentAsk 之間切換(session sticky;預設 Agent)。Ask 是疊加在 TOOL_AVAILABILITY_MATRIX 之上的 ceiling overlay,不是新的 AgentState,也不是 MCP Observe:

Ask(內建) MCP Observe
約束對象 內建 Agent 工具 外部 MCP session
仍可 讀 artifact/schema、run_query、重跑既有 SQL Tab(run_sql_tabsource_run_sql_tab)、統計 run_*ask_user、導航 依 AccessMode 政策
禁止 寫 Plan/Report/HTML/SQL 正文、create_html_report、知識寫入等 denylist 外部寫入(不暫停內建 Agent)

Ask 下 schema 隱藏寫入工具;若仍被呼叫 → 結構化 ASK_MODE_DENIED。切回 Agent 後下一 turn 恢復該 state 的完整工具集(含既有 overlays)。操作見 USER_GUIDE Agent/Ask 小節。


四、ReAct 迴圈細節

4.1 迭代上限

max_iterations 預設 100,防止異常迴圈耗盡資源;達上限發送 iteration_limit 事件。

4.2 探索預算(Exploration Budget)

show_tablesread_schemavalidate_query 計入探索。超過 exploration_soft_cap(預設 7)後,工具呼叫被跳過並回傳 EXPLORATION_BUDGET_REACHED

續跑協議: 下一個工具呼叫必須ask_user,且問題含標記 [[EXPLORATION_BUDGET]],選項含 Continue exploring / Stop for now。使用者選擇繼續則重置本輪探索計數;選擇停止則 Agent 應先整理發現再決策。不應僅靠靜默注入系統字串繞過人類確認。

4.3 Auto-continuation 與 output ceiling

finish_reason == "length" 且仍有空間時,可追加 "Please continue..."(次數上限),避免長報告截斷。空正文的 length 視為 output_truncated(非 context overflow)。

單次 max_tokensresolve_output_ceiling 決定:Profile maxTokens(可選)→ litellm max_output_tokens → state 預設,再以 state band 夾限;無 Profile 時 discovered 為硬頂。html_report 另有 20480 floor。Compact / history truncate 預留同一 ceiling。

4.3.1 同 turn 文件類 compact

每輪 tool 批次後,對 read_plan / read_report / read_referencedocs_k=1(按 iteration 批次):僅保留最近一輪(含同輪並行多份)全文,更早批次壓為最小 stub。完整 read_docs 與可配 K 見 PRD 260706_agent-token-p1-compact-read-docs

4.4 Mid-turn 狀態轉換

focus_projectadd_report 等執行後,從 session 重新解析狀態,更新 messages[0] system prompt 與工具集,使同輪後續迭代落在正確狀態(例如 NoProject → ProjectFocused)。


五、Provider fallback

LLM 呼叫失敗時可依設定 fallback 至備用 profile,並在 SSE 中標示,減少單點模型不可用導致整輪失敗。


六、System prompt 組裝與 mid-turn 刷新

LantideDataAgent._build_system_prompt() 合併 core、state、知識、catalog 提示、HTML 條件段。狀態轉換或 html_report_active 翻轉後同輪重建,與工具 schema 對齊。

6.1 Query Step Ledger 注入(Layer 3.5)

多步分析時,模型需要記住「上一步物化了哪張表、SQL 長什麼樣」。每個對話在 workspace 內維護 Query Step Ledgerrun_query / run_sql_tab 成功或失敗後寫入步驟索引;system prompt 附加精簡摘要(快取名 + SQL 預覽,≤1500 字元),使多輪後模型仍知近期步驟。完整 SQL 靠 query_resultget_cache_source 或 registry 查閱。

與 Plan Progress 分工: Plan 執行期的 Steps UIread_planexecution_record.steps 來自 *.progress.jsonsteps[](report 後 steps_frozen);Ledger 仍服務對話連續性。fork_conversation 會將來源 conv 的 ledger 全量複製到新 conv,使 fork 後 list_query_steps 與摘要不斷裂。

6.2 Ledger 相關工具

工具 / API 何時用 用途
get_cache_source 需審閱某快取表的原始 SQL 等同 UI「查看 SQL」,與 GET /cache/{tab_id}/source 同源
list_query_steps 多步鏈失序或需核對 step_id 列出本對話步驟;可選附截斷 SQL
query_result 讀取某次 run 的結果與 SQL 記憶體或 spill 後的 meta sidecar
[[QUERY_STEP]] 協議 每次 run 成功後(assistant 正文) 一行摘要;runner 解析後回填 purpose / pitfalls

conversation_id 由 runner 注入,LLM 不可自傳;無 conversation 時跳過 ledger,物化策略仍依 workspace 啟發式運作。

批註不走 Query Step Ledger——狀態在 *.annotations.json sidecar 與正文 mark,由 read_plan / read_report / resolve_annotations 處理(見 Prompt 與 Context 工程 §十一)。

6.3 專案文件與批註工具

工具 Safety 典型狀態 用途
read_plan / read_report safe Plan/Report 相關 磁碟正文 + sidecar + annotation_summary;已 Executed 的 Plan 優先讀 progress snapshot/執行 SQL(雙 flag),避免只開 live tab 當 evidence
resolve_annotations write Planning(非 Executing/Executed 正文鎖) 依 open 批註原子 patch 正文與 sidecar
read_reference safe ProjectFocused、PlanPlanning、PlanExecuting [Ref: …] When to read 載入 Reference 全文(Markdown+連結;不含圖像素)
patch_reference write 同上 小範圍 find-replace;不建立新 Reference 檔
add_reference write ProjectFocused、PlanPlanning(Ask/PlanExecuting 不可) 從對話/分析原生建立正文、alias 與必填 Intro;明確要求可一次提交,主動建議需 digest-bound 確認
prepare_external_ref write(授權) 同上(匯入流程) 以 GUI handle 或 Admin 路徑開始解析 staging
get_external_ref_import safe 同上 讀取 staging/匯入進度與摘要(不全文塞進 chat)
save_external_ref write 同上 原子寫入 Reference Markdown、圖片資產、來源證明與 Intro 狀態
read_reference_source safe 同上 按頁/sheet/slide 等讀取不可變來源快照細節

七、HTML 工具與契約(正文摘要)

產品動機見 Agent 時代的數據分析工作流 §五。操作見 §10

7.1 條件註冊 html_report_active

為真時(task_mode=html_report 或 Report tab 且磁碟已有契約 HTML):

  • 常駐 create_html_report(Generate / Re-generate)
  • 追加 read_html_textget/patch_html_chunk_by_id(含 mode=fullmode=replacereplacements)、insert_html_chunkdelete_html_chunkget/patch_html_styles
  • 注入 Editing HTML Reports state 段

同輪 create_html_report 成功後,下一 iteration 重算 flag 與 schemas,無需換 tab。Ask mode 開啟時上述寫入工具一律不可用(含 Generate)。

Scoped 結構編輯: 「加一段/刪一段」優先 insert_html_chunkdelete_html_chunk;區塊內改字用 mode=replace;整版換版面才 Re-generate。使用者操作細節見 使用者指南 §10

7.2 activate_html_editing

當使用者不在 Report tab 卻要改 HTML 時,Agent 先呼叫此工具解鎖 chunk/樣式工具,再 read → get → patch。多份 report 皆有 HTML 且未指定檔名時應追問或回 AMBIGUOUS_REPORT。無 data-report-contract="1" 的舊檔應 Re-generate。每輪對話需重新 activate 或回到 Report tab。

7.3 契約要點(內化敘述)

  • 根元素:<html data-report-contract="1" data-report-profile="standard|standard-chartjs|revealjs|offline">
  • <head>恰一個 <style id="report-styles"> 承載全域 CSS
  • <body> 直下可 patch 區塊:data-text-id(regex ^[a-z][a-z0-9_-]{0,63}$)+ data-chunk-desc;禁止巢狀可 patch 容器
  • create_html_report 做 full 驗證;chunk 工具僅驗證 body
  • standard-chartjs:head 含 Chart.js CDN + datalabels CDN + annotation CDN + #report-chart-init;圖表用 canvas[data-chart-spec] JSON(options.plugins.datalabels / annotation 純 JSON;deprecated lantide.dataLabels 仍兼容);patch 後需重新 Open Report 才重繪

7.4 內容品質警告與 skill recovery

create_html_report 通過契約驗證後,html_report_guidance 會評估可 patch 區塊數與 executive summary/limitations 關鍵字,附非阻斷 content_hints(legacy 別名 content_warnings 仍回傳同一陣列;如 HTML_CONTENT_THINHTML_MISSING_EXECUTIVE_SUMMARYHTML_MISSING_LIMITATIONS)與 delivery_guidancecontent_hint_guidance。這不 hard-block 工具成功,但 runner/外部 playbook 期望 Agent 擴寫後再 announce。

契約錯誤時 error payload 含 next_action:先 load_skilllantide:html-report-design 或 presentation 變體)→ read_skill_resourceread_report → 重建。外部 MCP Agent 可讀 playbook shared_skills.html_report_design(見 USER_GUIDE §10.5.3)。


八、Agent Memory 工具

工具 方向 說明
expand_knowledge_catalog catalog 模式下以父標籤批次載入 Rules/Info 完整條目
propose_knowledge 寫佇列 主動提案可重用知識進 Queued Knowledge;須使用者 Apply 後才注入

propose_knowledge 與被動萃取共用 knowledge_queue.json;提案入隊後仍須使用者 Apply 才注入。頻控:每則使用者訊息(整次 Agent stream)最多 2 條提案;每 conversation 最多 5 條 pending(Apply/Dismiss 後釋放)。evidence_quote 是可選的審閱依據,不要求逐字驗證;仍禁止 SQL、快取表名等執行態內容。詳見 可治理的 Agent Memory §4.3


九、UICommand 與前端 interceptor

部分工具不直接回傳字串結束,而發送 UICommand SSE:前端 handleUICommand 執行 open_tabupdate_tab_contentset_tab_resultssource_run_completefocus_project 等,並以 processedEventIds 去重。使用者看見 tab 與結果變化,形成「副駕駛」體驗。見 §12.6


十、ask_user:暫停/恢復

  1. Agent 呼叫 ask_user
  2. SSE type: ask_user → 前端互動區
  3. 使用者回覆 → POST /api_v1/ask_user_response
  4. 結果餵回 ReAct,繼續迭代

逾時約 5 分鐘。探索配額、執行模式、Execute 確認等皆走此協議(Agent 時代的數據分析工作流)。


十一、Token 壓縮策略

三種策略在不同時間尺度互補,可同次 ReAct 輪次內先後觸發

  • 歷史截斷(跨輪):長對話保留分析語境,丟棄較早無關訊息(Prompt 與 Context 工程 §十二)。
  • Mid-turn compaction(單輪內):一輪多次 tool 後 token 逼近上限,壓縮本輪 tool 結果,保留 step_id、快取名等關鍵欄位(同篇 §十三)。
  • Auto-continuation:模型輸出因 length 截斷時,在仍有餘量下追加續寫請求(§4.3);與 context window discovery 聯動。

十二、前端串流(可選)

streamStateMachine 將 SSE 分派為 thought / tool_call / tool_result / message 等 Segment,以 mutable draft + requestAnimationFrame 節流 flush,避免每 token 觸發 React re-render;結束/錯誤/abort 路徑 forceFlush。


十三、SQL 執行工具(與統一查詢層對齊)

工具 作用
run_sql_tab 執行持久分頁,物化快取
source_run_sql_tab Source Run DAG
sql_tab_result 讀取分頁快取結果(支援以分頁標題解析)
validate_query 探索:僅驗證;SQL Server 估計 >500k 列可回 warning_required
run_query 預覽列(≤200);條件物化;大結果須 large_remote_warning_id

工具回傳的品質訊號也會進入 runner 判定:complexity_hint 代表需要拆小步驟;optimization_hint 只是建議可考慮 cache,不阻擋分析。Release smoke 的 hard gate 聚焦在 plan → todo → report、無明顯錯誤迴圈與報告品質訊號;DAG / Source Run 則是 soft signal,用來觀察可追溯能力是否被模型自然使用。


十四、External MCP Agent runtime

Lantide 除 Built-in ReAct 外,亦作 本機 MCP Server 供 Cursor 等外部 client 連入(USER_GUIDE §13)。

維度 Built-in Agent External MCP Agent
對話面 Lantide AI 面板 外部 client
工具面 內建 tool registry compact core + capability search/read/write invokers;底層仍由 MCP ToolPolicy + adapters 執行
Writer 互斥 active Execute/Admin writer session 時內建 Agent 暫停
導覽 直接 UI playbook resources(external_agent_playbookrecommended_next_steps
Plan 核准 GUI Approve & Execute Execute 同左;Admin 單開可 claim_plan_approved
Config 交付 具名 mcpServers entry(lantide-quick-<workspace>lantide-<alias>);client 端合併而非整檔覆寫
原生 Reference ProjectFocused/PlanPlanning,受可信對話確認治理 Draft/Execute/Admin 可用 add_reference;Observe 不暴露,與 Internal AgentState 正交

外部 Agent 的 UICommand 與 mutation 仍寫入同一 workspace artifact、Activity 與 audit。Cloud folder/SQL Server 管理走 MCP admin_* 工具;在 Execute connection 上,需密文的 Admin 操作經 GUI 審批後轉 Connections 補憑證(Approve all 遇 handoff 暫停)。查詢語意與 Built-in 共用 統一查詢層 §9。HTML 產出應走 skill + content_warnings 路徑(§7.4)。

Initialize 後 unbound 的 All-workspaces profile(Observe/Execute/Admin):第一次 tools/list 回傳依 Access Mode 固定的 compact session catalog;core 直接呼叫,長尾能力由 search_capabilities 回傳 stable capability id、schema/policy digest 與 read/write route,再由分離 invoker 送入同一 canonical executor。搜尋使用單一動作的英文 action + object query;搜尋本身不執行能力,一般探索維持 side_effect=any,而 domain 是 optional single enum filter,不是 tool activation。外部 Agent 不暴露或呼叫任何 activate_*:HTML 與 Execute/Admin 的 11 個統計能力都直接搜尋,其中統計 formal execution 走 write invoker 與參數確認。invoker 不繞過原 ToolPolicy、mode、confirmation、evidence、Activity 或 audit。select_workspace(僅 Admin 可 create_workspace_and_select)只解除 workspace-bound execution precondition,不改變 catalog,也不發出 notifications/tools/list_changed。未綁定時呼叫 workspace-bound 工具會 fail closed 為 WORKSPACE_SELECTION_REQUIRED;綁定後直接呼叫 get_analysis_context,不重新 list。Observe 不取得 writer ownership。Execute/Admin 為單線:一次一個選定 writer 工作區;切換時撤銷舊區 execution grant 與該 session 的 pending reviews。Playbook 版本見 runtime PLAYBOOK_VERSION(現行 s4.21)。

十五、工程決策總覽

決策 原因
自研 ReAct SSE + 狀態 + UICommand
探索 ask_user 續跑 人類確認而非硬停
條件 HTML 工具 降預設 schema 噪音
max_iterations 100 長鏈分析可完成
UICommand 真實 IDE 副作用
Release gate 重分析品質 不讓快取形狀取代可信報告

十六、結語

Agent 架構是工作流與 Context 的執行引擎:ReAct 迴路把分層 prompt 變成 SSE 事件、工具呼叫與 IDE 副作用(UICommand),Harness 與聊天沙箱的差異在於執行綁定真實工作區。

若模型「一直 validate 卻不 run_query」,請同時檢查 Prompt 與 Context 工程 的 QEM 說明與本文探索預算;查詢物化與血緣語意見 統一查詢層