Skip to content

Interaction Components

Interaction Components

1. 目标 & 范围

目标:在 AI 访谈流程中,Agent 可通过 render_interaction tool call 按需渲染标准化交互组件;受访者完成交互后,结构化结果经独立 session submit API 回传并持久化,为后续抽取与 evidence 构建提供结构化数据源。

MVP 范围(P0)

  • 4 种必做组件:single_choice(单选)、multiple_choice(多选)、likert(量表)、ranking(排序)。
  • Interaction Registry:InteractionType → React 组件映射表。
  • 统一提交行为:InteractionToolResult 格式、跳过(skip)、校验(min/max_items)。
  • 组件状态:disabled / loading / submitted
  • 无障碍(a11y)与移动端适配。
  • Unsupported 兜底组件(UnsupportedInteraction)。

后续范围(P1+,本 PRD 不展开)

nps / rating / matrix / text_input / modal_form / concept_card / comparison / consent / file_upload / image_annotation / task_confirmation

非目标

  • 组件不直接调用 Agent API;提交统一由 InterviewShell 代理。
  • 组件不负责 evidence 抽取逻辑(见 06_extraction_evidence)。
  • Study Builder 中的组件预览不在本模块范围(见 03_study_builder)。

2. 用户故事

作为受访者(participant),我希望在访谈对话流中看到内嵌的交互卡片,以便快速完成结构化选择而不必离开对话界面。

作为受访者(participant),我希望在不确定或不愿回答时可以跳过组件,以便访谈不因强制填写而中断。

作为受访者(participant),我希望提交后看到轻量摘要(如”你选择了:人工整理成本高”),以便确认自己的选择已被记录。

作为研究人员(researcher),我希望 Agent 渲染的每次交互结果都能持久化并与 transcript 关联,以便在结果页面追溯具体选择与对话上下文。

作为研究人员(researcher),我希望组件校验(如多选最多 3 项)由前端执行,以便减少无效数据进入抽取流程。

作为 Admin/Developer,我希望新增交互类型时只需在 registry 中注册一条映射,以便扩展成本可控。


3. 功能需求

3.1 Interaction Registry

FR-05-1 优先级:P0
前端维护一个 interactionRegistry 映射表,将 InteractionType 字符串映射到对应 React 组件。

  • 验收要点:MVP 阶段注册 single_choice → RadioCardGroupmultiple_choice → CheckboxCardGrouplikert → LikertScaleranking → RankingList 四条映射,缺失时回退到 UnsupportedInteraction

FR-05-2 优先级:P0
InteractionToolCall.type 不在 registry 中时,渲染 UnsupportedInteraction 兜底组件,显示类型名与提示文案,不阻断对话流。

  • 验收要点:传入未注册的 type(如 nps)时,页面显示兜底卡片,控制台无未捕获异常,allow_skiptrue 时兜底组件亦展示跳过按钮。

3.2 MVP 组件:single_choice(单选)

FR-05-3 优先级:P0
RadioCardGroup 根据 InteractionToolCall 中的 options[] 渲染单选卡片列表,每项含 label、可选 description;用户点击一项后高亮选中状态,点击”提交”触发 onSubmit

  • 字段要求:options[].id(必填)、options[].label(必填)、options[].description(可选)、options[].exclusive(标记互斥兜底,单选组件默认全部互斥,此字段保留用于 schema 一致性)。
  • 验收要点:选中项 selected_option_idselected_label 写入 valuerequired: true 时未选择不可提交;allow_skip: true 时展示跳过按钮;display.mode 默认 inline_card

FR-05-4 优先级:P0
RadioCardGroup 提交后切换为 submitted 状态:所有选项变为只读,选中项保持高亮,提交按钮消失,展示轻量摘要文案(如”你选择了:配置复杂”)。

  • 验收要点:submitted 状态下无法重新点选;页面上只读展示,不触发二次 onSubmit

3.3 MVP 组件:multiple_choice(多选)

