Interview Runtime
Interview Runtime
1. 目标 & 范围
目标
提供受访者端完整的 AI 访谈运行时能力:从受访者点击公开链接到 session 结束,全程由 LangGraph harness 驱动,经 AG-UI 协议将事件推送至前端 assistant-ui,使受访者以自然对话方式完成研究目标覆盖,并在整个过程中生成可审计、可重放的结构化 transcript。
范围(本模块)
- Session 生命周期管理(
created → active → completed/abandoned) - 消息往返与 AG-UI streaming
- Harness 决策循环(
ask_question / probe / render_interaction / extract_fields / conclude) - Consent 采集与 screening 判断
- 追问策略执行与结束条件评估
- Pending interaction 阻塞(LangGraph
interrupt()) - Summary 确认流程
非范围(见其他模块)
- 交互组件 UI 实现见 05_interaction_components
- 字段抽取与 evidence 链见 06_extraction_evidence
- Study 配置与发布见 03_study_builder
- 结果查看与修正见 07_results_review
2. 用户故事
作为受访者,我希望通过公开链接无需注册即可进入访谈,以便降低参与门槛,让我专注于表达意见而非理解系统。
作为受访者,我希望 AI 以自然对话方式提问,而不是展示一份表格问卷,以便感受到被倾听、愿意深入分享。
作为受访者,我希望交互组件(选择、评分等)自然出现在对话流中,以便在需要时快速表达结构化偏好,而不打断叙述节奏。
作为受访者,我希望在访谈开始前看到数据使用说明并确认 consent,以便了解自己的数据如何被使用。
作为受访者,我希望随时可以结束访谈,以便在任何时刻退出而不感到被强制。
作为研究人员,我希望每个 session 完整记录 transcript、tool call、决策日志与 event log,以便事后审计 agent 行为,重放与排查问题。
作为研究人员,我希望 harness 自动追问直到覆盖 must objectives 或达到上限,以便不依赖人工主持也能获得足够深度的访谈数据。
3. 功能需求
3.1 Session 创建与初始化
FR-04-1(P0)受访者通过公开 slug 创建 session
- 调用
POST /api/public/studies/{public_slug}/sessions,返回session_id与初始状态created - 验收要点:无需认证、study 必须处于
published状态、返回session_id与status: "created"
FR-04-2(P0)Session 记录配置版本快照
- 创建时锁定并记录
study_config_version_id、agent_config_version_id、output_schema_version_id、model_config_version_id - 验收要点:快照字段非空;后续对 study 配置的修改不影响已进行中的 session
FR-04-3(P0)Session 状态机完整支持
- 状态流转:
created → active → waiting_for_user / waiting_for_tool → active → completed / abandoned - 验收要点:每次状态变更写入 event log;
completed与abandoned为终态,不可回退
3.2 Consent 与 Screening
FR-04-4(P0)Consent 采集
- Harness 在
CONSENT_OR_SCREENING阶段通过render_interaction渲染ConsentPanel,受访者必须提交status: "submitted"才可继续 - 验收要点:受访者拒绝 consent 则 session 状态置为
abandoned,后续 harness 不再推进;consent 结果记入 transcript
FR-04-5(P0)Screening 判断
- Harness 可在 consent 后向受访者提问 screening 问题;基于回答由 LLM 判断是否符合研究目标
- 验收要点:不符合 screening 条件时 session 以
abandoned结束并给出友好告知;符合条件时进入OPENING阶段
3.3 消息往返与 AG-UI Streaming
FR-04-6(P0)受访者发送消息
- 调用
POST /api/sessions/{session_id}/messages,后端存入 transcript turn,唤醒 harness 决策循环 - 验收要点:消息写入后 session 状态由
waiting_for_user切换为active;turn 有唯一turn_id
FR-04-7(P0)AG-UI 事件流
- 后端 LangGraph harness 经 AG-UI 协议(HTTP/SSE)向前端推送业务语义事件,前端 assistant-ui 通过
@assistant-ui/react-ag-uiadapter 消费 - 支持的
StreamEvent类型(见 api_contracts):message.delta:assistant 消息增量message.completed:消息结束,携带turn_idtool.call:harness 工具调用通知(render_interaction/extract_fields/conclude)interaction.render:前端渲染交互组件extraction.completed:轻量抽取结果session.completed:session 结束,携带摘要error:运行时错误
- 验收要点:前端流式渲染
message.delta;tool.call后前端展示 pending 状态;error时前端给出非技术性提示
FR-04-8(P0)前端禁用 chatbot 式编辑
- 受访者 Interview 端 ChatTimeline 禁用 edit / regenerate / branch 操作
- 验收要点:已发出的消息无法修改;无”重新生成”按钮;操作历史严格线性
3.4 Harness 决策循环
FR-04-9(P0)Harness 状态机节点
- Harness 经以下节点顺序执行(对应
02_agent_harness.md状态机):LOAD_CONTEXT → CONSENT_OR_SCREENING → OPENING → ASK_OR_PROBE → OPTIONAL_INTERACTION → EXTRACT → EVALUATE_COVERAGE → NEXT_TOPIC_OR_PROBE → SUMMARY_CONFIRMATION → CONCLUDE → POST_SESSION_EXTRACTION → END - 验收要点:每次节点切换写入
AgentDecisionLog;LangGraph checkpointer 持久化每个节点后的 graph state
FR-04-10(P0)Agent 动作执行
- Harness 在每个决策节点输出且仅输出以下动作之一,并记录
rationale:ask_question:提出新问题,关联objective_idprobe:对当前回答追问,携带based_on_turn_idrender_interaction:触发交互组件渲染extract_fields:触发字段抽取conclude:结束 session,携带reason
- 验收要点:每次决策有且仅有一个动作;
rationale字段非空;决策日志含model、prompt_version_id、latency_ms、token_usage
FR-04-11(P0)AgentDecisionLog 持久化
- 每次 harness 决策写入
AgentDecisionLog(含session_id、turn_id、phase、input_summary、selected_action、rationale、model、latency_ms) - 验收要点:decision log 可通过
GET /api/sessions/{session_id}关联查询;不得丢失任何决策记录
3.5 追问策略
FR-04-12(P0)先开放后结构化追问
- Harness 对每个 objective 先发出开放式问题,待受访者给出泛泛回答后再
probe或render_interaction - 验收要点:同一 objective 下第一个动作为
ask_question,不直接以render_interaction开场
FR-04-13(P0)最大追问次数限制
- 每个
StudyObjective配置max_probe_count(默认值:3);harness 对单个 objective 的probe动作不超过该上限 - 验收要点:probe 计数存入
SessionState.covered_objectives[obj_id];达到上限后 harness 转向下一个 objective 或触发extract_fields
FR-04-14(P0)追问触发条件
- Harness 应在以下条件触发
probe:回答过于泛泛(无具体例子)、回答含强情绪(追问影响与原因)、回答存在内在矛盾(追问澄清)、关键字段缺失(targeted probe) - 验收要点:probe
rationale须指明触发原因(可人工审查)
FR-04-15(P1)用户疲劳识别
- Harness 检测到受访者回答明显变短或频繁发出结束意愿信号时,优先触发
conclude而非继续追问 - 验收要点:测试场景中,连续 3 次单字回答后 harness 不再继续
probe
3.6 交互组件(Pending Interaction)
FR-04-16(P0)render_interaction 触发流程
- Harness 动作
render_interaction→ 后端生成InteractionToolCall(含interaction_id、type、schema_version、research_intent)→ 经interaction.renderAG-UI 事件推送 → 前端InteractionRenderer按 type 路由至对应组件 → 受访者提交后调用POST /api/sessions/{session_id}/interactions/{interaction_id}/submit→ 结果写入 transcript → harness 继续 - 验收要点:全流程无断点;
interaction_id全局唯一;tool call 与 tool result 均写入 event log
FR-04-17(P0)LangGraph interrupt 阻塞
render_interaction触发后,LangGraph graph 通过interrupt()暂停,session 状态切换为waiting_for_tool;收到submit请求后恢复执行- 验收要点:
waiting_for_tool期间受访者发送新消息不触发 harness 前进;submit 恢复后 session 状态回到active
FR-04-18(P0)交互结果记录
- 每次 interaction 提交写入
tool_calls、interaction_events、transcript_turns、session_state;skipped 结果同样记录(status: "skipped") - 验收要点:
GET /api/sessions/{session_id}返回的tool_calls包含对应记录;研究人员在结果页可查看每次组件的提交值
FR-04-19(P0)每屏单一主交互原则
- 同一时刻 ChatTimeline 中最多一个交互组件处于待提交状态;前一个组件未提交或跳过前,harness 不推送新的
render_interaction - 验收要点:并发提交测试中不出现两个并行 pending interaction
3.7 结束条件
FR-04-20(P0)Must objectives 全覆盖结束
- 所有
priority: "must"的 objective 状态达到covered时,harness 可触发conclude - 验收要点:
conclude的reason字段值为"all_must_objectives_covered"
FR-04-21(P0)关键字段 confidence threshold 结束
- 当 output schema 中所有
required: true字段的confidence均达到配置的confidence_threshold时,harness 可触发conclude - 验收要点:
conclude前最后一次extract_fields的结果可验证 threshold 已满足
FR-04-22(P0)最大轮次 / 时长结束
- 达到 study 配置的
max_turn_count或max_duration_minutes时,harness 强制触发conclude,reason为"max_turns_reached"或"max_duration_reached" - 验收要点:配置 max_turn=5 的 study,第 5 轮结束后 harness 不再问新问题
FR-04-23(P0)受访者主动结束
- 受访者发送明确结束意愿信号(如”结束”、“我说完了”)时,harness 识别并触发
conclude,reason为"participant_requested_end" - 验收要点:测试场景中受访者发送”结束”后 session 进入 summary 确认流程,不再继续追问
FR-04-24(P0)Screening 失败 / safety 触发结束
- Screening 判断不符合或 safety/privacy policy 触发时,harness 触发
conclude,reason为"screening_failed"或"safety_triggered",session 状态置为abandoned - 验收要点:
abandonedsession 不出现在研究人员结果页的有效 session 列表中
3.8 Summary 确认
FR-04-25(P0)session 结束前 summary 确认
- Harness 在
conclude前执行summarize_and_confirm动作,向受访者展示本次访谈摘要,受访者可补充或确认 - 验收要点:
StreamEvent类型session.completed携带summary字段;前端展示CompletionScreen组件
FR-04-26(P1)后处理抽取
- Session 进入
completed后,异步触发POST_SESSION_EXTRACTION,对全量 transcript 执行最终 extract_fields - 验收要点:最终抽取结果写入 DB;结果页可查看(见 06_extraction_evidence)
3.9 受访者体验约束
FR-04-27(P0)受访者无需理解 Agent / 工具概念
- 前端不向受访者暴露”工具调用”、“Agent 决策”、“LangGraph”等内部概念;所有 streaming 动作对受访者表现为自然的消息或组件
- 验收要点:ChatTimeline 中无 tool call 原始 JSON;loading 状态显示”正在思考…”而非技术性信息
FR-04-28(P0)访谈中断恢复
- 受访者关闭页面后,凭同一
session_id重新打开可继续未完成的 session(session 未达到终态) - 验收要点:
waiting_for_user状态的 session 重新加载后,ChatTimeline 恢复已有 transcript;受访者可继续作答
4. 关键流程
4.1 完整 Session 流程
1. 受访者访问公开 URL /interview/{public_slug}2. 前端调用 POST /api/public/studies/{public_slug}/sessions → session_id3. 建立 SSE 连接,接收 AG-UI 事件流4. Harness: LOAD_CONTEXT(加载 study config、锁定版本)5. Harness: CONSENT_OR_SCREENING 5a. render_interaction(consent) → 受访者确认 → transcript 5b. screening 问答 → 不符合 → abandoned;符合 → 继续6. Harness: OPENING → ask_question(开场问题) → AG-UI message.delta / message.completed → ChatTimeline 渲染7. 受访者回复 → POST /api/sessions/{session_id}/messages8. Harness: ASK_OR_PROBE(评估 coverage) → probe / ask_question / render_interaction9.(若 render_interaction)→ LangGraph interrupt() → interaction.render 事件 → InteractionRenderer 展示组件 → 受访者提交 POST .../interactions/{id}/submit → harness 恢复10. EXTRACT(checkpoint extraction)→ extraction.completed 事件11. EVALUATE_COVERAGE → 未覆盖 → 回到步骤 8;已覆盖 → 继续12. Harness: SUMMARY_CONFIRMATION → summarize_and_confirm → 受访者确认 / 补充 → 再次 extract13. Harness: CONCLUDE → session.completed 事件 → 前端展示 CompletionScreen14. POST_SESSION_EXTRACTION(异步)4.2 Runtime Event Flow(引用 01_system_architecture)
受访者发送消息 → Session API 接收 → 存入 transcript turn → Harness 加载 state → 检查 coverage 与 next objective → 可选 RAG 检索 → LLM 决策下一动作(ask / probe / render_interaction / extract / conclude) → 后端推送 AG-UI 事件流 → 前端渲染消息或交互组件 → 受访者提交交互结果 → 存入 transcript → harness 继续4.3 Pending Interaction 流程(LangGraph interrupt)
Harness 决策 render_interaction → 生成 InteractionToolCall(interaction_id、type、schema) → tool.call 事件 → interaction.render 事件 → LangGraph interrupt() → session.status = waiting_for_tool → 前端 InteractionRenderer 展示组件 → 受访者操作并提交 → POST .../interactions/{id}/submit → Backend 验证 → 写入 interaction_events / transcript → LangGraph resume → session.status = active → 继续决策循环5. 数据
主要实体(引用 03_data_model.md)
| 实体 | 关键字段 |
|---|---|
Session | session_id, study_id, status, current_phase, turn_count, started_at, last_activity_at, config_versions |
SessionState | covered_objectives: Record<objective_id, CoverageState>, extracted_fields, pending_interaction_id |
TranscriptTurn | turn_id, session_id, role (user/assistant), content, created_at |
ToolCall | tool_call_id, session_id, tool_name, tool_input, tool_result, created_at |
InteractionEvent | interaction_id, session_id, type, status, value, submitted_at, client_context |
AgentDecisionLog | id, session_id, turn_id, phase, input_summary, selected_action, rationale, model, prompt_version_id, latency_ms, token_usage, created_at |
EventLog | append-only,见下方事件类型 |
Event Log 事件类型(引用 01_system_architecture)
session.createdparticipant.message.createdagent.message.startedagent.message.deltaagent.message.completedtool.call.createdtool.call.completedinteraction.renderedinteraction.submittedextraction.startedextraction.completedsession.completedsession.abandoned6. 接口
REST 端点(引用 api_contracts)
| 方法 | 路径 | 说明 |
|---|---|---|
POST | /api/public/studies/{public_slug}/sessions | 创建 session(公开,无需认证) |
POST | /api/sessions/{session_id}/messages | 受访者发送消息,触发 harness |
POST | /api/sessions/{session_id}/interactions/{interaction_id}/submit | 提交交互组件结果,恢复 interrupt |
GET | /api/sessions/{session_id} | 查询 session 详情(transcript、tool_calls、extracted_fields) |
AG-UI 事件(Wire 层)
后端 LangGraph 经 AG-UI 协议(HTTP/SSE)推送事件,前端通过 @assistant-ui/react-ag-ui adapter 消费。业务语义 StreamEvent 类型如下(详见 api_contracts):
type StreamEvent = | { type: "message.delta"; content: string } | { type: "message.completed"; turn_id: string } | { type: "tool.call"; tool_call_id: string; tool_name: string; tool_input: unknown } | { type: "interaction.render"; interaction: InteractionToolCall } | { type: "extraction.completed"; fields: ExtractedField[] } | { type: "session.completed"; summary: string } | { type: "error"; message: string }7. 验收标准
AC-04-1:受访者访问公开 URL,无需登录,POST /api/public/studies/{slug}/sessions 返回 session_id 与 status: "created",耗时 < 500ms。
AC-04-2:Session 记录 study_config_version_id 等 4 个版本字段;发布新版 study config 后,已有进行中 session 的 harness 行为不变。
AC-04-3:Consent 面板渲染后,受访者提交拒绝,session status 置为 abandoned,后续消息接口返回 403/410,不继续推进 harness。
AC-04-4:受访者发送消息,前端在 200ms 内开始接收 message.delta 事件,字符逐步呈现;message.completed 事件后 turn_id 可查询。
AC-04-5:Harness 触发 render_interaction,interaction.render 事件到达前端,InteractionRenderer 正确渲染对应组件(single_choice → RadioCardGroup,likert → LikertScale 等);渲染期间 session status 为 waiting_for_tool。
AC-04-6:受访者提交交互组件,POST .../interactions/{id}/submit 返回 ok: true;session status 恢复 active;harness 继续决策;transcript 中可查到对应 tool call 与 tool result。
AC-04-7:同一时刻 ChatTimeline 最多一个交互组件处于 pending 状态;并发压测中不出现两个并行 pending interaction。
AC-04-8:同一 objective 下 probe 动作不超过 max_probe_count(默认 3);超过上限后 harness 切换 objective 或触发 extract,不继续 probe 同一 objective。
AC-04-9:所有 must objectives 覆盖后,harness 触发 summarize_and_confirm,受访者确认后 session.completed 事件推送,status 置为 completed。
AC-04-10:配置 max_turn_count: 10 的 study,第 10 轮回复后 harness 强制 conclude,reason 为 "max_turns_reached",session 进入 completed。
AC-04-11:受访者发送”结束”,harness 在下一决策循环触发 conclude,reason 为 "participant_requested_end",不再继续发送新问题。
AC-04-12:ChatTimeline 中不出现 tool call 原始 JSON、“LangGraph”、“Agent”等内部术语;assistant loading 状态显示”正在思考…”。
AC-04-13:所有 harness 决策均写入 AgentDecisionLog,字段 rationale 非空;GET /api/sessions/{session_id} 可关联查询。
AC-04-14:Event log 包含完整事件序列(session.created → 消息事件 → tool 事件 → session.completed);给定 session_id 可完整重放 session 行为。
AC-04-15:waiting_for_user 状态的 session,受访者重新打开页面后,ChatTimeline 恢复已有 transcript,可继续作答。
8. 边界 & 非目标
| 边界 / 非目标 | 说明 |
|---|---|
| 受访者无需理解 Agent 概念 | 所有 Agent/工具内部状态对受访者不可见 |
| 不支持 chatbot 式 edit/regenerate | ChatTimeline 为严格线性历史,不支持消息编辑或分支 |
| 不支持多轮并发 session(单人) | 同一受访者同一时刻只有一个 active session |
| 不做实时多人协作访谈 | MVP 不做焦点小组或多人同时访谈 |
| 不做视频 / 音频输入 | MVP 只支持文本消息与结构化交互组件 |
| Interaction 组件实现不在本模块 | 组件 UI 与题型详见 05_interaction_components |
| 字段抽取逻辑不在本模块 | 抽取与 evidence 链详见 06_extraction_evidence |
| LangGraph 内部调度不暴露给前端 | wire 层统一走 AG-UI;前端不直接访问 LangGraph API |
9. 依赖 & 风险
依赖
| 依赖 | 类型 | 说明 |
|---|---|---|
| 03_study_builder | 上游 | study config、objectives、output schema、agent config 必须已发布 |
| 05_interaction_components | 协作 | render_interaction 工具调用的组件渲染实现 |
| 06_extraction_evidence | 下游 | extract_fields 动作的抽取逻辑与 evidence 链 |
| AG-UI 协议 | 外部 | @assistant-ui/react-ag-ui adapter 与 LangGraph AG-UI 输出集成 |
| LangGraph checkpointer(PostgresSaver) | 基础设施 | session 可重放、interrupt/resume、time-travel 依赖 checkpointer 正确配置 |
| LiteLLM Model Gateway | 基础设施 | Harness LLM 调用经 LiteLLM 路由,provider 故障影响全部 session |
风险
| 风险 | 影响 | 缓解措施 |
|---|---|---|
| AG-UI adapter 与 LangGraph 集成不稳定 | 前端无法消费事件流,访谈中断 | 早期搭建 spike PoC,固定 adapter 版本;fallback 到 raw SSE |
| Streaming 事件乱序或丢失 | 前端 ChatTimeline 显示异常 | 每条事件携带 turn_id 和顺序号;前端做幂等渲染 |
| LangGraph interrupt 恢复超时 | 受访者提交后长时间等待 | 设置 interrupt 超时(建议 30s);超时后推送 error 事件并允许重试 |
| Harness 追问过度 | 受访者疲劳流失 | max_probe_count 强制限制;P1 引入疲劳检测(FR-04-15) |
| LLM 决策输出不符合 AgentAction schema | Harness 无法执行动作,session 阻塞 | structured output 解析失败时触发 fallback(ask_question 默认动作)并写入 error log |
| session 并发量增加时 DB checkpointer 成为瓶颈 | 响应延迟上升 | MVP 单机 PostgresSaver;P1 评估 Redis 或分库 |