137 lines
8.2 KiB
Markdown
137 lines
8.2 KiB
Markdown
# 本地交互式回答
|
||
|
||
统一样式见 [样式设计指南](style-design-guide.md),聊天布局与报告交互见 [Agent 交互与报告设计](agent-chat-ui-design.md)。本文保留协议、组件参数与业务交互约定。
|
||
|
||
聊天保留 AI SDK `UIMessage`、AI Elements 与现有 Agent 后端,新增
|
||
`@json-render/core` / `@json-render/react` 的本地注册组件。
|
||
组件代码随前端构建发布,无 ChatKit iframe、domain key 或 Vercel/OpenAI
|
||
托管 UI 请求。模型仍使用 Agent 的既有配置与工具调用能力。
|
||
|
||
## 可用内容与交互
|
||
|
||
`Stack`、`Section`、`Text`、`Metric`、`Table`、`Chart`、`Evidence`、
|
||
`MapLocation`、`FollowUpForm` 共九种组件。图表可切换折线、柱状和数据表;
|
||
表格可搜索、排序和选择行,图表可切换已加载区间、显隐序列并选点追问;
|
||
证据可按类别筛选、折叠并标注来源;地图定位通过现有控制器执行;表单从空值开始,
|
||
由用户主动提交,提交形成普通用户消息。业务证据是否保存以 Agent 实际工具结果为准。
|
||
|
||
Agent 通过 `present_answer` 发布完整的局部界面块,可在两块之间继续输出文字。
|
||
消息顺序为文字、`data-answer_ui`、后续文字与最终报告。同一轮内相同
|
||
`block_id` 更新原块;下一轮不会修改上一轮回答。
|
||
这是按内容块逐步出现的界面,不是逐 token 编译 JSX 或每个 JSON 属性都立即显示。
|
||
结构化消息与普通聊天共用持久化、历史与断线恢复通道。
|
||
|
||
生成期间展开显示过程;`final_answer` 到达后,仅展开最后报告的完整文字与
|
||
`ui_block_ids` 明确引用的已发布组件,其余文字、界面与工具轨迹默认折叠在
|
||
“查看处理过程”中。报告组件按引用顺序排列,同一组件不会同时作为报告和过程
|
||
挂载,避免重复表单。未被报告引用的过程界面只供回看。
|
||
|
||
已发布交互界面的普通/协作聊天在最终答复完成、且没有待完成工具时停止本轮原生
|
||
生成,防止最终报告之后反复自动修改界面。该收尾不标记后台调查任务完成;
|
||
后台执行继续等待其原有运行生命周期与 Server 完成/失败接口。
|
||
|
||
后台调查执行继续走任务报告与任务展示通道,聊天界面块不构成业务完成或浏览器回执。
|
||
|
||
## 正式报告与主工作区
|
||
|
||
正式报告可携带 `ui_blocks?: Array<{ block: AnswerUiBlock; evidence_refs: string[] }>`,最多 8 块,总 UTF-8 JSON 不超过 400000 字节。每块绑定报告中的已有证据,Server 验证任务归属;事实字段与审批不由 UI 块替代。组件树与聊天共用 `answer-ui@1`。例如:
|
||
|
||
```json
|
||
{
|
||
"ui_blocks": [{
|
||
"evidence_refs": ["saved-evidence-id"],
|
||
"block": {
|
||
"version": "answer-ui@1",
|
||
"block_id": "pressure-summary",
|
||
"fallback_text": "已保存记录中的压力为 0.30 MPa。",
|
||
"spec": {
|
||
"root": "pressure",
|
||
"elements": {
|
||
"pressure": { "type": "Metric", "children": [], "props": {
|
||
"label": "压力", "value": "0.30", "unit": "MPa", "source": "已保存监测记录"
|
||
} }
|
||
}
|
||
}
|
||
}
|
||
}]
|
||
}
|
||
```
|
||
|
||
此片段仅展示新增字段,发布时仍需完整报告与真实存在的证据编号。保存沿用 `agent-artifacts`、报告 JSON、幂等键和 revision;读取沿用报告列表、指定版本、任务详情及 Agent context,不新增写入端点或数据库表。
|
||
|
||
`InvestigationReportBlocks` 将正式报告接入 `AnswerUiView`,证据组件使用保存记录中的摘要、来源、时间与类别。未知版本或无法解析的块展示 fallback,报告固定事实仍可读。报告追问携带 `report_revision`、`reportGeneration` 和引用文本;表单先带入会话,确认后才发送。
|
||
|
||
`ExpandableReading` 将同一个 React 内容实例移至主工作区,保留图表视图、搜索、行选择、表单草稿。关闭恢复入口焦点,重开恢复阅读位置。主工作区宽度至少 960px 可对照地图,其余宽度在两者之间切换;原地图实例保留。
|
||
|
||
## 扩展组件
|
||
|
||
1. 在 `src/features/agent/answer-ui/catalog.ts` 增加严格的 Zod 参数定义、用途描述与必要的数据上限。
|
||
2. 在 `answer-ui-view.tsx` 注册 React 组件。需要工作台交互时通过 Agent 公共 API 的回调接入,保持依赖方向。
|
||
3. 从本仓库运行 `pnpm export:answer-ui-contract`,更新相邻 Agent 仓库的
|
||
`src/chat/answer-ui-catalog.json` 和 Server 的 `contracts/answer-ui-catalog.json`。组件目录是唯一参数定义源,生成文件需随三端一起交付。
|
||
4. 如新组件存在跨字段约束,同时更新三端的协议校验并加边界和交互测试。
|
||
|
||
导出脚本接受可选目标文件:
|
||
|
||
```bash
|
||
node scripts/export-answer-ui-contract.mjs /path/to/agent/src/chat/answer-ui-catalog.json
|
||
```
|
||
|
||
前端沿用 Zod 3,json-render 0.21 两个包通过作用域 override 使用相同 schema
|
||
运行时;实际组件渲染和参数校验已有聚焦测试。升级 json-render 时必须保留这组
|
||
兼容性测试,不能只根据包版本判断可用。Agent 原生插件仍使用其 Zod 4 工具边界,
|
||
执行后再按生成的 JSON Schema 校验组件参数。
|
||
|
||
## 校验边界
|
||
|
||
三端拒绝未知组件、额外属性、任意脚本/事件定义、缺失引用、循环、重复或游离节点。
|
||
每块最多 48 节点、深度 12、完整序列化结果不超过 100000 个 JavaScript UTF-16 代码单元,图表必须对齐且数值有限;表格每行与列数
|
||
一致,表单字段名唯一。组件只消费明确的 props,没有动态代码、网络 URL 或任意动作 DSL。
|
||
无法解析的历史内容显示该块的 `fallback_text`。
|
||
|
||
## 验证
|
||
|
||
```bash
|
||
pnpm lint
|
||
pnpm typecheck
|
||
pnpm test src/features/agent/answer-ui src/features/workbench/hooks/agent-session-message-state.test.ts
|
||
pnpm test:browser tests/browser/agent-answer-ui.e2e.ts tests/browser/agent-start-page.e2e.ts tests/browser/agent-report-workspace.e2e.ts
|
||
pnpm build
|
||
```
|
||
|
||
Agent 仓库:
|
||
|
||
```bash
|
||
bun run check
|
||
bun test tests/chat/answer-stream-projection.test.ts tests/chat/answer-ui.test.ts tests/routes/chatStream.test.ts tests/routes/taskCollaboration.test.ts tests/runtime/opencode.test.ts
|
||
bun run tests/integration/answer-ui-smoke.ts
|
||
```
|
||
|
||
原生 smoke 使用独立临时数据库,不发模型推理请求。浏览器用例用明确标注的演练
|
||
事件验证组件、更新、历史恢复、键盘展开证据、表单提交和 320/375px 布局,不能当作
|
||
真实模型已正确选择组件或现场资料已核验的证明。
|
||
|
||
前端、Agent 与 Server 需同步更新;Agent 进程需重新加载原生插件,Server 需加载新报告 schema。无需新增环境变量、
|
||
domain key 或数据库迁移。本轮不重启既有服务;浏览器演练和本地测试不代表运行中的服务已完成同步。
|
||
|
||
## 真实模型验收(显式启用)
|
||
|
||
以下浏览器用例直接连接本机 Agent,输入均为明确的演练资料。工作台地图与项目
|
||
读取使用测试数据,聊天、模型工具调用、流式 UI、聊天持久化与表单回传使用真实
|
||
服务。每次创建独立验收会话,结束后仅删除该会话,不删除既有用户记录。此用例
|
||
会发送模型请求,默认浏览器测试不执行它。
|
||
|
||
```bash
|
||
TJWATER_ANSWER_UI_REAL_AGENT_URL=http://127.0.0.1:18787 \
|
||
TJWATER_ANSWER_UI_REAL_MODEL_LABEL=专家 \
|
||
pnpm test:browser tests/browser/agent-answer-ui-real.e2e.ts --workers=1
|
||
```
|
||
|
||
先在 Agent 仓库启动隔离实例,可指定独立 INTEGRATION_DATA_DIR 保留联调数据。
|
||
可省略模型标签来使用默认模型。该用例不是生产 OAuth 登录或真实供水业务核验。
|
||
|
||
Vite 统一解析 React/React DOM,Playwright 按端口使用独立的依赖缓存,避免同时
|
||
运行的开发服务将旧临时目录的 React 预构建写回测试缓存。
|
||
|
||
消息入口为 `POST /api/v1/responses`,采用 Responses 文本输入、Response 输出和标准 SSE 生命周期。前端 `ResponsesChatTransport` 将网络事件转换为已有 UIMessage 渲染数据;审批、凭据续期、地图动作及 answer-ui 走 `tjwater.*` 扩展事件,不将 AI SDK 的 UIMessage 流作为网络协议。会话、模型及动作回执等接口统一使用 `/api/v1` 前缀,原 `/agent/chat` 路径已移除;Agent 服务和现有会话数据保留。
|