Skill: fandywang87/paper-translation (version 1.0.0)

--- name: paper-translation description: > ArXiv 论文精读级中文翻译，同步到 IMA 知识库 + 腾讯文档。 基于 5 篇论文（MDL/Kunlun/OneTrans/RankMixer/MixFormer）3 轮迭代实战经验。 触发场景：翻译论文、翻译 arxiv、论文精读、论文中文翻译、paper translation、 translate paper、翻译这篇论文、帮我翻译、精读翻译、论文全文翻译、 把这篇论文翻译成中文、翻译 arxiv 论文到知识库。 --- # ArXiv 论文精读翻译 Base directory for this skill: `{SKILL_DIR}` 将 ArXiv 论文逐段翻译成中文，生成双版本 Markdown（IMA + 腾讯文档），并上传到两个平台。 ## 三条铁律 1. **完整翻译不精简** — 逐段翻译每个 paragraph，不遗漏任何论证细节。大模型倾向于"帮你归纳"，但用户要的是精读级翻译。 2. **译注显式标记** — 大模型解读必须用 `> **[译注]**：...` 引用块，绝不混入原文翻译。 3. **简称首次标全称，后续直接用** — 首次出现标注全称并核对原文，后续不再展开。避免错误展开（如 TA=Target Attention 被误写为 Transformer Aggregator）。 ## 标准 6 步流程 ### Step 1: 获取原文 ``` web_fetch https://arxiv.org/html/<id>v<n> ``` - 只 fetch **一次**，节省 token - 同步下载图片：`curl -sL -o x{n}.png https://arxiv.org/html/<paper_id>/x{n}.png` - 下载后检查文件大小，相同大小的异常文件（404 垃圾响应）删除 ### Step 2: 翻译生成 - **逐段翻译**，不做精简 - 首行元信息：原标题、arxiv 链接、年月、机构、翻译辅助大模型名称 - 简称首次出现标全称（核对原文），后续用简称 - 译注用 `> **[译注]**：...` 格式 - 结构化排版：多级标题 + 列表 + 加粗 + 表格 + 引用块 - 公式保留 LaTeX；**`\bm` 全部替换为 `\boldsymbol`** - 图表按原文顺序插入**所在章节标题之后、小节正文之前** - 参考文献完整列出 **表格处理策略**： - 简单表格 → Markdown 表格重写（可搜索/编辑） - 复杂表格（合并单元格/特殊排版）→ PyMuPDF 从 PDF 截取 ### Step 3: 自动化校验 翻译完成后，运行校验脚本： ```bash python3 {SKILL_DIR}/scripts/validate_translation.py <markdown_file> ``` 校验项： | 检查项 | 标准 | |--------|------| | 章节完整性 | 包含：摘要/引言/相关工作/方法/实验/结论/参考文献 | | LaTeX 兼容性 | `\bm` 出现次数 = 0 | | 译注标记 | 数量 > 0，格式为 `> **[译注]**` | | 参考文献 | 条数列出供人工核对 | | 图片链接 | 外链格式正确 | ### Step 4: 生成两版 Markdown - **IMA 版**：图片用 arxiv 外链 URL / base64 data URI - **腾讯文档版**：用脚本从 IMA 版自动替换图片链接为 image_id 图片上传流程： 1. `curl -sL -o x{n}.png https://arxiv.org/html/<paper_id>/x{n}.png` 下载 2. 腾讯文档：`mcporter call tencent-docs upload_image` → 拿 image_id 3. IMA：直接用 arxiv 外链 URL 详见 [references/platform-compat.md](references/platform-compat.md) ### Step 5: 上传 IMA 知识库 ```bash # create_media → COS 上传 → add_knowledge（media_type=7 = Markdown） # 如遇 code=220030（限流），sleep 15s 重试，cos_key 仍有效 ``` ### Step 6: 上传腾讯文档 ```bash TITLE="【YYYY.MM｜组织】XXX 中文翻译" # 必须 ≤36 字符 jq -n --arg title "$TITLE" --rawfile mdx "$FILE" --arg cf "markdown" \ '{title:$title, mdx:$mdx, content_format:$cf}' > /tmp/args.json mcporter call tencent-docs create_smartcanvas_by_mdx --args "$(cat /tmp/args.json)" ``` mcporter 传大参数**不支持 `--args-file`**，必须用 `--args "$(cat file.json)"`。 ## 命名规范（强制） | 平台 | 格式 | 约束 | |------|------|------| | 腾讯文档标题 | `【YYYY.MM｜组织】XXX 中文翻译` | ≤36 字符（按字符数，非字节） | | IMA 文件名 | `【YYYY.MM｜组织】XXX 中文翻译.md` | 同名加 .md | - 两平台**必须完全一致**，不加 `v2`/`图文版` 等后缀 - 示例：`【2026.02｜ByteDance】MixFormer 中文翻译` ## 翻译后 Checklist 完成翻译后逐项确认： - [ ] `grep -c '\\bm'` = 0 - [ ] 简称首次出现已标全称且正确 - [ ] 译注均用 `> **[译注]**：...` 格式 - [ ] 图表位置与原文章节顺序一致 - [ ] 参考文献条数与原文一致 - [ ] IMA 版和腾讯文档版图片格式各自正确 - [ ] 两平台文件名/标题完全一致 - [ ] 首行包含论文元信息（标题/链接/年月/机构/大模型名称） ## 效率优化 - 只 `web_fetch` 一次原文（节省 token） - 直接生成最终版，不生成中间草稿（减少 50%+ 工具调用） - 图片下载 + 上传并行执行 - 用脚本自动从 IMA 版生成腾讯文档版 - 自动化校验脚本在上传前拦截格式问题 ## 参考文档 - **踩坑经验 + 平台兼容性**：[references/platform-compat.md](references/platform-compat.md) — LaTeX 兼容、图片跨平台、API 限流等详细说明 - **迭代历史**：[references/iteration-history.md](references/iteration-history.md) — MixFormer v1→v3 的完整教训记录

