Agent 状态持久化:中断需用户确认场景
LangGraph 持久化链路、NoSQL 会话读写、确认流图拓扑、SSE payload 补丁等 11 个子问题
1. 问题背景
神秘人 Agent(shenmiren-agent)在「先提案、用户确认、再执行工具」路径下,用文档库存会话与待确认工具调用,用 RDB REST 执行 create_plan 等。该链路里曾先后出现:网关 401、HTTP 失败无结构化体、确认二次请求读不回会话、user_id 落 default、工具成功与 success: false 矛盾、SSE TOOL_CALL_RESULT 缺 payload 致前端校验失败、上游 LLM 限流、persistence 图拓扑导致同轮偷跑写库等问题。
2. 关键问题列表
2.1 HTTP 失败无响应体与错误分层不清
根因:CloudBaseClient 在 HTTP 失败时返回 None,调用方拿不到状态码与 body。传输失败与业务失败混在笼统 message 里。
采纳方案:L0 返回 _http_error 字典(含 status_code、body);L2 tool_failure_from_gateway 带 error 块;L3 execute_tools 以返回 dict 的 success 写记录并生成说明文案。
2.2 RDB 网关鉴权失败(401)
根因:App 侧 RDB 多用用户 access token;Agent 侧用 API Key 作 Bearer,两条链独立。云托管环境变量未注入或与本地/控制台不一致时,网关直接 401。
采纳方案:控制台密钥具备 RDB REST 权限;云托管 CLOUDBASE_APIKEY 等与本地/控制台一致。
2.3 会话文档库写入/读出异常
根因:首轮轮询或 save 尚未完成时 load_session 本就可能 found=False;GET 解析与真实 data 形态不一致时得到空列表导致误判。
采纳方案:session_persistence 增强日志;threadId → thread_id 与 state.session_id 统一注入。
2.4 用户 ID 为占位 default
根因:网关 AiBot 把登录用户放在顶层 userId,若未写入 LangGraph state.user_id 且未调用 set_user,工具层仍回落 default。
采纳方案:预处理 _first_nonempty_str(state.user_id, input_data.userId, forwarded.userId) 写入 state["user_id"] 并 set_user(user_id)。
2.5 工具文案与 success 不一致
根因:execute_tools_node 曾在 invoke 未抛异常时写死 record["success"] = True,未读工具返回的 success。
采纳方案:SSE 专用结构化事件:工具结果、确认状态走独立 TOOL_CALL_RESULT 事件,含 success、message、error;UI 只读结构化字段。
2.6 确认结果前端 JSON.parse 失败
根因:多段 [工具成功/失败] 与 JSON 片段拼接,整体不是单一 JSON 文档。
采纳方案:与 2.5 一致——SSE 专用结构化事件,JSON 一次解析。
2.7 TOOL_CALL_RESULT SSE 缺 payload
根因:SDK handler 在转发 TOOL_CALL_RESULT 时重新构造,只传入 tool_call_id / content,未传入 payload,网关对外 SSE 因而丢掉机读字段。
采纳方案:猴子补丁同时替换 handler.handler 与 server.handler;在 TOOL_CALL_RESULT 分支用 model_copy(update={"payload": _pl}) 原样转发 payload。
2.8 上游 LLM 限流(2064)与持久化边界
根因:意图识别对模型调用返回 2064「当前服务集群负载较高」,与 NoSQL 会话读写无关。生产 Agent 不重试。
2.9 待确认已写入 NoSQL 却同轮执行写库
根因:LangGraph 编译结果里 persistence 同时有出边至 response 与 intent_recognition,同一 superstep 内两后继均可被调度。并行的 intent_recognition_node 读库看到 waiting_confirmation 后将 pending_tool_calls 直接映射为 execute_confirmed,绕过用户确认。
采纳方案:删除 persistence → intent_recognition 单边;persistence_node 返回 needs_confirmation: True;移除 intent 内「读 DB waiting_confirmation 即当已确认」的分支。
2.10 计划标题整句入库
根因:模型把整句用户话填进 plan_title,原 enrich_create_plan_calls_from_messages 在已有 plan_title 时不再改写。
采纳方案:在 execute_tools 前对 create_plan 做 normalize_plan_title_for_create。
3. 小结
持久化链路核心:确认恢复依赖文档库真实可读回;threadId + state.session_id 由预处理注入;persistence 只连 response,保证待确认写库不会在同一 send-message 内被并行 intent 偷跑执行。SSE TOOL_CALL_RESULT 需补丁双向绑定。