From 88fa7cc05c89a0e660de02f09ad9b96b8718217a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 16:49:13 +0000 Subject: [PATCH] chore(.features): sync feature memory (auto) (#47) Generated by sparticle-toolkit feature-memory-sync Co-authored-by: Denya0529 <217564326+Denya0529@users.noreply.github.com> --- .features/skill/MEMORY.md | 6 ++++ .features/skill/changelog/2026-Q2.md | 51 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/.features/skill/MEMORY.md b/.features/skill/MEMORY.md index 915fac2..5a2c15b 100644 --- a/.features/skill/MEMORY.md +++ b/.features/skill/MEMORY.md @@ -1,6 +1,7 @@ # Skill 功能 > 负责范围:技能包管理服务 - 核心实现 +> 最后更新:2026-06-02 > 最后更新:2026-05-26 > 最后更新:2026-05-23 > 最后更新:2026-04-20 @@ -28,9 +29,12 @@ MCP UI 类 skill 已按 MCP Apps 模式改造:工具返回数据,静态 HTML - `skills_developing/` - 开发中 skills - `agent/subagent_loader.py` - 扫描 skill `agents/*.md` 加载子 agent(2026-05 引入) - `agent/mcp_trace_meta.py` - 对 `ClientSession.call_tool` 做 monkey-patch,向 `rag_retrieve` / `table_rag_retrieve` 的 MCP `_meta` 注入 `trace_id`(2026-05 引入) +- `agent/tool_metrics_middleware.py` - `ToolMetricsMiddleware` 给每次 tool 调用 emit `catalog_agent.tool_call` 结构化指标(2026-05 引入) ## 最近重要事项 +- [2026-06-02](changelog/2026-Q2.md): 修复 deepagents 落盘 backend grep 扫描全盘问题——`create_custom_cli_agent` 中 `large_results_backend` / `conversation_history_backend` 的 `FilesystemBackend` 由 `virtual_mode=False` 改为 `True`,避免 `CompositeBackend` 剥前缀后把 `"/"` 转发给 grep 时扫到真实根目录,单次 grep 从 45–152s 回到毫秒级(`6bccd89`) +- [2026-05-29](changelog/2026-Q2.md): 新增 `ToolMetricsMiddleware`——通过 `wrap_tool_call` / `awrap_tool_call` 对每次 tool 调用计时并 emit `catalog_agent.tool_call` 结构化指标(成功/失败/取消三态、含 `tool_name`/`trace_id`/`bot_id`/`duration_ms`/`error_type`);插在 `init_agent` 中间件链的 `EmptyResponseRetryMiddleware` 之后、`ToolUseCleanupMiddleware` 之前(`9f0ae25`) - [2026-05-26](changelog/2026-Q2.md): skill 引入 `category` 字段——`routes/skill_manager.py` 在 `SkillItem` / `SkillValidationResult` 增加 `category`,从 `plugin.json` 与 `SKILL.md` frontmatter 解析,official skill 默认 `"other"`、user skill 默认 `"custom"`;并通过 batch 给 common/developing/onprem/support 路径下大量 skill 元数据补 `category`,`data-dashboard` / `mcp-ui` 归类 `Interactive UI`(`203dcf4`, `3ada55a`, `9658588`) - [2026-05-26](changelog/2026-Q2.md): developing 分支大合并新增多个 skill:`ai-ppt-generator`(百度 AI PPT)、`nfc-medicine-lookup`(NFC 药品检索)、`ppt-outline`(PPT 大纲 / HTML 演示文稿)、`z-card-image`(配图 / 卡片图),同时 `skills/linggan/*` 系列 skill 经合并回归(`3ada55a`) - [2026-05-23](changelog/2026-Q2.md): 新增 MCP App 型 `skills/developing/ecommerce-storefront/`——含 `product-list` / `order-confirm` 两个 HTML App + 自带 `ecommerce_server.py` MCP server;同时落地 `docs/mcp-app-training.md`(约 1063 行)作为 MCP App 培训材料(`9d001c8`) @@ -85,6 +89,8 @@ MCP UI 类 skill 已按 MCP Apps 模式改造:工具返回数据,静态 HTML - ⚠️ **skill `category` 默认值**:API 返回的 `SkillItem.category`——official skill fallback 为 `"other"`、user skill fallback 为 `"custom"`;前端做分类视图时需要同时识别这两个 sentinel,不要假设官方/用户 skill 用同一套缺省值。 - ⚠️ **`category` 字段双入口**:同一 skill 可以同时在 `.claude-plugin/plugin.json` 和 `SKILL.md` frontmatter 写 `category`;`get_skill_metadata` 优先走 `parse_plugin_json`,若 skill 包没有 plugin.json 才回落到 `parse_skill_frontmatter`——两者写不一致时以 plugin.json 为准。 - ⚠️ **Daytona shell_env 是文件注入而非 process env**:`init_agent` 通过 `cat > $REMOTE_BASH_ENV_PATH` 写入 `export VAR=...` 行,沙箱内必须由 shell(bash)的 `BASH_ENV` 加载才能生效;非 daytona 模式或不走 bash 启动的脚本拿不到这些变量。扩展注入项需直接改 `init_agent` 里的 `_shell_env` 字典。 +- ⚠️ **`CompositeBackend` 路由下的落盘 backend 必须 `virtual_mode=True`**:`create_custom_cli_agent` 中 `large_results_backend` / `conversation_history_backend` 都用独立 `tempfile.mkdtemp()` 做根目录,但 `CompositeBackend` 在路由时会剥掉前缀、可能把 `"/"` 转发给 grep;`virtual_mode=False` 会把 `"/"` 解析为真实根目录并扫到 `/usr`、`/var`、其他会话的 tmp 目录(单次 45–152s)。`virtual_mode=True` 才会把所有路径锚定到 `root_dir` 并过滤越界结果。后续新增"只服务本次会话"的落盘 backend 一律走 `virtual_mode=True`,真实 workspace backend 仍保持 `False`。 +- ⚠️ **`ToolMetricsMiddleware` 必须在重试中间件之后、其他工具中间件之前**:`init_agent` 中顺序约定为 `EmptyResponseRetryMiddleware → ToolMetricsMiddleware → ToolUseCleanupMiddleware → ...`,这样统计到的 `duration_ms` 才包含全部后续 tool 处理开销并自然覆盖重试边界。指标 emit 自身的异常被吞掉只打 logger.exception,所以指标缺失不会触发 agent 报错,必须在指标后端做独立告警。 ## Skill 目录结构 diff --git a/.features/skill/changelog/2026-Q2.md b/.features/skill/changelog/2026-Q2.md index ca34c8e..86b46f2 100644 --- a/.features/skill/changelog/2026-Q2.md +++ b/.features/skill/changelog/2026-Q2.md @@ -4,6 +4,57 @@ --- +## 2026-06-02: 修复 deepagents 落盘 backend grep 扫描全盘问题(virtual_mode=True) + +**类型**:Bug 修复 + +**背景**:`create_custom_cli_agent` 给 `large_results` / `conversation_history` 两个路由创建了独立 `FilesystemBackend(tempfile.mkdtemp(...), virtual_mode=False)`,配合 `CompositeBackend` 做前缀路由。线上出现 grep 调用耗时 45–152s 的异常。 + +**改动**:将 `large_results_backend` 与 `conversation_history_backend` 的 `virtual_mode` 由 `False` 改为 `True`,并在调用处补 NOTE 注释说明 virtual_mode 的语义。 + +**根因**:`CompositeBackend` 会先剥掉路由前缀,再把剩余路径(极端情况下就是 `"/"`)转发给被路由 backend 的 grep。当 backend 的 `virtual_mode=False` 时,`"/"` 解析为真实根目录而不是 `root_dir`,于是 grep 在沙箱 / 容器内对 `/usr`、`/var`、其他会话的 tmp 目录全盘扫描,单次耗时达数十秒到分钟级。`virtual_mode=True` 会把所有路径锚定到 `root_dir`,并过滤掉根目录之外的结果,把扫描限制在 backend 自己的 tmp 子目录内。 + +**影响**: +- 这两个 backend 的 grep 调用回落到毫秒级,整个 deep agent 的 tool 调用 P99 大幅下降。 +- 任何新增的"落盘但只服务于本次会话"的 `FilesystemBackend` 走 `CompositeBackend` 路由时,**必须**使用 `virtual_mode=True`,否则容易复现同样的全盘扫描问题。 +- 真实 workspace backend(用户文件根目录)仍然是 `virtual_mode=False`,因为它需要看到沙箱真实路径上的产物。 + +**相关文件**: +- `agent/deep_assistant.py` + +**Commit/PR**:`6bccd89` + +--- + +## 2026-05-29: 新增 ToolMetricsMiddleware(tool 调用埋点) + +**类型**:新功能 + +**背景**:deep agent 内部的工具调用(含 MCP tool / skill script / 文件系统操作等)此前缺少统一的耗时与成功率埋点;问题排查只能依赖日志,无法对接 `emit_question_metric` 的结构化指标体系。 + +**改动**: +- 新增 `agent/tool_metrics_middleware.py`,定义 `ToolMetricsMiddleware(AgentMiddleware)`: + - 同时实现 `wrap_tool_call`(同步)与 `awrap_tool_call`(异步)。 + - 对每次 tool 调用计时(`time.monotonic()`),通过 `emit_question_metric(stage="catalog_agent.tool_call", ...)` 上报:`status`(`success` / `error` / `cancel`)、`duration_ms`、`error_type`、`tool_name`、`tool_call_id`、`trace_id`、`bot_id`、`session_id`、`model`、`stream`、`tool_response`、`enable_thinking`。 + - 异步分支特别捕获 `asyncio.CancelledError` 上报 `status="cancel"` 后再 re-raise。 + - 指标 emit 自身的异常被 try/except 兜住,绝不影响 tool 调用本体。 +- `agent/deep_assistant.py::init_agent` 中间件链中,将 `ToolMetricsMiddleware(config)` 插在 `EmptyResponseRetryMiddleware` 之后、`ToolUseCleanupMiddleware` 之前。 + +**根因**:N/A(新功能) + +**影响**: +- 所有走 deep agent / sub agent 的 tool 调用现在都会自动出 `catalog_agent.tool_call` 结构化指标,可在指标后端按 `tool_name` / `bot_id` / `status` 聚合做 P50 / P99 / 错误率分析。 +- 中间件顺序硬约束扩展:`EmptyResponseRetryMiddleware → ToolMetricsMiddleware → ToolUseCleanupMiddleware → CustomFilesystemMiddleware → SubAgentMiddleware → AnthropicPromptCachingMiddleware`,调整 `init_agent` 中间件顺序时需保持 `ToolMetricsMiddleware` 在最外层(仅次于重试),否则统计到的耗时不包含其他中间件开销。 +- emit 失败只打 logger.exception,不会回传给 tool handler;指标缺失需在指标后端层面单独告警,不要依赖 agent 端口的报错。 + +**相关文件**: +- `agent/tool_metrics_middleware.py`(新增) +- `agent/deep_assistant.py` + +**Commit/PR**:`9f0ae25` + +--- + ## 2026-05-26: skill `category` 字段全面接入 **类型**:新功能