path: references/iteration-history.md
content: # 翻译迭代历史（MixFormer 案例）

## 版本演进

| 版本 | 主要问题 | 改进措施 |
|------|---------|---------|
| **v1** | 过度精简、译注未标记、表格用截图不可搜索、图片位置随意 | 用户反馈 5 大改进点 |
| **v2** | 逐段翻译、译注标记、表格改 Markdown、自动化校验 | 图片位置仍有偏差、译注覆盖不够 |
| **v3** | 精确核对图片章节位置、19 处译注（vs v2 的 9 处）、TA 全称修正 | 趋于成熟，形成 SOP v2 |

## 关键教训

1. **翻译质量需要 2-3 轮迭代**才能达到用户满意
2. **v1 的根因**：大模型自作主张精简 + 译注混入正文 + 简称展开错误
3. **v2 的不足**：大方向正确但细节不够（图片位置偏差、译注覆盖不充分）
4. **v3 的突破**：在细节上达标，形成可复用的 SOP

## 已翻译论文清单

| 论文 | 机构 | 年月 | 特殊注意 |
|------|------|------|---------|
| MDL | ByteDance | 2026.02 | — |
| Kunlun | Meta（非百度） | 2026.02 | 机构归属易搞错 |
| OneTrans | ByteDance | 2025.10 | 图片 x5-x8 为 404 |
| RankMixer | ByteDance | 2025.07 | — |
| MixFormer | ByteDance | 2026.02 | 经历 v1→v3 三轮迭代 |


path: references/platform-compat.md
content: # 平台兼容性与踩坑经验

## LaTeX 公式兼容性

| 问题 | 解决方案 |
|------|---------|
| 腾讯文档不支持 `\bm{...}`（bm 宏包） | 全部替换为 `\boldsymbol{...}` |
| `\bold` / `\mathbold` 不兼容 | 统一到 `\boldsymbol` |
| 罕见宏包命令 | 避免使用，仅 `\mathbb{R}` 等标准宏安全 |

**强制流程**：翻译完成后执行 `grep -c '\\bm' file.md`，结果必须为 0。

## 图片跨平台兼容性

| 平台 | 支持 | 不支持 |
|------|------|--------|
| IMA 知识库 | ArXiv 外链 URL、base64 data URI | 本地相对路径（上传后失效） |
| 腾讯文档 | `upload_image` 返回的 image_id | HTTP/HTTPS 外链 |

**标准图片上传流程**：

```bash
# 1. 下载
curl -sL -o x{n}.png https://arxiv.org/html/<paper_id>/x{n}.png

# 2. 腾讯文档：上传拿 image_id
mcporter call tencent-docs upload_image --args '{"file_name":"x1.png","image_base64":"<base64>"}'

# 3. IMA：直接用外链
![Fig](https://arxiv.org/html/<paper_id>/x1.png)
```