FR-05-5 优先级:P0
CheckboxCardGroup 渲染多选卡片列表;支持 validation.min_itemsvalidation.max_items 约束,违反时禁用提交按钮并展示行内错误提示。

  • 字段要求:options[].exclusive(为 true 时选中该项自动取消其他已选项,其他项选中时自动取消带 exclusive 的项)。
  • 验收要点:超出 max_items 时新选项不可勾选(或勾选时弹出提示);未达 min_items 时提交按钮 disabledexclusive 项与普通项互斥逻辑正确。

FR-05-6 优先级:P0
提交时 value.selected_option_ids 为已选 id 数组,顺序按用户选中先后排列;submitted 后展示摘要(如”你选择了 3 项:多项目管理、证据链输出、自定义工具”)。

  • 验收要点:selected_option_ids 非空数组,每个元素均为 options[].id 中的合法值;提交后组件只读。

3.4 MVP 组件:likert(量表)

FR-05-7 优先级:P0
LikertScale 根据 scale.minscale.maxscale.min_labelscale.max_label 渲染水平分级选择器;MVP 支持 1–5 和 1–7 两种量程;用户点击分值后高亮,可点击”提交”。

  • 字段要求:scale.min(整数,通常为 1)、scale.max(整数,通常为 5 或 7)、scale.min_label(端点文案)、scale.max_label(端点文案)。
  • 验收要点:渲染分值点数量 = scale.max - scale.min + 1;端点标签在两端正确展示;required: true 时未选不可提交。

FR-05-8 优先级:P0
提交时 value.score 为用户选中的整数分值;submitted 后展示分值摘要(如”你的评分:4 / 5”)并切换只读。

  • 验收要点:score[scale.min, scale.max] 范围内;组件 submitted 后不可重新点击。

3.5 MVP 组件:ranking(排序)

FR-05-9 优先级:P0
RankingList 渲染可拖拽排序列表,每项显示 options[].label;支持拖拽(桌面)和上移/下移按钮(移动端兜底)两种交互方式;初始顺序与 options[] 数组一致。

  • 字段要求:options[].id(必填)、options[].label(必填);无额外 ranking 专属校验字段(默认要求所有项都参与排序)。
  • 验收要点:拖拽后顺序实时更新;移动端上移/下移按钮正常响应;提交时 ranked_option_ids 包含全部 options[].id,顺序即排名结果(第 0 位 = 第 1 名)。

FR-05-10 优先级:P0
提交时 value.ranked_option_ids 为排序后 id 数组,长度必须等于 options 数组长度;submitted 后展示序号化摘要(如”1. 结构化抽取 2. Agent Harness …”)并切换只读。

  • 验收要点:ranked_option_ids.length === options.length;没有重复 idsubmitted 状态下拖拽禁用。

3.6 InteractionRenderer 与提交流程

FR-05-11 优先级:P0
InteractionRenderer 接收 InteractionToolCall 对象,查找 registry 获取目标组件并渲染;渲染时向子组件传递 onSubmitonSkip 两个回调,由 InterviewShell 统一执行 API 调用。

  • 验收要点:onSubmit 调用时构造完整 InteractionToolResult(含 interaction_idtypestatus: "submitted"valuesubmitted_atclient_context)传递给上层;onSkip 调用时构造 status: "skipped"value: null 的结果。

FR-05-12 优先级:P0
提交结果经 InterviewShell 调用独立 session submit API(POST /api/sessions/{sessionId}/interactions)回传,不走标准 AG-UI tool-result 通道;回传成功后组件切换 submitted 状态,回传失败时展示错误提示并允许重试。

  • 验收要点:成功响应后 interaction_results 表有对应行(session_idtool_call_idinteraction_idstatusvaluesubmitted_at 均不为空);网络失败时组件可重试,不丢失用户已选值。见 04_interview_runtime 中的 API 调用链。

FR-05-13 优先级:P0
每次 render_interaction tool call 及其 result 均写入 tool_calls 表(tool_input = InteractionToolCalltool_output = InteractionToolResult);同时写入 interaction_results 表。

  • 验收要点:session 结束后 tool_callstool_name = "render_interaction" 的行数 = 访谈中实际触发的交互次数;每行均有对应 interaction_results 记录(含跳过)。见 03_data_model

