feat(capabilities): S6 Superpowers+S7 终验(文档/humanizer CJK 修复/验收脚本)

- prompts/superpowers_{review,answer_quality,minimal_code}.md + prompt_pack.py
- agentic_controller/answer_composer Superpowers 注入点(env 总闸,关态逐字节一致)
- compile_qa.sh: prompts/ 目录自动拷贝
- humanizer: CJK 逗号规则 + 引用保护收窄(只护标记本身,带引用句可润色)
- docs: README 能力矩阵+env 表 / ARCHITECTURE Gateway 章 / API §6 / PRD:17 /
  TEST_CASES:126 多轮语义 / CHANGELOG
- tooling/cap/final_acceptance.sh 终验矩阵
- 终验: 金标 16/20 regressions=0, 多轮指代 PASS, 热加载 PID 不变 PASS,
  Humanizer 开态「此外」消除且 22 引用保留, 五上游全绿, 7913 已释放

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jerry
2026-09-02 18:27:41 +08:00
co-authored by Claude Fable 5
parent cd7e98e7c7
commit bbd5db2315
14 changed files with 286 additions and 17 deletions
+34
View File
@@ -90,6 +90,40 @@ bash tests/smoke_upstreams.sh
- 回滚窗(停而不删,30 天):QA 容器 `qa-normalized-formal-20260828`(7920)、methodology 宿主进程(8000)、7900 protected 容器 - 回滚窗(停而不删,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` - 回滚方法:重启旧服务 + `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 级流式(答案在终态一次返回),打字机为客户端揭示动画 - 上游 QA 无 token 级流式(答案在终态一次返回),打字机为客户端揭示动画
@@ -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.
@@ -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.
+14
View File
@@ -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.
@@ -18,6 +18,29 @@ class AgenticOutputError(RuntimeError):
super().__init__(f"{stage} failed: {self.cause}") 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 SYSTEM_PROMPT = """You are the retrieval planning and evidence review controller for a
plant-biology knowledge-base QA system. plant-biology knowledge-base QA system.
+25 -1
View File
@@ -76,6 +76,30 @@ Web citation rule:
When Web Search Results are supplied, cite them as [WEB: <full URL>] 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. When Web Search Results are supplied, cite them as [WEB: <full URL>] 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): def __init__(self, llm: LLMClient):
self.llm = llm self.llm = llm
@@ -127,7 +151,7 @@ When Web Search Results are supplied, cite them as [WEB: <full URL>] exactly as
else "" else ""
) )
return self.llm.complete( return self.llm.complete(
self.SYSTEM, self._system_with_superpowers(),
f"""--- Data Report --- f"""--- Data Report ---
{context_data} {context_data}
@@ -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}"
+47
View File
@@ -429,3 +429,50 @@ MinerU 不可用:502 降级信封,message 说明原因。读超时 600s。
- SSE 消费用 `fetch` + `ReadableStream`(**禁止 EventSource**),解析器处理 `event:`/`data:`/`:` 三种行。 - SSE 消费用 `fetch` + `ReadableStream`(**禁止 EventSource**),解析器处理 `event:`/`data:`/`:` 三种行。
- 流式持久化节奏:骨架 `POST /messages` → 每 3s `PATCH` 检查点 → 终态 `PATCH`。刷新中断恢复依赖此节奏。 - 流式持久化节奏:骨架 `POST /messages` → 每 3s `PATCH` 检查点 → 终态 `PATCH`。刷新中断恢复依赖此节奏。
- 所有列表请求失败都渲染空态/离线态,**禁止**无限 loading 转圈。 - 所有列表请求失败都渲染空态/离线态,**禁止**无限 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": ["<name>"], "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 时行为与既往一致。
+39
View File
@@ -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. 已知的既存故障(不修,仅规避) ## 10. 已知的既存故障(不修,仅规避)
| 现象 | 影响 | 我们的对策 | | 现象 | 影响 | 我们的对策 |
+22
View File
@@ -1,5 +1,27 @@
# CHANGELOG.md —— 进度与决策记录(PM 独占写入) # 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 ## 2026-08-25
- **计划批准**:新前端 + 薄适配层方案获批。端口 8090(BFF)/ 5180(Vite dev),并行部署不切换。 - **计划批准**:新前端 + 薄适配层方案获批。端口 8090(BFF)/ 5180(Vite dev),并行部署不切换。
+1 -1
View File
@@ -14,7 +14,7 @@
**使用场景**:桌面深度研究为主,**手机端随时查询为重要补充**(实验室、会议、田间)。 **使用场景**:桌面深度研究为主,**手机端随时查询为重要补充**(实验室、会议、田间)。
**不做什么**:不做多轮对话记忆(上游 `max_rounds:1`,每次提问独立检索);不做文献全文阅读器;不做基因家族分析流程执行(那是 PlantAI-dev 的职责)。 **不做什么**:默认支持同会话上下文记忆(可在设置中关闭)(上游 `max_rounds:1`,每次提问独立检索);不做文献全文阅读器;不做基因家族分析流程执行(那是 PlantAI-dev 的职责)。
--- ---
+1 -1
View File
@@ -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-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-031 | FR-CHAT-008 | 刷新恢复中断会话 | 流式进行中刷新页面 | 刷新后检查该会话 | 显示最后检查点内容;标注「已中断」;会话状态 error;不自动重试 | L5-D | P0 |
| TC-CHAT-032 | FR-CHAT-008 | 同一会话并发提问防护 | 流式进行中 | 观察发送按钮 | 按钮 disabled;无法发送第二个问题 | L5-D | P1 | | 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) #### 1.5.4 流水线卡与揭示动画(FR-CHAT-009 / FR-CHAT-010)
+16 -14
View File
@@ -10,25 +10,26 @@ import re
from typing import Any, List, Tuple from typing import Any, List, Tuple
# 9 类规则(替换文本:AI 痕迹 → 自然表达或删除) # 9 类规则(替换文本:AI 痕迹 → 自然表达或删除)
# 逗号一律匹配中英两种([,,])
RULES: List[Tuple[str, str]] = [ RULES: List[Tuple[str, str]] = [
# 1. 冗余开场 # 1. 冗余开场
(r"^(值得注意的是|值得注意的是,|值得注意的是,)\s*", ""), (r"^(值得注意的是[,,]?)\s*", ""),
(r"^(总而言之|综上所述,|综上所述,)\s*", ""), (r"^(总而言之|综上所述)[,,]?\s*", ""),
(r"^(首先,|首先,)\s*", ""), (r"^(首先[,,]?)\s*", ""),
# 2. AI 高频连接词 # 2. AI 高频连接词
(r"此外,", "另外,"), (r"此外[,,]", "另外,"),
(r"不仅如此,", "而且,"), (r"不仅如此[,,]", "而且,"),
(r"值得注意的是,", "要注意的是,"), (r"值得注意的是[,,]", "要注意的是,"),
(r"与此同时,", "同时,"), (r"与此同时[,,]", "同时,"),
(r"更重要的是,", "更关键的是,"), (r"更重要的是[,,]", "更关键的是,"),
# 3. 空洞强调 # 3. 空洞强调
(r"充分体现了", "体现了"), (r"充分体现了", "体现了"),
(r"极大地", "显著"), (r"极大地", "显著"),
(r"毋庸置疑地", ""), (r"毋庸置疑地", ""),
(r"显而易见地", ""), (r"显而易见地", ""),
# 4. 机械总结语 # 4. 机械总结语
(r"总的来说,", "整体来看,"), (r"总的来说[,,]", "整体来看,"),
(r"综上所述,", "概括来说,"), (r"综上所述[,,]", "概括来说,"),
# 5. 过度递进 # 5. 过度递进
(r"不仅(.{1,40}?)而且(.{1,40}?)(?=[。,,])", r"\1,也\2"), (r"不仅(.{1,40}?)而且(.{1,40}?)(?=[。,,])", r"\1,也\2"),
(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"大量的研究", "多项研究"),
(r"越来越多的证据", "已有证据"), (r"越来越多的证据", "已有证据"),
# 8. 标签式总结 # 8. 标签式总结
(r"一言以蔽之,", ""), (r"一言以蔽之[,,]", ""),
(r"简而言之,", "简单来说,"), (r"简而言之[,,]", "简单来说,"),
# 9. 空转过渡 # 9. 空转过渡
(r"那么问题来了,", ""), (r"那么问题来了[,,]", ""),
(r"接下来,我们来(.{1,20}?)", "下面\1"), (r"接下来,我们来(.{1,20}?)", "下面\1"),
(r"接下来,我们来(.{1,20}?)", "下面\1"),
] ]
# 块级保护:整块跳过(不应用任何规则) # 块级保护:整块跳过(不应用任何规则)
@@ -54,7 +56,7 @@ _FIG_MARKER = re.compile(r"\[\[FIG:[^\]]+\]\]")
_IMG_MARKDOWN = re.compile(r"!\[[^\]]*\]\([^)]*\)") _IMG_MARKDOWN = re.compile(r"!\[[^\]]*\]\([^)]*\)")
_HEADING = re.compile(r"^#{1,6}\s+.*$", re.MULTILINE) _HEADING = re.compile(r"^#{1,6}\s+.*$", re.MULTILINE)
_LIST_ITEM = re.compile(r"^\s*[-*+]\s+.*$|^\s*\d+[.)]\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]]: def _protect(text: str) -> Tuple[str, List[str]]:
+5
View File
@@ -61,6 +61,11 @@ do
install -D -m 0640 "$SOURCE_ROOT/$relative" "$OUTPUT_DIR/$relative" install -D -m 0640 "$SOURCE_ROOT/$relative" "$OUTPUT_DIR/$relative"
fi fi
done 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. # Namespace/package markers are intentionally left as minimal Python files.
touch \ touch \