**图片 404 排查**：下载后检查文件大小，多个文件大小完全相同通常为 404 垃圾响应，应删除。

## 图表插入位置

图/表插入在**所在章节标题之后、小节正文之前**（与原文顺序一致）。

- Figure 2 应放在 §3.4 章节入口，而非 §3.4.1 之后
- Table 1/2 放在章节标题后即可
- 避免图表打断论证链条

## 腾讯文档 API 踩坑

### 标题长度限制

`create_smartcanvas_by_mdx` 的 `title` 字段**限制 36 字符**（按字符数计算，非字节）。

超长报错：`business 400001: title length exceeds 36 characters`

### mcporter 传大参数

`mcporter call` **不支持 `--args-file`**。可靠写法：

```bash
# 先生成 JSON 文件
jq -n --arg title "$TITLE" --rawfile mdx "$FILE" --arg cf "markdown" \
  '{title:$title, mdx:$mdx, content_format:$cf}' > /tmp/args.json

# 再用 cat 传入
mcporter call tencent-docs create_smartcanvas_by_mdx --args "$(cat /tmp/args.json)"
```

### 授权流程

`setup.sh` 在 CodeBuddy 独立 shell 环境下后台进程会被回收。解决方案：绕开 setup.sh，用同步轮询获取 token：

1. `openssl rand -hex 8` 生成 code
2. 展示授权链接给用户
3. `curl` 同步轮询 token（每 10 秒一次，最多 3 分钟）
4. `mcporter config add` 注册 token

## IMA 知识库 API 踩坑

### add_knowledge 限流

短时间内多次调用返回 `code=220030`（表面"没有权限"，实际是限流）。

**解决方案**：sleep 15 秒后重试，已上传的 cos_key 仍有效，无需重新 create_media。

### 图片不支持直传

`create_media` 的 `media_type=9`（图片）返回 220030。图片只能嵌入 Markdown（base64 data URI 或外链 URL）。

## 翻译内容质量常见问题

| 问题 | 表现 | 预防 |
|------|------|------|
| 简称全称搞错 | TA=Target Attention 错写为 Transformer Aggregator | 首次出现标全称并核对原文 |
| 默认精简原文 | 完整论证压缩成摘要式描述 | 逐段翻译，不做默认精简 |
| 模型解读混入正文 | 评论和翻译混在一起 | 强制 `> **[译注]**：...` 引用块 |
| 事实性错误 | Kunlun 归属错写为百度（实际 Meta） | 元信息必须从原文/搜索确认 |
| PDF 截图文件名搞反 | table1.png 实际是 Table 2 | 保存后按内容核对文件名 |


path: scripts/validate_translation.py
content: #!/usr/bin/env python3
"""
论文精读翻译 — 自动化校验脚本
用法: python3 validate_translation.py <markdown_file>

检查项:
1. 章节完整性（摘要/引言/相关工作/方法/实验/结论/参考文献）
2. LaTeX 兼容性（\bm 出现次数必须为 0）
3. 译注标记（> **[译注]** 格式，数量 > 0）
4. 参考文献条数
5. 图片链接格式
6. 首行元信息
"""

import re
import sys
from pathlib import Path