3.7 组件状态管理

FR-05-14 优先级:P0
所有 MVP 组件须支持以下四种状态,且状态转换单向:idle → (loading →) submitted | skippeddisabled 作为外部属性可在任意状态叠加。

状态含义UI 表现
idle等待用户操作正常可交互
loading正在提交按钮 spinner,选项不可点击
submitted已提交只读,展示摘要
skipped已跳过折叠或展示”已跳过”标记
disabled外部禁用(如 session 已结束)灰化,不可操作
  • 验收要点:无法从 submitted 退回 idleloading 期间二次点击提交无效;disabled 覆盖所有其他状态。

3.8 校验

FR-05-15 优先级:P0
前端执行 InteractionToolCall.validation 中的约束:min_items / max_items(多选组件);required(所有组件)。校验失败时禁用提交按钮并展示行内错误文案,不弹 toast。

  • 验收要点:required: true 且无选择时,提交按钮 disabled,错误文案在卡片内展示;max_items 达到上限时后续选项勾选被阻止或显示超限提示;错误状态在用户满足条件后自动清除。

3.9 跳过(Skip)

FR-05-16 优先级:P0
allow_skip: true 时,组件展示”跳过”按钮(低优先级样式,区别于主提交按钮);点击后直接构造 status: "skipped" result,组件切换 skipped 状态,不执行任何字段校验。

  • 验收要点:allow_skip: false 或字段缺失时,跳过按钮不渲染;跳过后 interaction_results.status = "skipped"valuenull;跳过 required 字段不阻断访谈,由 extraction 阶段标记 needs_review

3.10 无障碍(a11y)与移动端

FR-05-17 优先级:P0
所有 MVP 组件满足 WCAG 2.1 AA 基础要求:可完整键盘操作(Tab 聚焦、Space/Enter 选择、Arrow 切换选项);选项有语义化 aria-labelrole;状态变更(已选、错误、只读)通过 aria-* 属性反映。

  • 验收要点:使用键盘可完成 single_choice 全流程(聚焦 → 选择 → 提交);屏幕阅读器可读出选项文案与当前选中状态;提交成功后焦点移至摘要区域。

FR-05-18 优先级:P0
所有 MVP 组件在 375 px 宽度(iPhone SE 基准)下无横向滚动、无遮挡;RankingList 在移动端以上移/下移按钮替代拖拽(拖拽保留但非主交互)。

  • 验收要点:在 375 px viewport 下视觉测试通过;触摸点击区域 ≥ 44 × 44 px;移动端上移/下移按钮有效。

3.11 Timeline 集成

FR-05-19 优先级:P0
交互组件以 card 形式内嵌在 ChatTimeline 中,与 MessageBubble 混排,不弹独立全屏模态(ranking 可选 wide card 展示);submitted / skipped 后组件收缩为轻量摘要行,保持在 timeline 历史中可见。

  • 验收要点:timeline 中出现 interaction card 后不阻断滚动;提交/跳过后 card 转为摘要,不影响后续 Agent 消息渲染;同一 timeline 内不同 interaction 相互独立。

4. 关键流程

4.1 主流程:Agent 触发 → 组件渲染 → 用户提交

1. Agent Harness(LangGraph)决定调用 render_interaction tool
2. 构造 InteractionToolCall(含 type / options / validation / research_intent …)
3. 经 AG-UI 协议以 tool call 事件推送至前端
4. assistant-ui generative UI 捕获事件,InteractionRenderer 查 registry 获取组件
5. 渲染对应组件(RadioCardGroup / CheckboxCardGroup / LikertScale / RankingList)
6. 用户与组件交互(选择 / 排序)
7. 前端执行 validation;通过后启用提交按钮
8. 用户点击提交 → 组件切换 loading 状态
9. InterviewShell 调用 POST /api/sessions/{sessionId}/interactions
10. 后端写入 interaction_results + tool_calls(tool_output)
11. 返回 200 → 前端切换 submitted 状态,展示摘要
12. InterviewShell 将 InteractionToolResult 以 tool result 事件回传 Agent
13. Agent 继续对话(follow-up 或下一步)

