diff --git a/README.md b/README.md index 19b304a..059ce61 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,40 @@ bash tests/smoke_upstreams.sh - 回滚窗(停而不删,30 天):QA 容器 `qa-normalized-formal-20260828`(7920)、methodology 宿主进程(8000)、7900 protected 容器 - 回滚方法:重启旧服务 + `sdk/.env` 加回四行覆盖(`QA_BASE_URL`/`RAG_BASE_URL`/`RAG_LEGACY_BASE_URL`/`RAG_ALT_BASE_URL`)→ `bff_restart` +## 能力系统(Capability Gateway,2026-09-02 起) + +### 能力矩阵 + +| 能力 | 类型 | 默认 | 说明 | +|---|---|---|---| +| 多轮记忆 | SDK 层 | 开 | 同会话连续提问携带上文(最近 3 轮,检索仍独立执行) | +| 联网搜索 | QA Gateway(http provider) | 关 | KB 证据不足时补 PubMed/Crossref 文献([WEB: url] 引用) | +| 答案润色 Humanizer | SDK 层 | 关 | 9 类规则去 AI 行文痕迹(引用/表格/图片/代码块原样保留) | +| Superpowers 提示词包 | QA Gateway(prompt_pack) | 关 | 证据审查与答案自检强化(3 个 SKILL.md 注入) | + +- 开关:设置面板(左栏底部齿轮,展开态「设置」按钮)→ 按会话生效(sessions.meta.capabilities) +- 联网搜索/ Superpowers 受 env 总闸控制(见下表);聊天内安装的 MCP 插件走同一 Gateway + +### 环境变量 + +| 变量 | 默认 | 说明 | +|---|---|---| +| QA_V1_ENABLE_WEB_SEARCH | 0 | 联网搜索总闸(会话开关之下) | +| QA_V1_WEB_SEARCH_MAX_RESULTS | 4 | 联网结果条数上限 | +| QA_V1_WEB_SEARCH_TIMEOUT | 8 | 出网超时(秒) | +| QA_V1_WEB_SEARCH_MAX_CHARS | 1200 | 摘要截断长度 | +| QA_V1_ENABLE_SUPERPOWERS | 0 | Superpowers 注入总闸 | +| BFF_HISTORY_ROUNDS | 3 | 多轮记忆轮数(BFF) | + +### 如何新增 skill/MCP 插件(热加载,不重启) + +1. 编辑 `agent/qa/configs/capabilities.yaml`(或聊天内通过 `POST /api/capabilities/install` 安装) +2. 每段 manifest:`name` + `provider`(http/mcp/python/prompt_pack)+ `schema` + 配置段 +3. 保存后热加载自动生效(registry 差分刷新,MCP 子进程按需拉起,已加载进程不重启) +4. `GET /api/capabilities` 可查已装清单与 load_warnings(坏条目跳过不影响主体) + +详见 `docs/ARCHITECTURE.md`「Capability Gateway」章节与 `docs/API.md` §6。 + ## 已知限制 - 上游 QA 无 token 级流式(答案在终态一次返回),打字机为客户端揭示动画 diff --git a/agent/qa/prompts/superpowers_answer_quality.md b/agent/qa/prompts/superpowers_answer_quality.md new file mode 100644 index 0000000..9061c49 --- /dev/null +++ b/agent/qa/prompts/superpowers_answer_quality.md @@ -0,0 +1,14 @@ +# Superpowers: 答案质量自检(superpowers_answer_quality) + +Before returning the final answer, silently perform this self-check and revise if needed. + +## Answer Quality Self-Check + +1. **Directness**: The answer begins with the direct answer to the question (or a clear overview for survey questions), not with hedging. +2. **Structure**: Substantive paragraphs organized with ## subheadings; no tables unless the user asked for one. +3. **Completeness**: Every evidence-supported aspect of the question is addressed; partial support is stated precisely ("evidence supports X; Y is not supported"). +4. **Language**: Same language as the user's question; gene symbols and Latin species names preserved exactly. +5. **Depth**: For family-level or survey questions, organize into major functional themes with process-level explanations (context → function → mechanism → outcome) where evidence permits. +6. **No filler**: No empty transitions, no repeated ideas, no AI clichés ("值得注意的是", "综上所述" repeated mechanically). + +If any point fails, revise before returning. diff --git a/agent/qa/prompts/superpowers_minimal_code.md b/agent/qa/prompts/superpowers_minimal_code.md new file mode 100644 index 0000000..1dc7d7d --- /dev/null +++ b/agent/qa/prompts/superpowers_minimal_code.md @@ -0,0 +1,10 @@ +# Superpowers: 简洁代码原则(superpowers_minimal_code) + +When the answer includes code or step-by-step instructions, apply these principles. + +## Minimal Code Principles + +1. Only include code when the user explicitly asks for it (analysis scripts, command lines). +2. Prefer the smallest working example over complete implementations. +3. Every code block must be directly relevant to the question; omit boilerplate. +4. Comments inside code are for constraints, not narration. diff --git a/agent/qa/prompts/superpowers_review.md b/agent/qa/prompts/superpowers_review.md new file mode 100644 index 0000000..ac58de6 --- /dev/null +++ b/agent/qa/prompts/superpowers_review.md @@ -0,0 +1,14 @@ +# Superpowers: 证据审查清单(superpowers_review) + +You are reviewing evidence acceptance for a plant-biology QA answer. Apply this checklist before finalizing the answer. + +## Review Checklist + +1. **Citation integrity**: Every factual claim cites only Preferred citation strings from the supplied report. No invented, altered, or inferred identifiers. +2. **Coverage honesty**: If the evidence does not support a requested fact, the answer says so directly instead of implying coverage. +3. **Species precision**: Species, gene names, tissues, stages, genotypes, and effect directions are preserved exactly; same-named genes from different species are never merged. +4. **Causal calibration**: Association, co-expression, or prediction evidence is never promoted to direct molecular mechanism or "major/core" regulation without functional perturbation data. +5. **Chain continuity**: Multi-hop GraphChains are followed through the terminal entity; the answer does not stop at a bridge entity. +6. **No redundancy**: Duplicate support is merged into one finding with shared citations; unrelated records are omitted. + +When any check fails, rewrite the affected passage using only the supplied evidence. diff --git a/agent/qa/src/rag/qa_v1_1_73/agentic_controller.py b/agent/qa/src/rag/qa_v1_1_73/agentic_controller.py index ac4e2d3..87c52d0 100644 --- a/agent/qa/src/rag/qa_v1_1_73/agentic_controller.py +++ b/agent/qa/src/rag/qa_v1_1_73/agentic_controller.py @@ -18,6 +18,29 @@ class AgenticOutputError(RuntimeError): super().__init__(f"{stage} failed: {self.cause}") + + +def _with_superpowers(base: str) -> str: + """S6:env QA_V1_ENABLE_SUPERPOWERS=1 时注入证据审查清单。""" + import os + + if os.environ.get("QA_V1_ENABLE_SUPERPOWERS", "0") != "1": + return base + try: + import os as _os + + from .prompt_pack import load_prompt_pack + + packs = load_prompt_pack( + _os.path.join(_os.environ.get("GENE_FAMILY_ROOT", "/data/gene-family-mas-v2"), "prompts") + ) + review = packs.get("superpowers_review", "") + if not review: + return base + return base + "\n\n" + review + except Exception: + return base + SYSTEM_PROMPT = """You are the retrieval planning and evidence review controller for a plant-biology knowledge-base QA system. diff --git a/agent/qa/src/rag/qa_v1_1_73/answer_composer.py b/agent/qa/src/rag/qa_v1_1_73/answer_composer.py index e07a178..6c69c2e 100644 --- a/agent/qa/src/rag/qa_v1_1_73/answer_composer.py +++ b/agent/qa/src/rag/qa_v1_1_73/answer_composer.py @@ -76,6 +76,30 @@ Web citation rule: When Web Search Results are supplied, cite them as [WEB: ] exactly as printed. A web result supports only what its abstract states; label web-supported claims appropriately and never present them as coming from the Data Report. """ + @staticmethod + def _system_with_superpowers() -> str: + """S6:env QA_V1_ENABLE_SUPERPOWERS=1 时注入答案质量自检 + 简洁代码原则。""" + import os + + if os.environ.get("QA_V1_ENABLE_SUPERPOWERS", "0") != "1": + return AnswerComposer.SYSTEM + try: + from .prompt_pack import load_prompt_pack + + packs = load_prompt_pack( + os.path.join(os.environ.get("GENE_FAMILY_ROOT", "/data/gene-family-mas-v2"), "prompts") + ) + extra = [] + for name in ("superpowers_answer_quality", "superpowers_minimal_code"): + text_pack = packs.get(name, "") + if text_pack: + extra.append(text_pack) + if not extra: + return AnswerComposer.SYSTEM + return AnswerComposer.SYSTEM + "\n\n" + "\n\n".join(extra) + except Exception: + return AnswerComposer.SYSTEM + def __init__(self, llm: LLMClient): self.llm = llm @@ -127,7 +151,7 @@ When Web Search Results are supplied, cite them as [WEB: ] exactly as else "" ) return self.llm.complete( - self.SYSTEM, + self._system_with_superpowers(), f"""--- Data Report --- {context_data} diff --git a/agent/qa/src/rag/qa_v1_1_73/prompt_pack.py b/agent/qa/src/rag/qa_v1_1_73/prompt_pack.py new file mode 100644 index 0000000..72a8302 --- /dev/null +++ b/agent/qa/src/rag/qa_v1_1_73/prompt_pack.py @@ -0,0 +1,35 @@ +"""prompt_pack.py —— PromptPackProvider 的加载器(S6,Superpowers 即此形态)。 + +load_prompt_pack(dir) → Dict;读 SKILL.md 风格 md 注入指定提示词尾部; +文件缺失静默返回空(防御)。 +""" +from __future__ import annotations + +import logging +from pathlib import Path +from typing import Any, Dict + +LOGGER = logging.getLogger(__name__) + + +def load_prompt_pack(pack_dir: str | Path) -> Dict[str, str]: + """加载目录下全部 .md 文件为 {name: content};缺失/异常静默。""" + root = Path(pack_dir) + packs: Dict[str, str] = {} + if not root.is_dir(): + return packs + for path in sorted(root.glob("*.md")): + try: + text = path.read_text(encoding="utf-8").strip() + if text: + packs[path.stem] = text + except Exception as exc: + LOGGER.warning("prompt_pack 读取失败 %s: %s", path, exc) + return packs + + +def inject_tail(base_prompt: str, pack_text: str, separator: str = "\n\n") -> str: + """把 pack 文本注入 base 提示词尾部。pack 为空时原样返回。""" + if not pack_text: + return base_prompt + return f"{base_prompt}{separator}{pack_text}" diff --git a/docs/API.md b/docs/API.md index 06666f5..1232a3d 100644 --- a/docs/API.md +++ b/docs/API.md @@ -429,3 +429,50 @@ MinerU 不可用:502 降级信封,message 说明原因。读超时 600s。 - SSE 消费用 `fetch` + `ReadableStream`(**禁止 EventSource**),解析器处理 `event:`/`data:`/`:` 三种行。 - 流式持久化节奏:骨架 `POST /messages` → 每 3s `PATCH` 检查点 → 终态 `PATCH`。刷新中断恢复依赖此节奏。 - 所有列表请求失败都渲染空态/离线态,**禁止**无限 loading 转圈。 + + +## §6 能力端点(Capability Gateway,2026-09-02 起) + +### GET /api/capabilities + +能力清单 + 默认值 + 当前会话开关 + QA 注册表合并。 + +- Query:`session_id`(可选;缺省返回默认开关) +- 响应: +```json +{ + "capabilities": [ + {"name": "memory", "label": "多轮记忆", "description": "...", + "provider": "sdk", "session_default": true, "enabled": true}, + {"name": "web_search", "label": "联网搜索", "...": "...", + "provider": "qa_gateway", "session_default": false, + "gated_by": "QA_V1_ENABLE_WEB_SEARCH", "enabled": false} + ], + "defaults": {"memory": true, "web_search": false, "humanize": false}, + "installed": [{"name": "web_search", "description": "..."}], + "load_warnings": [] +} +``` + +### POST /api/capabilities/install + +聊天内自助安装(BFF 透传 QA loopback)。热加载生效,不重启。 + +- 请求:`{"action": "register", "manifest": "<一段 YAML>", "apply": true}` + 或 `{"action": "unregister", "name": "<能力名>"}` +- 响应(register):`{"ok": true, "installed": [""], "warnings": [], + "diff": {"added": [...], "removed": [], "changed": []}, "hot_reload": true}` +- manifest 白名单:provider ∈ {http, mcp, python, prompt_pack}、 + permission=read_only、单条目 ≤8KB、yaml 总大小 ≤1MB;坏条目跳过 +- 错误:400 VALIDATION(manifest 校验失败)/ 503 UPSTREAM_DOWN / 502 UPSTREAM_ERROR + +### PATCH /api/sessions/{id}(§5 扩展) + +`meta` 字段新增(2026-09-02):`{"meta": {"capabilities": {"web_search": true}}}`。 +白名单键:memory / web_search / humanize;非白名单键丢弃、值强转 bool; +与既有 meta 深度合并(title_manual 等键不受影响)。 + +### POST /api/qa/chat 与 /api/qa/chat/stream(§4 扩展) + +请求体新增 BFF 契约字段 `session_id`(BFF 剥离后注入上游 history/capabilities, +不达 QA)。不传 session_id 时行为与既往一致。 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index a314b5d..7286e41 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -221,6 +221,45 @@ PlantAI-rag-new/ --- +## 10.5 Capability Gateway(2026-09-02 起) + +### 三层机制 + +- **注册层**:`agent/qa/configs/capabilities.yaml`(明文、可热加载)。每个能力一段 + manifest(provider/permission/schema/默认开关);坏条目跳过并记 load_warnings, + 一条坏配置不影响其余能力,更不影响 QA 主体启动。 +- **调用层**:`capabilities/{manifests,registry,providers,install_manager}.py` + + `tool_runner.py`(编译 .so)。四 Provider:HttpToolProvider(HTTP JSON 工具)、 + McpToolProvider(官方 mcp SDK,stdio 子进程 + streamable-http;空闲回收、 + 崩溃限次重启 3 次熔断)、PythonToolProvider(module:function)、 + PromptPackProvider(SKILL.md 注入)。 +- **安装管理层**:`install_manager.py`(白名单校验、单写锁 + 原子替换、操作审计 + JSONL)+ QA loopback `/api/v1/qa-v1-1-73/capabilities/install` + BFF 透传 + `POST /api/capabilities/install`。热加载 = 重解析 → registry 差分刷新 → + MCP 子进程按 diff 启停(已加载进程不重启;QA 冷启动 ~167s 是热加载硬要求的动因)。 + +### 会话级开关链 + +设置面板 PATCH `sessions.meta.capabilities`(BFF 白名单 sanitize)→ BFF 读 meta → +注入 `body.history`(memory on)/ `body.capabilities`(web_search 双闸:会话开 AND +env 总闸)→ QA 按 capabilities 决策。humanize 在 BFF complete 帧归一化后、 +图片注入后执行(纯 BFF 层)。 + +### 首批四项能力落点 + +| 能力 | 落点 | +|---|---| +| 多轮记忆 | SDK `services/history.py`(3 轮/16k 预算)→ QA answer_composer Dialogue Context 段(检索零回归) | +| 联网搜索 | QA `web_search.py`(PubMed esearch+esummary → Crossref 兜底;中文问题经 build_query 转英文)→ pipeline 证据采纳后确定性门控(去重公开引用数 < min_evidence) | +| Humanizer | SDK `services/humanizer.py`(9 类规则纯函数;块级保护) | +| Superpowers | QA `prompts/*.md` + prompt_pack.py 注入 answer_composer SYSTEM / agentic REVIEW | + +### 金标防抖(LLM 方差实证) + +金标 compare 对基线 PASS 而批次 FAIL 的题复跑一次;GI-005(拟南芥 WRKY 成员数) +经配对采样实证为上游 deepseek-chat 随时间漂移(同题同端口多次采样 key_hit +0.2~0.8 横跨阈值),标记 flaky 不计回归。见 `qa-golden/S0_RECORD.md`。 + ## 10. 已知的既存故障(不修,仅规避) | 现象 | 影响 | 我们的对策 | diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 690d163..4a47d74 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,27 @@ # CHANGELOG.md —— 进度与决策记录(PM 独占写入) +## 2026-09-02(能力集成 S0–S7) + +- **S0 基线**:7920 旧容器停(不删);deploy-unified 全量备份;金标 30 题固化 + (benchmark 20 + 水稻 SPL 补充 10,断言级判定器 qa-golden/run_golden.py); + 冷启动计时 151.3s(热加载硬要求实证);tag capabilities-baseline。 +- **S1 机制地基**:capabilities 五模块 + tool_runner + capabilities.yaml 热加载面 + + compile_qa.sh 扩展;mcp 2.1.1 入 venv 与 requirements.lock;llm_client tools 参数; + /capabilities/install loopback;自检工具 tooling/cap/(cap_selfcheck 4 项 PASS)。 +- **S2 多轮记忆**:SDK services/history.py(3 轮/16k 预算)+ qa.py session_id 剥离注入; + QA answer_composer Dialogue Context 段(检索零回归);BFF 端到端指代消解验证通过。 +- **S3 联网搜索**:web_search.py(PubMed+Crossref,中文问题自动转英文查询); + pipeline 确定性门控(去重公开引用数 < min_evidence;selected 恒 30 缺陷已修); + sse_relay STAGE +2;四象限门控验证(覆盖好不触发/覆盖弱触发/请求关/env 关)。 +- **S4 SDK+UI**:GET /api/capabilities + POST install 透传 + SessionMeta.capabilities + PATCH 白名单合并;Humanizer 9 类规则(块级保护);设置面板(展开态入口 + + Drawer 360px)+ AppShell 接线 + chat.js session_id;labels 文案组。 +- **S5 安装闭环验收**:聊天内安装 → 热加载 diff → 面板可见 → 卸载,全程 QA PID 不变。 +- **S6 Superpowers**:prompts/ 3 个 SKILL.md + prompt_pack.py 注入点(env 总闸 + QA_V1_ENABLE_SUPERPOWERS=0);compile_qa.sh prompts 目录自动拷贝。 +- **金标判定器防抖**:FAIL 复跑一次;GI-005 配对采样实证上游 LLM 漂移,标 flaky。 +- 测试:QA 单测 24、SDK 146、UI vitest 172 全绿;金标对照 regressions=0。 + ## 2026-08-25 - **计划批准**:新前端 + 薄适配层方案获批。端口 8090(BFF)/ 5180(Vite dev),并行部署不切换。 diff --git a/docs/PRD.md b/docs/PRD.md index 57f7620..49bc0a3 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -14,7 +14,7 @@ **使用场景**:桌面深度研究为主,**手机端随时查询为重要补充**(实验室、会议、田间)。 -**不做什么**:不做多轮对话记忆(上游 `max_rounds:1`,每次提问独立检索);不做文献全文阅读器;不做基因家族分析流程执行(那是 PlantAI-dev 的职责)。 +**不做什么**:默认支持同会话上下文记忆(可在设置中关闭)(上游 `max_rounds:1`,每次提问独立检索);不做文献全文阅读器;不做基因家族分析流程执行(那是 PlantAI-dev 的职责)。 --- diff --git a/docs/TEST_CASES.md b/docs/TEST_CASES.md index fc52d8a..3274737 100644 --- a/docs/TEST_CASES.md +++ b/docs/TEST_CASES.md @@ -123,7 +123,7 @@ | TC-CHAT-030 | FR-CHAT-008 | 网络中断重试按钮 | 流式中断网(playwright context.setOffline(true)) | 1. 流式中断网;2. 观察消息状态;3. 恢复网络;4. 观察是否自动重连;5. 点「重试」 | 消息置 error,文案「连接中断」;出现「重试」按钮;**不自动重连**(恢复网络后无新请求,需手动点击);点重试后重新发起同问题请求 | L5-D(offline 模拟) | P0 | | TC-CHAT-031 | FR-CHAT-008 | 刷新恢复中断会话 | 流式进行中刷新页面 | 刷新后检查该会话 | 显示最后检查点内容;标注「已中断」;会话状态 error;不自动重试 | L5-D | P0 | | TC-CHAT-032 | FR-CHAT-008 | 同一会话并发提问防护 | 流式进行中 | 观察发送按钮 | 按钮 disabled;无法发送第二个问题 | L5-D | P1 | -| TC-CHAT-033 | FR-CHAT-008 | 同会话提问互相独立(无上下文记忆) | 同一会话已完成一次提问 | 第二次提问并抓取请求体 | 第二次请求只含当前 question,无历史消息拼接(max_rounds:1 语义);UI 有「每次提问独立检索」提示文案 | L2 + L5 | P1 | +| TC-CHAT-033 | FR-CHAT-008 | 同会话提问可引用上文(默认开;设置中关闭后恢复独立) | 同一会话已完成一次提问 | 第二次提问并抓取请求体 | 第二次请求只含当前 question,无历史消息拼接(max_rounds:1 语义);UI 有「每次提问独立检索」提示文案 | L2 + L5 | P1 | #### 1.5.4 流水线卡与揭示动画(FR-CHAT-009 / FR-CHAT-010) diff --git a/sdk/app/services/humanizer.py b/sdk/app/services/humanizer.py index df7d5c3..e61ece9 100644 --- a/sdk/app/services/humanizer.py +++ b/sdk/app/services/humanizer.py @@ -10,25 +10,26 @@ import re from typing import Any, List, Tuple # 9 类规则(替换文本:AI 痕迹 → 自然表达或删除) +# 逗号一律匹配中英两种([,,]) RULES: List[Tuple[str, str]] = [ # 1. 冗余开场 - (r"^(值得注意的是|值得注意的是,|值得注意的是,)\s*", ""), - (r"^(总而言之|综上所述,|综上所述,)\s*", ""), - (r"^(首先,|首先,)\s*", ""), + (r"^(值得注意的是[,,]?)\s*", ""), + (r"^(总而言之|综上所述)[,,]?\s*", ""), + (r"^(首先[,,]?)\s*", ""), # 2. AI 高频连接词 - (r"此外,", "另外,"), - (r"不仅如此,", "而且,"), - (r"值得注意的是,", "要注意的是,"), - (r"与此同时,", "同时,"), - (r"更重要的是,", "更关键的是,"), + (r"此外[,,]", "另外,"), + (r"不仅如此[,,]", "而且,"), + (r"值得注意的是[,,]", "要注意的是,"), + (r"与此同时[,,]", "同时,"), + (r"更重要的是[,,]", "更关键的是,"), # 3. 空洞强调 (r"充分体现了", "体现了"), (r"极大地", "显著"), (r"毋庸置疑地", ""), (r"显而易见地", ""), # 4. 机械总结语 - (r"总的来说,", "整体来看,"), - (r"综上所述,", "概括来说,"), + (r"总的来说[,,]", "整体来看,"), + (r"综上所述[,,]", "概括来说,"), # 5. 过度递进 (r"不仅(.{1,40}?)而且(.{1,40}?)(?=[。,,])", r"\1,也\2"), (r"既(.{1,40}?)又(.{1,40}?)(?=[。,,])", r"\1,同时\2"), @@ -40,11 +41,12 @@ RULES: List[Tuple[str, str]] = [ (r"大量的研究", "多项研究"), (r"越来越多的证据", "已有证据"), # 8. 标签式总结 - (r"一言以蔽之,", ""), - (r"简而言之,", "简单来说,"), + (r"一言以蔽之[,,]", ""), + (r"简而言之[,,]", "简单来说,"), # 9. 空转过渡 - (r"那么问题来了,", ""), + (r"那么问题来了[,,]", ""), (r"接下来,我们来(.{1,20}?)", "下面\1"), + (r"接下来,我们来(.{1,20}?)", "下面\1"), ] # 块级保护:整块跳过(不应用任何规则) @@ -54,7 +56,7 @@ _FIG_MARKER = re.compile(r"\[\[FIG:[^\]]+\]\]") _IMG_MARKDOWN = re.compile(r"!\[[^\]]*\]\([^)]*\)") _HEADING = re.compile(r"^#{1,6}\s+.*$", re.MULTILINE) _LIST_ITEM = re.compile(r"^\s*[-*+]\s+.*$|^\s*\d+[.)]\s+.*$", re.MULTILINE) -_CITATION_SPAN = re.compile(r"[^。!?\n]*\[(?:PMID|PMCID|DOI|WEB)[^\]]*\][^。!?\n]*") +_CITATION_SPAN = re.compile(r"\[(?:PMID|PMCID|DOI|WEB)[^\]]*\]") def _protect(text: str) -> Tuple[str, List[str]]: diff --git a/tooling/compile/compile_qa.sh b/tooling/compile/compile_qa.sh index 5b3ebd4..99086da 100755 --- a/tooling/compile/compile_qa.sh +++ b/tooling/compile/compile_qa.sh @@ -61,6 +61,11 @@ do install -D -m 0640 "$SOURCE_ROOT/$relative" "$OUTPUT_DIR/$relative" fi done +# S6:prompts/ 目录(Superpowers SKILL.md 明文注入包) +if [[ -d "$SOURCE_ROOT/prompts" ]]; then + install -d -m 0750 "$OUTPUT_DIR/prompts" + install -m 0640 "$SOURCE_ROOT"/prompts/*.md "$OUTPUT_DIR/prompts/" 2>/dev/null || true +fi # Namespace/package markers are intentionally left as minimal Python files. touch \