diff --git a/.features/skill/MEMORY.md b/.features/skill/MEMORY.md index 8ff455c..f7516e9 100644 --- a/.features/skill/MEMORY.md +++ b/.features/skill/MEMORY.md @@ -1,24 +1,21 @@ # Skill 功能 > 负责范围:技能包管理服务 - 核心实现 -<<<<<<< Updated upstream +> 最后更新:2026-05-23 > 最后更新:2026-04-20 -======= > 最后更新:2026-05-20 ->>>>>>> Stashed changes ## 当前状态 Skill 系统支持两种来源:官方 skills (`./skills/`) 和用户 skills (`projects/uploads/{bot_id}/skills/`)。支持 Hook 系统和 MCP 服务器配置,通过 SKILL.md 或 plugin.json 定义元数据。 -<<<<<<< Updated upstream -======= +2026-04 起 skill 包可在 `agents/*.md` 下定义子 agent(由 `SubAgentMiddleware` 加载);启用 Daytona 沙箱时 skill 加载路径变为沙箱内的 `/workspace/skills`。 + MCP UI 类 skill 已按 MCP Apps 模式改造:工具返回数据,静态 HTML App 由 host 加载后通过 postMessage 接收数据渲染。 目前已新增一批**纯 `SKILL.md` 型业务 skill MVP**,用于研究、摘要、报告和情报编排,底层文件处理与外部检索能力继续复用既有 skill。 ->>>>>>> Stashed changes ## 核心文件 - `routes/skill_manager.py` - Skill 上传/删除/列表 API @@ -28,10 +25,25 @@ MCP UI 类 skill 已按 MCP Apps 模式改造:工具返回数据,静态 HTML - `routes/mcp_resources.py` - MCP App 静态 HTML resource REST 入口 - `skills/` - 官方 skills 目录 - `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 引入) ## 最近重要事项 +- [2026-05-12](changelog/2026-Q2.md): 跨 6→10 个 skill 变体批量精修 `retrieval-policy*.md`,统一 onprem/support/autoload 各路径下的 policy 口径(`be96f24`, `7b4f03d`) +- [2026-05-11](changelog/2026-Q2.md): 新增子 agent (SubAgent) 支持——skill 包通过 `agents/*.md` 暴露子 agent,由 `SubAgentMiddleware` 加载;附 `pmda-drug-info` skill 示例(`5b634bc`) +- [2026-05-11](changelog/2026-Q2.md): `pmda-drug-info` 的 `pmda_server.py` 大改为 mock 实现(`a92096a`) +- [2026-05-11](changelog/2026-Q2.md): `retrieval-policy.md` 跨 4 个 skill 变体内容同步更新(`e6d1698`) +- [2026-05-08](changelog/2026-Q2.md): 通过 monkey-patch `ClientSession.call_tool`,把 trace_id 透传到 `rag_retrieve` / `table_rag_retrieve` 的 MCP `_meta`(`1f06450`) +- [2026-05-06](changelog/2026-Q2.md): support 分支新增 `kfs-answer` skill(`a9227b8`) +- [2026-05-06](changelog/2026-Q2.md): 修复 Daytona 沙箱增量同步漏掉符号链接的问题——`find -type f` 不覆盖 symlink、`tar.add` 默认不 dereference 导致悬空软链;并统一 dataset 路径为复数 `datasets/`(`3c0fa49`) +- [2026-04-24](changelog/2026-Q2.md): 非流式响应路径上 `_execute_post_agent_hooks` 改为 `asyncio.create_task` 非阻塞执行;同时临时注释停用 `ToolOutputLengthMiddleware`(`45a9494`) +- [2026-04-23](changelog/2026-Q2.md): PrePrompt hook 内容改为通过 `{hook_content}` 占位符注入系统提示词模板,不再在 prompt 末尾追加(`51fbf01`) +- [2026-04-23](changelog/2026-Q2.md): Daytona 沙箱接入——`init_agent` 并行加载 + 返回元组增加 `sandbox` 字段;`skills_sources` 在沙箱模式下变为 `/workspace/skills`,`agent_dir_path` 变为 `/workspace`(`c9e0789`, `8446dab`) +- [2026-04-22](changelog/2026-Q2.md): `skills/developing/` 下新增 `rag-retrieve-no-citation` 与 `novare-context` 两个开发中 skill(`7a30e52`) + - 2026-05-20: `mcp-ui` 和 `data-dashboard` 改为 MCP Apps 标准模式,App HTML 放在 skill 的 `apps/` 目录,由 host 加载后 postMessage 数据 + - 2026-04-20: 为 `rag-retrieve` 新增 `retrieval-policy-forbidden-self-knowledge.md`,禁止知识问答场景使用模型自身知识补全答案,要求严格基于检索证据作答 - 2026-04-19: 环境变量 `SKILLS_SUBDIR` 重命名为 `PROJECT_NAME`,用于选择 `skills/{PROJECT_NAME}` 和 `skills/autoload/{PROJECT_NAME}` 目录 - 2026-04-19: `create_robot_project` 的 autoload 去重和 stale 清理补强,autoload 目录也纳入 managed 清理,避免 `rag-retrieve-only` 场景下旧的 `rag-retrieve` 残留 @@ -41,8 +53,6 @@ MCP UI 类 skill 已按 MCP Apps 模式改造:工具返回数据,静态 HTML ## Gotchas(开发必读) -<<<<<<< Updated upstream -======= - ⚠️ MCP App resource REST 读取路径是 `projects/robot/{bot_id}/skills/{server_name}/apps/{resource_name}.html`,前端 bot_id 应由 `ChatView` 从当前 bot 传给 `ChatMessage`,不要在子组件里重新调用 `useBotManager()` - ⚠️ `langchain-mcp-adapters` 会丢失 `EmbeddedResource` 的 uri/_meta;MCP App payload 需作为 text JSON 传递给前端识别 @@ -60,6 +70,13 @@ MCP UI 类 skill 已按 MCP Apps 模式改造:工具返回数据,静态 HTML - ⚠️ 上传大小限制:50MB(ZIP),解压后最大 500MB - ⚠️ 压缩比例检查:最大 100:1(防止 zip 炸弹) - ⚠️ 符号链接检查:禁止解压包含符号链接的文件 +- ⚠️ **子 agent 同名静默 last-wins**:`subagent_loader._parse_agent_md` 跨 skill 扫描 `agents/*.md` 时,按 `name` 字段去重,**后扫描到的覆盖先扫描的**,只打 warning 不报错。多 skill 都暴露子 agent 时需自觉错开命名。 +- ⚠️ **`SubAgentMiddleware` 中间件顺序**:必须插在 `CustomFilesystemMiddleware` 之后、`AnthropicPromptCachingMiddleware` 之前——这是匹配 `deepagents.create_deep_agent` 的官方顺序,调整 `create_custom_cli_agent` 中的中间件顺序时不能随意挪动这一段。 +- ⚠️ **Daytona 模式下 skill 路径不同**:`DAYTONA_ENABLED=true` 时 `enable_skills` 的 `skills_sources` 是 `/workspace/skills`(沙箱内),同时 system prompt 的 `agent_dir_path` 是 `/workspace`;写死本地路径的 hook / 脚本需要兼容两种环境。 +- ⚠️ **PostAgent hook 非流式分支已 fire-and-forget**:`routes/chat.py` 用 `asyncio.create_task` 启动 hook,调用方不会等待也不会感知到 hook 的异常——hook 失败只会被自己的 logger 捕获。 +- ⚠️ **MCP `_meta.trace_id` 是全局 monkey-patch 注入**:`agent/mcp_trace_meta.patch_mcp_client_session_trace_meta()` 在 `get_tools_from_mcp()` 入口调用一次后,会把 `mcp.ClientSession.call_tool` 永久包装;仅对工具名在 `{"rag_retrieve", "table_rag_retrieve"}` 集合内的调用注入 `_meta.trace_id`,扩展白名单要直接改 `_TRACE_META_TOOL_NAMES` 常量。 +- ⚠️ **PrePrompt hook 内容位置由模板决定**:自 2026-04-23 起 hook 产出通过 `{hook_content}` 占位符注入 `prompt/system_prompt.md`,不再追加在 prompt 末尾;自定义模板必须包含 `{hook_content}` 占位符否则 hook 内容会丢失。 +- ⚠️ **`init_agent` 返回值已变 3 元素**:Daytona 改造后 `init_agent` 返回 `(agent, checkpointer, sandbox)`;调用方解构必须更新。 ## Skill 目录结构 diff --git a/.features/skill/changelog/2026-Q2.md b/.features/skill/changelog/2026-Q2.md index 83c2148..3d70869 100644 --- a/.features/skill/changelog/2026-Q2.md +++ b/.features/skill/changelog/2026-Q2.md @@ -1,5 +1,272 @@ # 2026-Q2 Skill Changelog +按时间倒序记录本季度的重要变更。 + +--- + +## 2026-05-12: 批量精修 retrieval policy 文案 + +**类型**:内容调整 + +**背景**:[待补充] + +**改动**: +- `be96f24`: 跨 6 个 skill 变体调整 `retrieval-policy-forbidden-self-knowledge.md` 的措辞(onprem / support / autoload-onprem / autoload-onprem-rag-only / autoload-support-rag-only 路径下的版本及一份 `retrieval-policy.md`)。 +- `7b4f03d`: 在更广的 10 个文件范围内同步更新 `retrieval-policy.md` 与 `retrieval-policy-forbidden-self-knowledge.md` 两套 policy,使各 skill 变体的策略口径保持一致。 + +**根因**:N/A(非 Bug) + +**影响**:所有使用 `rag-retrieve` / `rag-retrieve-only` 这两个 hook 的 skill 在策略行为上保持一致;同时影响 onprem 与 support 两个发布分支的部署。 + +**相关文件**: +- `skills/onprem/rag-retrieve/hooks/retrieval-policy*.md` +- `skills/support/rag-retrieve/hooks/retrieval-policy*.md` +- `skills/autoload/onprem/rag-retrieve/hooks/retrieval-policy*.md` +- `skills/autoload/onprem/rag-retrieve-only/hooks/retrieval-policy*.md` +- `skills/autoload/support/rag-retrieve-only/hooks/retrieval-policy*.md` + +**Commit/PR**:`be96f24`, `7b4f03d` + +--- + +## 2026-05-11: 子 agent (SubAgent) 支持 + pmda-drug-info skill + +**类型**:新功能 + +**背景**:需要让单个 skill 在主 agent 之外承载多个专用子 agent,按用途隔离上下文与工具集(如 pmda 药品信息场景下的 single-drug / interaction / adverse-event / patient-specific 四个专用 agent)。 + +**改动**: +- 新增 `agent/subagent_loader.py`:扫描 skill 目录下的 `agents/*.md`,按 YAML frontmatter 的 `name` / `description` / `tools` 字段解析为 `SubAgent` 字典;按 `name` 去重,**后扫描的覆盖先扫描的**(last-wins)。 +- `agent/deep_assistant.py`:`init_agent` 调用 `load_subagents()`,存在则将 `SubAgentMiddleware`(来自 `deepagents.middleware.subagents`)插在 `CustomFilesystemMiddleware` 之后、`AnthropicPromptCachingMiddleware` 之前,顺序匹配 `create_deep_agent`。 +- 新增 `skills/developing/pmda-drug-info/`:完整 skill 包,包含 `.claude-plugin/plugin.json`、`hooks/pre_prompt.py` + `hooks/pmda-instructions.md`、四个 `agents/*.md`、自带 `pmda_server.py` MCP server + `pmda_tools.json`、`mcp_common.py` 工具基础类。 + +**根因**:N/A + +**影响**: +- skill 包结构新增约定:`agents/*.md` 目录下的 markdown 文件会被加载为子 agent。 +- skill 加载流程在 `init_agent` 内增加一次目录扫描;对没有 `agents/` 的 skill 无影响。 +- skill 跨 bot 共享时存在 sub-agent 同名冲突的风险——同名 sub-agent 不会报错,而是被后扫描到的覆盖。 + +**相关文件**: +- `agent/subagent_loader.py`(新) +- `agent/deep_assistant.py`(接线) +- `skills/developing/pmda-drug-info/`(新 skill) + +**Commit/PR**:`5b634bc` + +--- + +## 2026-05-11: pmda-drug-info MCP server 重写为 mock 实现 + +**类型**:内部改造 + +**背景**:[待补充] + +**改动**:`skills/developing/pmda-drug-info/pmda_server.py` 大幅替换(+322 / -385),保留接口面向 agent 的契约,内部替换为 mock 数据实现。 + +**根因**:N/A + +**影响**:pmda-drug-info skill 当前不再依赖外部真实 PMDA 数据源,便于开发期联调。 + +**相关文件**: +- `skills/developing/pmda-drug-info/pmda_server.py` + +**Commit/PR**:`a92096a` + +--- + +## 2026-05-11: retrieval-policy.md 内容更新 + +**类型**:内容调整 + +**背景**:[待补充] + +**改动**:在 onprem / support / autoload-onprem-rag-only / autoload-support-rag-only 四个版本的 `retrieval-policy.md` 上做了同步内容更新。 + +**根因**:N/A + +**影响**:与同月 12 日的 policy 批量精修配套,使 rag-retrieve hook 策略保持一致。 + +**相关文件**: +- `skills/onprem/rag-retrieve/hooks/retrieval-policy.md` +- `skills/support/rag-retrieve/hooks/retrieval-policy.md` +- `skills/autoload/onprem/rag-retrieve-only/hooks/retrieval-policy.md` +- `skills/autoload/support/rag-retrieve-only/hooks/retrieval-policy.md` + +**Commit/PR**:`e6d1698` + +--- + +## 2026-05-08: 通过 MCP `_meta` 透传 trace_id 给 RAG 工具 + +**类型**:新功能 + +**背景**:需要把 catalog-agent 的 trace_id 透传给 MCP 端的 `rag_retrieve` / `table_rag_retrieve` 服务,便于跨进程追踪。 + +**改动**: +- 新增 `agent/mcp_trace_meta.py`:通过 `patch_mcp_client_session_trace_meta()` 对 `mcp.ClientSession.call_tool` 做一次幂等 monkey-patch,调用时若工具名在 `{"rag_retrieve", "table_rag_retrieve"}` 集合内且当前请求上下文有 `trace_id`,则注入到 `kwargs["meta"]["trace_id"]`;并提供 `_call_tool_with_meta_compat` 以兼容旧版 MCP SDK(不接受 `meta=` 关键字时退化为手动构造 `CallToolRequestParams._meta`)。 +- `agent/deep_assistant.py`:在 `get_tools_from_mcp()` 入口处调用一次补丁安装。 +- 同步调整 `skills/onprem/rag-retrieve/rag_retrieve_server.py` 与 `skills/support/rag-retrieve/rag_retrieve_server.py`,接收并使用 `_meta.trace_id`。 + +**根因**:N/A + +**影响**: +- `rag_retrieve` / `table_rag_retrieve` 现在在 MCP `_meta` 上必带 `trace_id`(若上下文存在)。 +- 全局 monkey-patch 风格 - 只要 `get_tools_from_mcp()` 被调用过一次后,所有 `ClientSession.call_tool` 都会被包装。 + +**相关文件**: +- `agent/mcp_trace_meta.py`(新) +- `agent/deep_assistant.py` +- `skills/onprem/rag-retrieve/rag_retrieve_server.py` +- `skills/support/rag-retrieve/rag_retrieve_server.py` + +**Commit/PR**:`1f06450` + +--- + +## 2026-05-06: 新增 kfs-answer skill (support 分支) + +**类型**:新功能 + +**背景**:[待补充] - 为 support 分支补齐 kfs-answer 能力(onprem 分支此前已有同名 skill)。 + +**改动**:新增 `skills/support/kfs-answer/`,包括 `SKILL.md` 与 `scripts/` 下的 `query.py` / `search.py` / `detail.py` / `query_db.py` / `format_answer.py` / `merge_citations.py` / `_session.py` 共 7 个脚本(约 1809 行)。 + +**根因**:N/A + +**影响**:support 部署版本获得 kfs-answer 能力。 + +**相关文件**: +- `skills/support/kfs-answer/**` + +**Commit/PR**:`a9227b8` + +--- + +## 2026-05-06: Daytona 沙箱增量同步漏掉符号链接 + +**类型**:Bug 修复 + +**背景**:dataset 通过符号链接挂载,但增量同步用 `find -type f` 只匹配普通文件,导致 dataset 符号链接没被检测到也没被打包同步到 Daytona 沙箱;并且 `tar.add()` 默认不 dereference,打进去的是指向宿主机路径的悬空软链。 + +**改动**: +- `utils/daytona_sync._list_local_changed_files`:同时匹配 file 和 symlink (`-type f -o -type l`)。 +- `utils/daytona_sync._tar_workspace_entries`:`tar.add(dereference=True)`,把软链解引用为实际内容打包。 +- `skills/onprem/kfs-answer/SKILL.md` 和 `prompt/system_prompt_deep_agent.md`:统一数据集路径用复数形式 `datasets/`。 + +**根因**:`find -type f` 与 `tar.add()` 默认行为对符号链接不友好。 + +**影响**:Daytona 模式下 kfs-answer 等依赖 dataset 软链的 skill 可以正常使用沙箱内的数据;提示词与 SKILL.md 内的路径口径统一。 + +**相关文件**: +- `utils/daytona_sync.py` +- `skills/onprem/kfs-answer/SKILL.md` +- `prompt/system_prompt_deep_agent.md` + +**Commit/PR**:`3c0fa49` + +--- + +## 2026-04-24: PostAgent hooks 非阻塞执行 + 临时停用 ToolOutputLengthMiddleware + +**类型**:性能优化 / 临时调整 + +**背景**:非流式响应路径上 `_execute_post_agent_hooks` 是同步等待,阻塞了响应返回。 + +**改动**: +- `routes/chat.py`:非流式分支将 `await _execute_post_agent_hooks(...)` 改为 `asyncio.create_task(_execute_post_agent_hooks(...))`,hook 在后台执行,不阻塞响应。 +- `agent/deep_assistant.py`:将 `ToolOutputLengthMiddleware` 整段注释掉(未删除,可恢复)。 +- `utils/settings.py`:切换 `DAYTONA_API_KEY` / `DAYTONA_SERVER_URL` 注释行(启用自托管 Daytona,注释掉 SaaS 行)。 + +**根因**:N/A(性能优化为主) + +**影响**: +- 非流式接口响应不再等待 PostAgent hooks 完成 → hook 中失败/异常**只会被 task 内部的 logger 捕获**,调用方收不到错误反馈。 +- 工具输出长度暂时不再被截断,存在超长输出冲爆上下文的风险(中间件已被注释,并未拆除)。 + +**相关文件**: +- `routes/chat.py` +- `agent/deep_assistant.py` +- `utils/settings.py` + +**Commit/PR**:`45a9494` + +--- + +## 2026-04-23: PrePrompt hook 内容改为模板占位符注入 + +**类型**:重构 + +**背景**:原先 PrePrompt hook 的产出文本是在 `system_prompt_default.format(...)` 之后追加在 prompt 末尾,hook 内容在 prompt 中的位置固定且偏后,模板对它的可见性差。 + +**改动**:`agent/prompt_loader.load_system_prompt_async`:先执行 `execute_hooks('PrePrompt', config)` 拿到 `hook_content`,然后通过新增的 `{hook_content}` 占位符传入 `system_prompt_default.format(...)`;模板侧 `prompt/system_prompt.md` 增加对应占位符。 + +**根因**:N/A(结构化注入更可控) + +**影响**:编写 PrePrompt hook 的 skill 必须依赖模板里 `{hook_content}` 占位符的位置;若使用了未升级的旧模板,hook 内容将不再出现在最终 system prompt 中。 + +**相关文件**: +- `agent/prompt_loader.py` +- `prompt/system_prompt.md` + +**Commit/PR**:`51fbf01` + +--- + +## 2026-04-23: Daytona 沙箱接入 + +**类型**:新功能 + +**背景**:技能脚本需要在隔离沙箱中执行(Daytona),避免直接污染宿主机。 + +**改动**: +- `agent/deep_assistant.py`: + - 在 `init_agent` 中读取 `DAYTONA_ENABLED` / `DAYTONA_API_KEY` / `DAYTONA_SERVER_URL`,启用时创建 `DaytonaSandbox`;并将 `sandbox` / `sandbox_type` 传到 `create_custom_cli_agent` / `agent.invoke_config`。 + - 重构为并行加载:`load_system_prompt_async` 与 `load_mcp_settings_async` 用 `asyncio.gather` 并行;`get_tools_from_mcp` 与 `asyncio.to_thread(init_daytona_sandbox, ...)` 并行;`init_agent` 现在返回 `(agent, checkpointer, sandbox)`(多了 sandbox)。 + - `enable_skills` 时 `skills_sources` 从 `"/skills"` 改为 `"/workspace/skills"`(指向沙箱内的路径)。 +- `agent/prompt_loader.py`:`agent_dir_path` 在 `DAYTONA_ENABLED=True` 时改为 `/workspace`,否则保持本地路径。 +- `utils/daytona_sync.py` 新增(204 行):沙箱与本地 workspace 双向同步。 +- `pyproject.toml` / `poetry.lock` / `requirements.txt`:新增 `daytona`、`langchain_daytona` 依赖。 +- `utils/settings.py`:新增 `DAYTONA_API_KEY` / `DAYTONA_SERVER_URL` / `DAYTONA_ENABLED` 配置。 + +**根因**:N/A + +**影响**: +- `init_agent` 返回元组从 2 元素变为 3 元素 (`agent, checkpointer, sandbox`)——**调用方必须同步更新解构**。 +- skill 在沙箱模式下的根路径与本地模式不同,所有写死路径的 hook / 脚本需要兼容两种环境。 + +**相关文件**: +- `agent/deep_assistant.py` +- `agent/prompt_loader.py` +- `utils/daytona_sync.py`(新) +- `utils/settings.py` +- `pyproject.toml`, `poetry.lock`, `requirements.txt` + +**Commit/PR**:`c9e0789`, `8446dab` + +--- + +## 2026-04-22: 新增 rag-retrieve-no-citation 与 novare-context 两个开发中 skill + +**类型**:新功能 + +**背景**:[待补充] + +**改动**: +- `skills/developing/rag-retrieve-no-citation/`:完整 skill 包,含 `.claude-plugin/plugin.json`、`README.md`、`hooks/pre_prompt.py`、`hooks/retrieval-policy.md` 与 `hooks/retrieval-policy-forbidden-self-knowledge.md`、独立 `rag_retrieve_server.py` + `rag_retrieve_tools.json` + `mcp_common.py`。 +- `skills/developing/novare-context/`:包含 `.claude-plugin/plugin.json`、`README.md`、`hooks/pre_prompt.py`。 + +**根因**:N/A + +**影响**:开发中 skill 集合扩张,可作为后续正式版本的母版。 + +**相关文件**: +- `skills/developing/rag-retrieve-no-citation/**` +- `skills/developing/novare-context/**` + +**Commit/PR**:`7a30e52` + +--- ### 2026-05-20 - **变更**: `mcp-ui` 和 `data-dashboard` 从自定义 `uri + data` 工具协议改为 MCP Apps 模式 - **说明**: 静态 HTML App 放在各 skill 的 `apps/` 目录,host 通过 resource URI 加载 iframe,再用 postMessage 传递工具数据 diff --git a/.features/skill/decisions/2026-05-subagent-support.md b/.features/skill/decisions/2026-05-subagent-support.md new file mode 100644 index 0000000..181eb68 --- /dev/null +++ b/.features/skill/decisions/2026-05-subagent-support.md @@ -0,0 +1,47 @@ +--- +date: "2026-05-11" +status: pending +topic: "subagent-support" +impact: [skill, agent, deep_assistant] +--- + +# Sub-Agent 在 skill 内的承载方式 + +## 背景 + +业务方 (pmda-drug-info) 需要在单个 skill 内同时承载若干面向不同子任务的专用 agent +(single-drug / interaction / adverse-event / patient-specific),每个子 agent +需要独立的 system prompt 和工具白名单,但应与主 agent 复用同一组 MCP 工具实例 +与同一份 LLM。 + +[待补充]:是否考虑过用 skill-per-subagent 的方式(每个子 agent 一个独立 skill)。 + +## 选项 + +### 选项 A:skill 内 `agents/*.md` + 全局 `SubAgentMiddleware`(已实现) +- 优点: + - skill 包自洽,子 agent 定义与 hook / MCP 同包发布。 + - 复用 `deepagents.middleware.subagents.SubAgentMiddleware`,无需自研路由层。 + - 工具按 `tools` 字段白名单过滤,统一以 MCP tool name 引用。 +- 缺点: + - 跨 skill 子 agent **同名时静默 last-wins 覆盖**,仅有 warning,无强校验。 + - 中间件位置耦合:`SubAgentMiddleware` 必须插在 + `CustomFilesystemMiddleware` 之后、`AnthropicPromptCachingMiddleware` 之前 + (与 `create_deep_agent` 顺序匹配),改动中间件顺序时容易踩坑。 + +### 选项 B:每个子 agent 单独建一个 skill +- 优点:天然隔离,命名冲突由 skill 加载层处理。 +- 缺点:同一业务的多个子 agent 在 skill 列表里散落,部署 / autoload 配置复杂; + pmda-drug-info 的 4 个子 agent 强相关,作为同一 skill 更自然。 + +## 决策 + +选择 **选项 A**(已落地)。 + +## 影响 + +- 需要改动:调用方知道 `init_agent` 返回元组现已包含 sandbox(与 daytona 改动叠加)。 +- 风险:sub-agent 同名静默覆盖;未来如多 skill 都暴露 sub-agent,需要增加冲突检测。 +- 后续任务: + 1. 沉淀 sub-agent 编写规范(`agents/*.md` frontmatter 字段 + 工具白名单约定)。 + 2. 跨 skill sub-agent 命名冲突的检测策略——是否升级为 error / 加 skill 名前缀。 diff --git a/agent/deep_assistant.py b/agent/deep_assistant.py index ec11513..20439b7 100644 --- a/agent/deep_assistant.py +++ b/agent/deep_assistant.py @@ -480,13 +480,19 @@ def create_custom_cli_agent( backend = FilesystemBackend(root_dir=workspace_root, virtual_mode=False) # Set up composite backend with routing based on the new implementation + # NOTE: virtual_mode=True anchors all paths to root_dir. This is required for + # these offload-only backends: CompositeBackend strips the route prefix and + # forwards "/" to grep, so virtual_mode=False would resolve "/" to the real + # filesystem root and scan the whole disk (hitting /usr, /var, other sessions' + # temp dirs), causing 45-152s grep calls. virtual_mode=True confines grep to + # the temp dir and filters out-of-root results. large_results_backend = FilesystemBackend( root_dir=tempfile.mkdtemp(prefix="deepagents_large_results_"), - virtual_mode=False, + virtual_mode=True, ) conversation_history_backend = FilesystemBackend( root_dir=tempfile.mkdtemp(prefix="deepagents_conversation_history_"), - virtual_mode=False, + virtual_mode=True, ) composite_backend = CompositeBackend( default=backend,