4.2 跳过流程

用户点击"跳过" → 组件切换 loading → InterviewShell 提交 status: "skipped" → 写库 → 组件切换 skipped
→ Agent 收到 skipped result → 继续访谈(视 required 级别决定是否追问)

4.3 Unsupported 兜底流程

InteractionRenderer 查 registry → 未命中 → 渲染 UnsupportedInteraction
→ 展示"当前版本不支持此题型({type}),请跳过"→ allow_skip: true 时自动展示跳过按钮
→ 跳过后正常写 skipped result

协议消费细节见 04_interview_runtime


5. 数据

5.1 核心表

本模块写入以下两张表(完整 DDL 见 03_data_model):

interaction_results(主结果表)

字段类型说明
idUUID PK
session_idUUID FK → sessions所属 session
tool_call_idUUID FK → tool_calls对应 render_interaction tool call
interaction_idTEXTInteractionToolCall.interaction_id 一致
interaction_typeTEXTsingle_choice / multiple_choice
statusTEXTsubmitted / skipped / cancelled / expired
valueJSONB结构化结果(见各组件字段定义)
client_contextJSONBduration_ms / changed_count / device
submitted_atTIMESTAMPTZ客户端提交时间

tool_calls(tool call 记录,interaction 作为其子集)

  • tool_name = "render_interaction"
  • tool_input = 完整 InteractionToolCall JSON
  • tool_output = 完整 InteractionToolResult JSON(提交后回填)

5.2 各组件 value schema

组件value 字段
single_choice{ selected_option_id: string, selected_label: string }
multiple_choice{ selected_option_ids: string[] }
likert{ score: integer }
ranking{ ranked_option_ids: string[] }
跳过(任意类型)null

6. 接口

6.1 提交接口

POST /api/sessions/{sessionId}/interactions

请求体为 InteractionToolResult(见 03_interactive_ui_protocol):

{
"interaction_id": "int_primary_pain_001",
"type": "single_choice",
"status": "submitted",
"value": {
"selected_option_id": "manual_analysis",
"selected_label": "人工整理成本高"
},
"submitted_at": "2026-06-22T10:30:00Z",
"client_context": {
"duration_ms": 5400,
"device": "desktop"
}
}

响应:200 OK(含写入后的 interaction_result.id);400(validation 失败);409(该 interaction_id 在本 session 已提交)。

本接口为独立 REST 接口, AG-UI tool-result 通道。AG-UI tool result 事件由后端在写库成功后构造并推送,前端不直接发送。详见 05_implementation/02_api_contracts

6.2 AG-UI 事件关联

  • Agent 触发交互:ToolCallStart + ToolCallArgs 事件(tool_name = "render_interaction")。
  • 后端收到提交结果后推送:ToolCallEnd 事件(含 InteractionToolResult)。
  • 前端 assistant-ui 的 generative UI 监听 ToolCallArgs 映射到对应组件渲染,见 04_interview_runtime

7. 验收标准

AC-05-1:传入合法 single_choice InteractionToolCall,RadioCardGroup 正确渲染所有 options,选中一项后点击提交,interaction_results 表中出现 status = "submitted"value.selected_option_id 正确的行。

AC-05-2:传入 multiple_choice 并设 validation.max_items = 3,用户尝试选第 4 项时被阻止(或显示超限提示),提交按钮保持 disabled;选 1–3 项后可正常提交,value.selected_option_ids 长度在 [min_items, max_items] 范围内。

AC-05-3:传入 likert(scale 1–5),用户点击分值 4,提交后 interaction_results.value.score = 4,组件切换只读展示”你的评分:4 / 5”。

AC-05-4:传入 ranking(5 个选项),用户拖拽调整顺序后提交,interaction_results.value.ranked_option_ids 为长度 5 的数组,包含全部 options[].id 且无重复,顺序与用户最终排列一致。

AC-05-5:传入 allow_skip: true 的任意组件,点击跳过,interaction_results.status = "skipped"value = null;传入 allow_skip: false(或字段缺失)时,跳过按钮不渲染。

