Skip to content

fix: 修复技能调用白名单、脚本参数解析与工具失败结果回流问题 - #2849

Open
Greenplumwine wants to merge 4 commits into
Tencent:mainfrom
Greenplumwine:fix/skill-tool-fixes-clean
Open

fix: 修复技能调用白名单、脚本参数解析与工具失败结果回流问题#2849
Greenplumwine wants to merge 4 commits into
Tencent:mainfrom
Greenplumwine:fix/skill-tool-fixes-clean

Conversation

@Greenplumwine

@Greenplumwine Greenplumwine commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Description

本 PR 修复了技能调用与工具失败结果处理中的若干缺陷,按子系统拆分为 4 个独立提交:

1. 技能调用白名单(internal/application/service/session_agent_qa.go

@mention 原先被实现为收窄 AllowedSkills,导致用户提及技能 A 后,智能体提示词要求调用未提及的技能 B 时 read_skill(B) 返回 "skill not allowed"。改为 @mention 只生成 <must_use> pin 提示,不再改动门控:智能体配置的全部技能始终可用,pin 仅记录被提及且当前允许的技能(空 AllowedSkills 视为全允许,与 Manager.isSkillAllowed 一致)。

2. 脚本参数解析(internal/agent/tools/skill_execute.go

部分模型供应商把 execute_skill_scriptargs(schema 为 []string)emit 成 JSON 字符串,内容本身是个 JSON 数组(如 "[\"--project-name\",\"X\"]")。原 UnmarshalJSON 兜底用 strings.Fields 拆分,把方括号/引号拆成垃圾 token,脚本 argv 全是乱码,argparse 永远报参数缺失。改为先把字符串内容当 JSON 数组解析,成功即用;否则再退回 strings.Fields

3. 工具失败结果保留诊断信息(internal/modelcontext/registry.go

ModelToolResultForTool 在工具失败时只回 Error 短信息,丢弃 Outputexecute_skill_script 把非零退出的 stdout/stderr 写进 Output,导致 AI 拿不到报错正文,无法诊断失败。失败且 Output 非空时将其拼到 Error 之后;对失败时不设 Output 的工具零影响。

4. 工具失败走 tool_result 而非 error 事件(internal/handler/session/agent_stream_handler.go + frontend/src/composables/useChatStreamHandler.ts

handleToolResult!success 时把 SSE 事件类型从 tool_result 改成 error,导致工具失败与流程中断错误共用 error 通道,前端只能靠 tool_name 有无隐式区分。改为工具失败始终发 tool_resultsuccess=false 区分成败),error 事件留给流程中断错误。前端 useChatStreamHandler 同步收敛:error 事件短路到致命处理,tool_result 失败时显示完整 output(含 stdout/stderr)。

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 📚 Documentation update
  • 🎨 Refactor
  • ⚡ Performance improvement
  • 🧪 Test
  • 🔧 Configuration / Build / CI

Related Issue

Fixes #

Testing

每个修复均附带或增强了单元测试,覆盖对应的边界形态:

  • internal/agent/tools/skill_execute_test.go:新增 UnmarshalJSON 各形态单测(JSON 数组字符串、普通空格分隔字符串、原生数组),验证 stringified JSON 数组可正确还原为 argv。
  • internal/modelcontext/registry_test.go:验证工具失败时 Output 非空会被拼接到 Error 之后;失败时无 Output 的工具行为不变。
  • internal/application/service/session_agent_qa_scope_test.go:验证 @mention 不再收窄 AllowedSkills,pin 仅记录被提及且允许的技能。

本地验证命令与结果:

# 1. 全仓编译
go build ./...                       # exit 0(仅 cmd/desktop|server 无关 -lc++ 链接警告)

# 2. 变更包单测
go test ./internal/agent/tools/... ./internal/modelcontext/...   # PASS

# 3. 大包装测试包编译校验(不实际运行,避免环境依赖干扰)
go test -run '^$' ./internal/application/service/ ./internal/handler/session/   # 编译通过

# 4. diff 范围 lint
golangci-lint run --new-from-rev=upstream/main \
  ./internal/agent/tools/... ./internal/modelcontext/... \
  ./internal/application/service/... ./internal/handler/session/...   # 0 issues

Checklist

  • git diff --check origin/main...HEAD passes
  • Changed source files are formatted(gofmt -l 无输出)
  • Targeted tests for the changed packages/components pass
  • Diff-scoped lint passes(golangci-lint run --new-from-rev=upstream/main ./... 针对变更包 0 issues)
  • Full-repository checks were run(go build ./... 通过;无关 cmd/desktop|server-lc++ 链接警告不影响)
  • Self-reviewed the code
  • Added/updated tests covering the change
  • Updated related documentation (README, docs/, Swagger annotations, etc.) — 本 PR 无文档/接口契约变更,无需更新
  • Breaking changes are clearly called out in the description above — 无破坏性变更:SSE 事件语义在工具失败场景下由 error 收敛为 tool_result(success=false),属行为修复,不破坏对外接口契约

Screenshots / Recordings

Greenplumwine and others added 4 commits August 28, 2026 11:50
ModelToolResultForTool 在工具失败时只回 Error 短信息,丢弃 Output。
execute_skill_script 把非零退出的 stdout/stderr 写进 Output,导致
AI 拿不到报错正文,无法诊断失败。失败且 Output 非空时将其拼到
Error 之后;对失败时不设 Output 的工具零影响。

Co-Authored-By: Claude <noreply@anthropic.com>
部分模型供应商把 execute_skill_script 的 args(schema 为 []string)
emit 成 JSON 字符串,内容本身是个 JSON 数组("[\"--project-name\",\"X\"]")。
原 UnmarshalJSON 兜底用 strings.Fields 拆这种字符串,把方括号/引号拆成
垃圾 token,脚本 argv 全是乱码,argparse 永远报参数缺失,模型无论如何
调整格式都失败。改为先把字符串内容当 JSON 数组解析,成功即用;否则再
退回 strings.Fields。补 UnmarshalJSON 各形态单测。

Co-Authored-By: Claude <noreply@anthropic.com>
applyPerRequestSkillScope 原把 @mention 实现为收窄 AllowedSkills,
导致用户提及技能 A 后,智能体提示词要求调用未提及的技能 B 时
read_skill(B) 返回 "skill not allowed"。改为 @mention 只生成
<must_use> pin 提示,不动门控:智能体配置的全部技能始终可用,
pin 仅记录被提及且当前允许的技能(空 AllowedSkills 视为全允许,
与 Manager.isSkillAllowed 一致)。

Co-Authored-By: Claude <noreply@anthropic.com>
handleToolResult 在 !success 时把 SSE 事件类型从 tool_result 改成
error,导致工具失败与模型流/pipeline 真错误共用 error 通道,前端只能
靠 tool_name 有无隐式区分。改为工具失败始终发 tool_result(success=
false 区分成败),error 事件留给流程中断错误。

前端 useChatStreamHandler 同步收敛:error 事件短路到致命处理,不再
走无意义的 pending 工具卡匹配;tool_result 失败时显示完整 output
(含 stdout/stderr)而非短 error 标签。

Co-Authored-By: Claude <noreply@anthropic.com>
@Greenplumwine Greenplumwine changed the title fix(skill/tool): 修复技能调用与工具失败结果的若干问题 fix: 修复技能调用白名单、脚本参数解析与工具失败结果回流问题 Aug 28, 2026
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.

1 participant