卡片交互机制(ask_user / resume)
本系统的人机协同遵循一个强约束:
- 任何需要人工补问/选择/确认的步骤,都必须由技能输出
control.action == "ask_user"触发 - 外层不做“自动修正/兜底”,必须等待用户提交卡片后才能继续推进
这一机制在当前多仓库形态下由:
ai-engine负责“中断/恢复”(LangGraph interrupt)consultations-service负责“前端 SSE 输出与转发”
1) 输出契约:ask_user
技能输出的 control 段示例(摘录):
{
"control": {
"action": "ask_user",
"review_type": "clarify",
"questions": [
{
"question": "请补充被告的身份证号",
"field_key": "profile.defendant.id_number",
"input_type": "text",
"required": true
}
]
}
}
语义:
action=ask_user:当前轮必须中断等待用户输入questions[*].field_key:用于把用户答案回填到 state(支持点路径)review_type:前端渲染/交互类型(clarify/select/confirm/phase_done)
2) 事件流:NDJSON → SSE
2.1 ai-engine:NDJSON 事件流
ai-engine internal API(以当前实现为准):
GET /api/v1/internal/ai/agent/pending_card?thread_id=...POST /api/v1/internal/ai/agent/execute/stream(NDJSON)POST /api/v1/internal/ai/agent/resume/stream(NDJSON)
流式输出为逐行 JSON:
{"event":"card","data":{...}}
{"event":"end","data":{"output":""}}
2.2 consultations-service:SSE 转发
对前端:
POST /api/v1/consultations/sessions/{sessionId}/chat(SSE)POST /api/v1/consultations/sessions/{sessionId}/resume(SSE)GET /api/v1/consultations/sessions/{sessionId}/pending_card
转发策略(关键点):
token事件只转发“用户可见节点”的 token(避免 router/skill 等内部 JSON 推理内容污染前端)card事件原样转发给前端,用于弹出悬浮卡片并锁定输入end结束本轮流式连接
3) pending_card:断线重连兜底
SSE 链路常见问题是前端刷新/断线重连。为避免“卡片已经触发但前端丢失”,提供:
GET /api/v1/consultations/sessions/{sessionId}/pending_card
其内部通常会根据 thread_id 去 ai-engine 查询:
GET /api/v1/internal/ai/agent/pending_card?thread_id=...
返回格式(概念):
{
"has_pending_card": true,
"card": { "review_type": "...", "questions": [...] }
}
4) resume:提交答案并继续执行
前端提交卡片答案后:
- 调用 consultations-service 的
/resume(SSE) - consultations-service 将
user_response透传给 ai-engine 的/agent/resume/stream - ai-engine 把答案回填到 state 后继续运行 LangGraph
consultations-service 的请求体结构(摘录):
{
"user_id": 123,
"user_response": {
"answers": [
{ "field_key": "profile.defendant.id_number", "value": "..." }
]
}
}
5) 与 Matter Todo 的关系(需要约束)
系统同时存在两种“待处理交互”形态:
1) 会话侧:pending_card(thread interrupt) 2) 事项侧:MatterTodo(待办任务,持久化在 matter-service)
二者的定位不同:
- pending_card:更偏“流程推进必须的即时交互”,与 LangGraph 执行强绑定
- MatterTodo:更偏“事项业务上的可追踪任务”,可用于工作台/派单/进度
为了避免前端出现“两个入口/两个待办互相打架”,需要产品/前端明确:
- 哪些交互必须走 pending_card(ask_user)
- 哪些交互可以落到 MatterTodo(例如阶段性确认、交付物审核)
当前实现中,两者会在部分场景同时存在(例如创建事项时自动生成 todo),后续建议做规则收敛。