Skip to content

fix(agents): 修复 HIL 工具审批 interrupt 被误当 ask_user_question 处理#784

Closed
xiangfei258 wants to merge 6 commits into
xerrors:mainfrom
xiangfei258:fix/hil-interrupt-payload-routing
Closed

fix(agents): 修复 HIL 工具审批 interrupt 被误当 ask_user_question 处理#784
xiangfei258 wants to merge 6 commits into
xerrors:mainfrom
xiangfei258:fix/hil-interrupt-payload-routing

Conversation

@xiangfei258

Copy link
Copy Markdown
Contributor

变更描述

修复 LangChain HumanInTheLoopMiddleware(HIL)工具审批 interrupt 在前后端多处被按 ask_user_question 误处理的问题,使 HIL 工具审批端到端可用。

HIL 触发的 interrupt 载荷为 action_requests/review_configs 结构(HITLRequest),与 ask_user_question 的 questions 结构不同。原有逻辑在三处按 questions 假设,导致 HIL 不可用:

  1. check_and_handle_interrupts:无 questions 时塞默认空问题「请选择一个选项」,前端收到空弹窗。现按 payload 是否含 action_requests 分流,HIL 中断发 human_approval_required 并携带 action_requests/review_configs,ask_user_question 保持原有行为。
  2. _compact_stream_chunk:verbose=false 精简白名单漏掉 action_requests/review_configs,前端拿不到审批载荷。白名单补入这两个字段。
  3. 前端 hasPendingInterruptForRun:只认 questions,HIL 中断被判为非 interrupt,弹窗一闪而过。兼容 actionRequests
  4. run_worker 摘要:只取 questions,HIL 摘要为空。_interrupt_summary 兼容两类载荷,HIL 摘要改为「操作需要确认: 工具名(参数)」。

前端新增 HumanToolApprovalModal:HIL 中断渲染待审批工具名与参数,提供「确认执行/编辑参数/拒绝」三种决策,提交 { decisions: [{ type: "approve" | "reject" | "edit", ... }] },与 LangGraph Command(resume=...) 恢复语义一致。ask_user_question 中断仍走原有选项式弹窗,行为不变。

说明:本 PR 只修复 HIL 管道使其可用,未挂载 HIL 中间件、未决定哪些工具需审批。挂载与危险工具清单配置化是后续工作。

变更类型

  • Bug 修复
  • 新功能(前端 HIL 审批弹窗)

测试

  • 已在 Docker 环境测试
  • 相关功能正常工作

测试结果

  • 后端单元测试 56 passed(payload 分流、compact 字段保留、摘要生成)
  • 前端测试 PASS(extractPendingInterrupt 分流)
  • make format / make lint 通过
  • 前端 build 通过
  • 浏览器 e2e:HIL 中断弹窗稳定显示,「确认执行」后工具正确执行并返回结果;普通对话回归正常

说明

  • 发现 1(SubagentTransformer 重复注册)已在上游 fd9558cf 修复,本分支已 merge 同步
  • 完整调试与根因分析记录在 commit message 中