def validate(filepath: str) -> list[dict]:
    """Validate a translation markdown file. Returns list of check results."""
    path = Path(filepath)
    if not path.exists():
        print(f"ERROR: File not found: {filepath}")
        sys.exit(1)

    content = path.read_text(encoding="utf-8")
    lines = content.split("\n")
    results = []

    # --- 1. 章节完整性 ---
    required_sections = {
        "摘要": r"(摘要|Abstract)",
        "引言": r"(引言|Introduction|1[\.\s])",
        "相关工作": r"(相关工作|Related Work)",
        "方法": r"(方法|Method|Approach|Architecture|Model|Framework)",
        "实验": r"(实验|Experiment|Evaluation|Result)",
        "结论": r"(结论|Conclusion|Discussion)",
        "参考文献": r"(参考文献|References|Bibliography)",
    }
    headers = [l for l in lines if l.startswith("#")]
    header_text = " ".join(headers)

    missing = []
    for name, pattern in required_sections.items():
        if not re.search(pattern, header_text, re.IGNORECASE):
            missing.append(name)

    results.append({
        "check": "章节完整性",
        "status": "PASS" if not missing else "WARN",
        "detail": f"缺少: {', '.join(missing)}" if missing else "所有核心章节均存在",
    })

    # --- 2. LaTeX 兼容性 ---
    bm_count = len(re.findall(r"\\bm\b", content))
    results.append({
        "check": "LaTeX 兼容性 (\\bm)",
        "status": "PASS" if bm_count == 0 else "FAIL",
        "detail": f"\\bm 出现 {bm_count} 次" + ("" if bm_count == 0 else " — 必须全部替换为 \\boldsymbol"),
    })

    # --- 3. 译注标记 ---
    notes = re.findall(r">\s*\*\*\[译注\]\*\*", content)
    note_count = len(notes)
    results.append({
        "check": "译注标记",
        "status": "PASS" if note_count > 0 else "WARN",
        "detail": f"共 {note_count} 处译注" + ("" if note_count > 0 else " — 建议至少添加 1 处译注"),
    })

    # --- 4. 参考文献条数 ---
    # 匹配 [数字] 开头的引用条目
    refs = re.findall(r"^\s*\[\d+\]", content, re.MULTILINE)
    results.append({
        "check": "参考文献",
        "status": "INFO",
        "detail": f"共 {len(refs)} 条（请与原文核对）",
    })

    # --- 5. 图片链接 ---
    img_links = re.findall(r"!\[.*?\]\((.*?)\)", content)
    arxiv_imgs = [l for l in img_links if "arxiv.org" in l]
    base64_imgs = [l for l in img_links if l.startswith("data:image")]
    imageid_imgs = [l for l in img_links if not l.startswith("http") and not l.startswith("data:") and len(l) > 5]
    local_imgs = [l for l in img_links if l.startswith("./") or l.startswith("../") or l.startswith("/")]

    results.append({
        "check": "图片链接",
        "status": "WARN" if local_imgs else "PASS",
        "detail": (
            f"ArXiv 外链: {len(arxiv_imgs)}, Base64: {len(base64_imgs)}, "
            f"Image ID: {len(imageid_imgs)}, 本地路径: {len(local_imgs)}"
            + (" — 本地路径在 IMA 上传后会失效!" if local_imgs else "")
        ),
    })

    # --- 6. 首行元信息 ---
    first_lines = "\n".join(lines[:20])
    has_title = bool(re.search(r"(论文|Paper|标题|Title)", first_lines, re.IGNORECASE))
    has_link = bool(re.search(r"arxiv\.org", first_lines))
    has_model = bool(re.search(r"(翻译辅助|大模型|Model|Claude|GPT|Gemini|Opus)", first_lines, re.IGNORECASE))

    meta_items = []
    if not has_title:
        meta_items.append("论文标题")
    if not has_link:
        meta_items.append("ArXiv 链接")
    if not has_model:
        meta_items.append("翻译大模型名称")

    results.append({
        "check": "首行元信息",
        "status": "PASS" if not meta_items else "WARN",
        "detail": f"缺少: {', '.join(meta_items)}" if meta_items else "标题、链接、大模型名称均存在",
    })

    return results


def main():
    if len(sys.argv) < 2:
        print("用法: python3 validate_translation.py <markdown_file>")
        sys.exit(1)

    filepath = sys.argv[1]
    print(f"\n{'='*60}")
    print(f"  论文翻译校验报告")
    print(f"  文件: {filepath}")
    print(f"{'='*60}\n")

    results = validate(filepath)

    pass_count = sum(1 for r in results if r["status"] == "PASS")
    fail_count = sum(1 for r in results if r["status"] == "FAIL")
    warn_count = sum(1 for r in results if r["status"] == "WARN")

    for r in results:
        icon = {"PASS": "✅", "FAIL": "❌", "WARN": "⚠️", "INFO": "ℹ️"}[r["status"]]
        print(f"  {icon} [{r['status']}] {r['check']}: {r['detail']}")

    print(f"\n{'─'*60}")
    print(f"  结果: ✅ {pass_count} 通过 | ❌ {fail_count} 失败 | ⚠️ {warn_count} 警告")
    print(f"{'─'*60}\n")

    if fail_count > 0:
        print("  ❌ 存在 FAIL 项，请修复后重新校验！\n")
        sys.exit(1)
    elif warn_count > 0:
        print("  ⚠️ 存在 WARN 项，建议检查后再上传。\n")
    else:
        print("  ✅ 所有检查通过，可以上传！\n")


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)