配套②:自行实作 ReAct 串流回圈,以换取对事件型别、状态过滤与 IDE 整合的完全控制。操作见 使用者指南 §12。
系列位置
完整导读见 系列导读与产品定位。
| 顺序 | 文章 | 本题 |
|---|---|---|
| 0 | 系列导读与产品定位 | 系列导读与产品定位 |
| 1–3 | Agent 时代的数据分析工作流、可治理的 Agent Memory、Prompt 与 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 fallback、token 压缩等产品级策略
因此采用 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_plan、open_report、HTML 条件工具 |
| PlanPlanning | 文件与探索;限量 run_query 探数允许;无 SQL Tab 工具群、无统计 run_*(prompt 禁止 activate_analysis) |
| PlanExecuting | run_sql_tab、source_run_sql_tab、add_report…;不含 update_report/patch_report(正式 closeout 须新增 Report) |
| ProjectFocused / PlanPlanning | 除读取/小修与授权汇入外,可用 lantide:reference-authoring/add_reference 原生建立正文+alias+必填 Intro |
| ProjectFocused / PlanPlanning / PlanExecuting | read_reference、patch_reference;授权汇入时另有 prepare_external_ref/get_external_ref_import/save_external_ref/read_reference_source(lantide:load-external-ref) |
State prompt 不列工具目录 — 名称与参数以当轮 function schemas 为准(见 Prompt 与 Context 工程)。
3.1 Ask mode(内建 capability ceiling)
内建聊天 composer 可在 Agent 与 Ask 之间切换(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_tab/source_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_tables、read_schema、validate_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_tokens 由 resolve_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_reference 做 docs_k=1(按 iteration 批次):仅保留最近一轮(含同轮并行多份)全文,更早批次压为最小 stub。完整 read_docs 与可配 K 见 PRD 260706_agent-token-p1-compact-read-docs。
4.4 Mid-turn 状态转换
focus_project、add_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 Ledger:run_query / run_sql_tab 成功或失败后写入步骤索引;system prompt 附加精简摘要(快取名 + SQL 预览,≤1500 字元),使多轮后模型仍知近期步骤。完整 SQL 靠 query_result、get_cache_source 或 registry 查阅。
与 Plan Progress 分工: Plan 执行期的 Steps UI 与 read_plan 的 execution_record.steps 来自 *.progress.json 的完整 steps[](report 后 steps_frozen);Ledger 仅保留最近 50 笔服务对话连续性。fork_conversation 会复制来源 conv 的保留窗与下一流水号,使 fork 后 list_query_steps 与摘要延续;正式 Plan 快照不受这个上下文保留窗限制。
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_text、get/patch_html_chunk_by_id(含
mode=full与mode=replace/replacements)、insert_html_chunk、delete_html_chunk、get/patch_html_styles 等 - 注入 Editing HTML Reports state 段
同轮 create_html_report 成功后,下一 iteration 重算 flag 与 schemas,无需换 tab。Ask mode 开启时上述写入工具一律不可用(含 Generate)。
Scoped 结构编辑: 「加一段/删一段」优先 insert_html_chunk/delete_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;deprecatedlantide.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_THIN、HTML_MISSING_EXECUTIVE_SUMMARY、HTML_MISSING_LIMITATIONS)与 delivery_guidance/content_hint_guidance。这不 hard-block 工具成功,但 runner/外部 playbook 期望 Agent 扩写后再 announce。
契约错误时 error payload 含 next_action:先 load_skill(lantide:html-report-design 或 presentation 变体)→ read_skill_resource → read_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_tab、update_tab_content、set_tab_results、source_run_complete、focus_project 等,并以 processedEventIds 去重。使用者看见 tab 与结果变化,形成「副驾驶」体验。见 §12.6。
十、ask_user:暂停/恢复
- Agent 呼叫
ask_user - SSE
type: ask_user→ 前端互动区 - 使用者回复 →
POST /api_v1/ask_user_response - 结果喂回 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_playbook、recommended_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、availability、data access 与 workspace read scope,再由分离 invoker 送入同一 canonical executor。种类相符的 invoker 亦接受 core capability 作为 host routing 相容路径,但不改变其 schema、权限或 lifecycle。compact core 的公开 schema 避免 oneOf/not 等部分 host 会静默省略的组合;add_plan 对 external session 只公开 inline verification,互斥与 legacy receipt 相容由 server 处理。搜寻使用单一动作的英文 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;成功 response 直接内嵌 analysis_context,只有 refresh trigger 或需要最新状态时才再呼叫 get_analysis_context。analysis-context.v2 的 Knowledge manifest 只揭露 metadata,正文由 read_applicable_knowledge 按需读取已核准的 User/focused Project Knowledge。未绑定时呼叫 workspace-bound 工具会 fail closed 为 WORKSPACE_SELECTION_REQUIRED。Observe 不取得 writer ownership。Execute/Admin 为单线:一次一个选定 writer 工作区;切换沿用 owner_key 并保留原区 Plan 版本核准与待审纪录;实际执行中或待恢复操作须先完成,返回时重新取得 writer 并检查版本。已绑定后可用 invoke_read_capability(target_workspace_id=…) 对其他 Workspace 做 default-deny、snapshot-safe artifact read,不切 GUI/writer,也不撤销现有 grant/review;live source、query、GUI handoff 与 write 不在此平面。Playbook 版本见 runtime PLAYBOOK_VERSION(现行 s4.40)。
Managed Workspace 与 External Delivery: 外部 Execute/Admin Agent 可用 durable operations 准备、暂存、储存、inspect、规划及套用单一 Workspace bundle;Execute 需 GUI approval,Admin 可留下 warning/Activity/audit 后 direct apply,加密输入只接受 GUI-issued opaque access。Report 的 External Delivery 则由 list/link/update/unlink_external_delivery 管理 P4/KM/URL/Other pointer metadata;跨 Workspace 只允许 list,不会自动抓取或验证外部内容,HTTP/HTTPS location 可由使用者主动开启。
十五、工程决策总览
| 决策 | 原因 |
|---|---|
| 自研 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 说明与本文探索预算;查询物化与血缘语意见 统一查询层。