Merge pull request #33 from sparticleinc/bot/feature-memory-sync

chore(.features): feature memory sync — monthly
This commit is contained in:
Denya0529 2026-05-23 13:53:28 +09:00 committed by GitHub
commit a4b32aad7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 338 additions and 1 deletions

View File

@ -1,12 +1,14 @@
# Skill 功能
> 负责范围:技能包管理服务 - 核心实现
> 最后更新2026-04-20
> 最后更新2026-05-23
## 当前状态
Skill 系统支持两种来源:官方 skills (`./skills/`) 和用户 skills (`projects/uploads/{bot_id}/skills/`)。支持 Hook 系统和 MCP 服务器配置,通过 SKILL.md 或 plugin.json 定义元数据。
2026-04 起 skill 包可在 `agents/*.md` 下定义子 agent`SubAgentMiddleware` 加载);启用 Daytona 沙箱时 skill 加载路径变为沙箱内的 `/workspace/skills`
## 核心文件
- `routes/skill_manager.py` - Skill 上传/删除/列表 API
@ -15,9 +17,22 @@ Skill 系统支持两种来源:官方 skills (`./skills/`) 和用户 skills (`
- `agent/prompt_loader.py` - PrePrompt hooks + MCP 配置合并
- `skills/` - 官方 skills 目录
- `skills_developing/` - 开发中 skills
- `agent/subagent_loader.py` - 扫描 skill `agents/*.md` 加载子 agent2026-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-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` 残留
@ -34,6 +49,13 @@ Skill 系统支持两种来源:官方 skills (`./skills/`) 和用户 skills (`
- ⚠️ 上传大小限制50MBZIP解压后最大 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 目录结构

View File

@ -1,5 +1,273 @@
# 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-04-20
- **新增**: `skills/autoload/onprem/rag-retrieve/hooks/retrieval-policy-forbidden-self-knowledge.md`
- **说明**: 基于现有 `retrieval-policy.md` 衍生出更严格的检索策略,明确禁止在知识问答场景中使用模型自身知识补全答案,要求回答只能来自检索证据

View File

@ -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
## 选项
### 选项 Askill 内 `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 名前缀。