AC-05-6:传入 required: true 且未做任何选择时,提交按钮为 disabled,行内展示校验提示文案;完成选择后按钮恢复可用。

AC-05-7:提交成功后组件进入 submitted 状态:所有选项只读,提交/跳过按钮消失,展示摘要文案;无法通过点击恢复到 idle 状态。

AC-05-8:提交 API 请求失败(模拟 500)时,组件退回 idle(非 submitted),展示错误提示,用户可重试提交,已选值保留。

AC-05-9:传入 registry 中不存在的 type(如 nps),页面渲染 UnsupportedInteraction 兜底卡片,allow_skip: true 时展示跳过按钮,控制台无未捕获异常。

AC-05-10:使用键盘(无鼠标)完成 single_choice 全流程:Tab 聚焦组件 → Arrow 切换选项 → Space 确认 → Tab 到提交按钮 → Enter 提交;屏幕阅读器可读出选项文案与当前选中状态。

AC-05-11:在 375 px 宽度视口下,所有 MVP 组件无横向滚动,触摸区域有效,ranking 上移/下移按钮正常响应。

AC-05-12tool_calls 表中每次 render_interaction 记录在提交/跳过后 tool_output 不为空;interaction_results 表中每条记录均可通过 tool_call_id 关联到 tool_calls

AC-05-13:同一 session 中多个 interaction 依次渲染,前一个 submitted 后不影响后续 interaction 的 idle 状态;timeline 中历史 submitted card 保持可见(只读摘要形式)。


8. 边界 & 非目标

  • 不做:组件不直接发送 AG-UI tool result 事件;结果提交统一经 session submit API,后端负责构造并推送 ToolCallEnd 事件。
  • 不做:组件不持有 Agent 对话状态;InterviewShell 负责 session 生命周期管理(见 04_interview_runtime)。
  • 不做:MVP 不实现组件级多语言(i18n);title / label 由 Agent 在 tool call 中直接传入目标语言文案。
  • 不做:MVP 不做 Admin 端的交互组件预览(Study Builder 的 preview 功能属于 03_study_builder)。
  • 不做:MVP 不实现 expired 状态(session 超时后组件自动失效),留作 P1。
  • 边界display.confirm_before_submit 字段在协议中已定义,MVP 默认忽略(不弹二次确认弹窗),P1 实现。
  • 边界display.mode: "modal" 在协议中已定义,MVP 仅实现 inline_cardranking 可用 wide card,不做独立 modal。

9. 依赖 & 风险

9.1 依赖

依赖说明关联模块
AG-UI 协议 / assistant-uigenerative UI 驱动 InteractionRendererToolCallArgs 事件须正确传递 InteractionToolCall04_interview_runtime
session submit APIPOST /api/sessions/{sessionId}/interactions 须在 Interview Runtime 实现04_interview_runtime05_implementation/02_api_contracts
interaction_results / tool_calls数据持久化基础,DDL 已在数据模型中定义03_architecture/03_data_model
extraction 阶段interaction_resultscite_selection evidence 由抽取模块消费06_extraction_evidence
InteractionToolCall schema(schema_version: "2026-06-01"前端渲染依赖协议字段稳定03_interactive_ui_protocol

9.2 风险

风险可能性影响缓解措施
AG-UI tool call 事件与组件渲染时序不稳定(streaming 中断)高(组件无法渲染)InterviewShell 维护 pending tool calls 队列;streaming 断开时组件保持 idle 并提示重试
InteractionToolCall schema 字段变更导致渲染失败锁定 schema_version;前端对未知字段做 graceful ignore;UnsupportedInteraction 兜底
移动端拖拽体验不佳导致 ranking 可用性下降MVP 提供上移/下移按钮作为主交互替代方案,拖拽为增强
提交 API 与 AG-UI 事件回传顺序不一致导致 Agent 重复追问后端在写库成功后才推送 ToolCallEnd;前端 submitted 后不重发
组件类型扩展导致 registry 维护成本上升Registry 设计为声明式映射,新增类型只需一行注册,无需修改 InteractionRenderer