xiangfei258 and others added 6 commits June 12, 2026 22:08
合并上游 68 个提交,主要包含:
- CLI 上传与认证登录流程
- Agent 评估运行流式入口 + Langfuse 观测性,AgentRun 接口重构
- 文件预览/渲染组件解耦,知识库文件列表分页懒加载
- FastAPI 查询链路并发优化与限流信号量修复
- YUXI_CORS_ORIGINS、API Key/Dashboard 权限收紧 (xerrors#783)
- 超大数量文件场景批量解析/入库优化

本地定制改动(logo/首页/登录页/配置)保留,自动合并无冲突。
合并上游 SubagentTransformer 字段冲突修复(fd9558c):
langchain 1.3.10 的 create_agent 内置 native SubagentTransformer 注册
subagents key,与项目 YuxiSubAgentMiddleware 注册的同名 key 冲突。
上游改用 yuxi_subagents key 避冲突并保留可见性过滤。
HumanInTheLoopMiddleware 触发的工具审批 interrupt 载荷为
action_requests/review_configs(HITLRequest),与 ask_user_question 的
questions 结构不同,原有逻辑在三处按 questions 假设导致 HIL 不可用:

1. check_and_handle_interrupts 无 questions 时塞默认空问题
   「请选择一个选项」,前端收到空弹窗。现按 payload 是否含
   action_requests 分流,HIL 中断发 human_approval_required 并携带
   action_requests/review_configs,ask_user_question 保持原有行为。
2. _compact_stream_chunk 的 verbose=false 精简白名单漏掉
   action_requests/review_configs,前端拿不到审批载荷。白名单补入
   这两个字段。
3. run_worker 的 interrupt 摘要只取 questions,HIL 摘要为空。
   _interrupt_summary 兼容两类载荷,HIL 摘要改为
   「操作需要确认: 工具名(参数)」。

补充单元测试覆盖 payload 分流、compact 字段保留与摘要生成。
前端按 interrupt 类型分流:
- human_approval_required 渲染新的 HumanToolApprovalModal,
  展示待审批工具名与参数,提供确认执行/编辑参数/拒绝三种决策,
  提交 { decisions: [{ type: "approve" | "reject" | "edit", ... }] }
  与后端 Command(resume=...) 恢复语义一致
- ask_user_question_required 仍走原有选项式弹窗,行为不变

useApproval 与 useAgentRunStream 的 hasPendingInterruptForRun
兼容 actionRequests(HIL 中断无 questions),避免弹窗一闪而过。
将 extractPendingInterrupt 抽到 approvalInterrupt.js 纯函数文件,
便于纯 node 单测。补充前端分流单测。

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for LangChain's HumanInTheLoopMiddleware tool approval interrupts, distinguishing them from standard user questions across both the backend and frontend. It also adds Docker configurations for the MinerU GPU service, updates branding to '楚天智航', and removes GitHub star/link integrations. Feedback on the backend implementation suggests using or operators in chat_service.py to correctly handle None values when formatting tool descriptions, and using json.dumps in run_worker.py to format complex tool arguments as standard JSON in the interrupt summary.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +621 to +624
action["description"] = "操作需要确认\n\nTool: {name}\nArgs: {args}".format(
name=action.get("name", ""),
args=action.get("args", {}),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

如果 action 字典中存在 "name""args" 键但其值为 Noneaction.get("name", "")action.get("args", {}) 仍会返回 None(因为 get 的默认值仅在键不存在时生效)。这会导致格式化后的描述中显示 Tool: NoneArgs: None。\n\n建议使用 or 运算符来确保在值为 None 时也能正确回退到默认值:

            action["description"] = "操作需要确认\\n\\nTool: {name}\\nArgs: {args}".format(\n                name=action.get("name") or "",\n                args=action.get("args") or {},\n            )

Comment on lines +254 to +257
if args:
return f"操作需要确认: {name}({args})"
return f"操作需要确认: {name}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

args 是字典或列表等复杂对象时,直接使用 f-string 格式化 {args} 会输出 Python 特有的字符串表示形式(例如使用单引号 {'path': '/x'},布尔值首字母大写 True 等)。\n\n为了使输出的摘要更符合标准的 JSON 格式且更具可读性,建议使用 json.dumps 进行序列化:

            if name:\n                if args:\n                    try:\n                        args_str = json.dumps(args, ensure_ascii=False)\n                    except Exception:\n                        args_str = str(args)\n                    return f"操作需要确认: {name}({args_str})"\n                return f"操作需要确认: {name}"

@xerrors

xerrors commented Jun 23, 2026

Copy link
Copy Markdown
Owner

提交有误

@xerrors xerrors closed this Jun 23, 2026
@xiangfei258 xiangfei258 deleted the fix/hil-interrupt-payload-routing branch June 23, 2026 11:37
@xiangfei258 xiangfei258 restored the fix/hil-interrupt-payload-routing branch June 23, 2026 11:37
@xiangfei258 xiangfei258 deleted the fix/hil-interrupt-payload-routing branch June 23, 2026 11:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants