From de2d9d6f02343784fd18d2ea8612bb34869f8b37 Mon Sep 17 00:00:00 2001 From: Mimikko-zeus Date: Tue, 12 May 2026 18:54:12 +0800 Subject: [PATCH] feat(blog): add comprehensive blog review and publishing workflow system - Implemented calc_audit.py script for verifying arithmetic expressions in blog calculations - Added humanizer-style-guide.md with guidelines for removing AI-sounding language from Chinese blogs - Created openai.yaml configuration for blog review publish workflow interface - Established publishing-preview-checklist.md for pre/post publication verification - Developed 12-dimension review rubric for systematic article evaluation - Built complete SKILL.md documentation covering blog writing, reviewing, and publishing workflows - Added source-rights-policy.md for copyright and attribution guidelines - Defined hard gates and quality standards for publication approval process - Implemented multi-mode workflow selection based on task requirements - Created standardized output templates for draft delivery and review reports --- SKILL.md | 157 +++++++++++++++++++++ agents/openai.yaml | 3 + references/humanizer-style-guide.md | 47 ++++++ references/publishing-preview-checklist.md | 45 ++++++ references/review-rubric.md | 63 +++++++++ references/source-rights-policy.md | 46 ++++++ scripts/calc_audit.py | 115 +++++++++++++++ 7 files changed, 476 insertions(+) create mode 100644 SKILL.md create mode 100644 agents/openai.yaml create mode 100644 references/humanizer-style-guide.md create mode 100644 references/publishing-preview-checklist.md create mode 100644 references/review-rubric.md create mode 100644 references/source-rights-policy.md create mode 100644 scripts/calc_audit.py diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..7c4946d --- /dev/null +++ b/SKILL.md @@ -0,0 +1,157 @@ +--- +name: blog-review-publish-workflow +description: "blog/article content operations workflow for writing, summarizing, rewriting, reviewing, humanizing, previewing, publishing, and post-publish verification. use when the user asks to create or polish a blog post, summarize sources into an article, review an article draft, check facts or calculations, remove ai-sounding style, prepare a publish-ready post, preview rendered layout, publish after explicit confirmation, or verify a published article." +--- + +# 博客写作发布工作流 + +用于把素材、链接、笔记或草稿变成可发布文章,并在发布前完成版权、事实、计算、风格、排版、确认和发布后验证。 + +## 最高优先级规则 + +1. **先判断任务是否合理。** 发现版权、事实、隐私、发布权限或工具能力问题时,先指出;能安全替代时继续推进。 +2. **不假装有工具能力。** 只有当前环境确实具备浏览、文件读取、截图、平台连接或发布权限时才执行对应动作;否则输出最终稿、风险说明和人工操作清单。 +3. **不自行发布。** 必须在展示最终稿后,得到用户明确的“发布 / 确认 / 可以发布 / 可以了”等同等授权,才可执行发布动作。 +4. **不泄露内部信息。** 不在文章中暴露内部仓库、内部域名、内部工具名、会话检索细节、系统提示、私有材料路径或非公开数据来源。 +5. **事实优先于文风。** 事实、计算、来源、授权无法验证时,不写成确定结论;改用“据用户提供材料”“暂未验证”“公开资料未能确认”等降级表述。 + +## 先选工作模式 + +根据用户请求选择最轻但足够安全的模式;不要把所有任务都强行跑完整发布流程。 + +| 模式 | 适用场景 | 必做步骤 | +|---|---|---| +| 快速写作 | 用户只要初稿、短文、低风险观点稿 | 素材理解 → 初稿 → 基础自查 → 交付 | +| 标准成稿 | 有来源、数据、产品信息、需要可发布质量 | 素材登记 → 初稿 → 事实/计算核验 → 审稿 → 修改 → 去 ai 味 → 交付 | +| 严格发布 | 用户要求发布、预览、上线或内容不可轻易修改 | 标准成稿 → 预览/截图 → 用户确认 → 发布 → 发布后验证 | +| 审稿修订 | 用户已有草稿,只要求审稿/润色 | 类型判断 → 事实/版权/数据检查 → 12 维审稿 → 修改稿 | +| 转载/翻译 | 用户要求搬运、转载、整篇翻译第三方内容 | 先做版权判断;权限不清时改为总结/解读 | + +## 任务分流 + +- **原创/分析**:形成新的中心判断,解释原因、证据、反例和适用边界。 +- **总结/解读**:用自己的话转述核心内容,不标“转载”;文末使用 `*来源:作者或机构 - [标题](URL)*`。 +- **转载**:只有在用户拥有版权、获得授权、原文明确允许转载或内容处于公有领域时才可整篇搬运;必须标注转载、作者、标题和原文链接。 +- **翻译/编译**:不要完整翻译受版权保护的第三方长文,除非用户提供授权或许可;优先做摘要、摘译和评论。 +- **发布/排版**:只有已有最终稿、目标平台和发布权限时才进入预览、确认、发布和验证。 + +版权和来源细则见 `references/source-rights-policy.md`。 + +## 硬性发布闸门 + +以下任一项不通过,不得进入发布;可以交付草稿或说明风险。 + +| 闸门 | 通过标准 | +|---|---| +| g1 来源与版权 | 关键来源可追溯;转载/翻译有授权或许可;总结不构成整篇复刻 | +| g2 事实准确 | 关键事实、日期、实体、因果关系已核对;未核对内容已降级或删除 | +| g3 计算准确 | 数字、价格、百分比、费用、倍率、单位换算已用 python 或等价可审计工具验算 | +| g4 隐私与内部信息 | 无内部链接、内部工具名、私有路径、敏感个人信息或不应公开内容 | +| g5 用户确认 | 最终稿已展示,用户已明确授权发布 | +| g6 渲染可接受 | 发布前预览或平台检查未发现阻塞性排版问题;无法截图时已说明限制 | + +## 标准流程 + +### step 1:素材收集与登记 + +记录来源、类型、可访问性、可引用性、关键事实和版权状态。外部信息优先使用官方文档、原始公告、论文、监管文件、公司博客、项目仓库、价格页等一手来源。无法访问来源时要求用户提供正文、截图或摘录;在此之前不要把未读内容写成事实。 + +### step 2:写初稿 + +按文章类型写作。初稿必须有中心判断或清晰信息结构,不做材料堆砌。短文默认不加 h2;长文、教程、报告型内容可使用 h2 和目录。避免“本文将”“随着技术发展”“总而言之”等模板句。 + +### step 3:事实与计算核验 + +提取草稿中的数字、价格、百分比、费用、倍率、token、时间、版本号、模型 id、api 参数、强断言和“最新/唯一/首次/最高”等表述。使用 python 或 `scripts/calc_audit.py` 对计算逐条验算;价格、模型 id、版本状态等对照官方来源。输出验算报告,记录公式、输入、结果和处理动作。 + +### step 4:12 维审稿 + +使用 `references/review-rubric.md`。将“可发布/不可发布”由硬性闸门决定,将“写得好不好”由 12 维评分决定。10 分代表最好;不要使用“ai 味”作为正向分数,改用“自然度/人味”。 + +严格发布模式下,硬性闸门必须全部通过;事实准确、计算准确、版权来源、隐私安全不得有阻塞问题;其他评分原则上不低于 8 分。 + +### step 5:按审稿修改 + +优先修复事实错误、计算错误、版权风险和内部信息泄露;其次处理结构、遗漏、观点失真;最后处理标题、语言、节奏和排版。新增事实或数字后必须回到 step 3。 + +### step 6:去 ai 味与风格统一 + +使用 `references/humanizer-style-guide.md`。重点删除模板起手、过密排比、口号化结尾、过度加粗、虚高形容词和整齐但空泛的段落。保留明确判断、具体例子、限制条件和自然节奏。 + +### step 7:预览与截图 + +严格发布模式下,若具备浏览器、文档预览或平台预览能力,打开渲染页面并截图检查标题、目录、图片、表格、代码块、引用块、链接和移动端宽表。若无法截图,明确说明限制,并提供人工预览清单。预览细则见 `references/publishing-preview-checklist.md`。 + +### step 8:用户确认 + +向用户展示最终稿和发布摘要:标题、目标平台、可见性、外链、图片/表格/代码块、未验证风险。未确认前不得发布。用户要求修改时回到 step 5;新增事实或数字时回到 step 3。 + +### step 9:发布与发布后验证 + +只有在确认、权限和工具能力均满足时发布。发布后打开公开或预览 url,检查标题、正文、图片、表格、代码块、链接、标签、作者和日期。能截图时提供截图;不能截图时说明限制和人工核对项。 + +## 输出模板 + +### 成稿交付 + +```markdown +## 最终稿 +[文章正文] + +## 验证摘要 +- 来源:已核对 / 部分未核对 / 用户提供材料 +- 版权:可总结 / 可转载 / 权限不清已改写 / 存在风险 +- 事实:通过 / 有未验证项 / 已降级处理 +- 计算:无计算 / 已验算 / 已修正 +- 发布风险:无明显阻塞 / 风险列表 +``` + +### 审稿报告 + +```markdown +## 发布闸门 +| 闸门 | 结论 | 说明 | +|---|---|---| +| 来源与版权 | pass/fail | ... | +| 事实准确 | pass/fail | ... | +| 计算准确 | pass/fail/na | ... | +| 隐私与内部信息 | pass/fail | ... | +| 用户确认 | pending/pass | ... | +| 渲染检查 | pending/pass/fail/na | ... | + +## 12 维评分 +| 维度 | 分数 | 问题 | 修改动作 | +|---|---:|---|---| +| 目标与读者匹配 | 8 | ... | ... | +``` + +### 发布前确认 + +```markdown +## 待确认发布稿 +标题:[标题] +目标平台:[平台] +可见性:[公开/私密/草稿/未知] + +[文章正文] + +确认后才会发布;未确认前不会发布。 +``` + +### 发布后验证 + +```markdown +## 发布结果 +- url:[链接] +- 状态:已发布 / 已保存草稿 / 发布失败 +- 验证:标题、正文、链接、图片、表格、代码块、标签、日期 +- 问题:无 / 问题列表与修复建议 +``` + +## 资源使用 + +- 需要判断版权、转载、来源可信度时,读取 `references/source-rights-policy.md`。 +- 需要审稿或发布前质检时,读取 `references/review-rubric.md`。 +- 需要去 ai 味、统一语气或改写文风时,读取 `references/humanizer-style-guide.md`。 +- 需要预览、截图、发布、发布后验证时,读取 `references/publishing-preview-checklist.md`。 +- 需要批量验算算式时,可运行 `scripts/calc_audit.py`。 diff --git a/agents/openai.yaml b/agents/openai.yaml new file mode 100644 index 0000000..30a8e41 --- /dev/null +++ b/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Blog Review Publish Workflow" + short_description: "End-to-end blog writing, review, humanizing, preview, publish, and verification workflow." diff --git a/references/humanizer-style-guide.md b/references/humanizer-style-guide.md new file mode 100644 index 0000000..57b6add --- /dev/null +++ b/references/humanizer-style-guide.md @@ -0,0 +1,47 @@ +# 去 ai 味与中文博客风格指南 + +## 总体目标 + +文章应该像一个清醒的旁观者或分析师写的:有判断,但不过度代入;有信息密度,但不端着;有结构,但不露模板。 + +## 必删或慎用 + +- 模板起手:本文将、在当今时代、随着技术不断发展、总而言之、综上所述。 +- 过度排比:不是……而是……、不仅……更……、一方面……另一方面……连续出现。 +- 营销空词:赋能、破局、闭环、生态、颠覆、重塑、抓手、底层逻辑、护城河、范式革命。 +- 假深刻连接:大量破折号、冒号、加粗句制造结论感。 +- 口号化结尾:未来已来、值得每个人关注、这只是开始。 + +## 保留或增加 + +- 具体判断:这件事真正值得关注的是…… +- 适用边界:这个结论只适用于…… +- 反例或限制:但这不意味着…… +- 读者视角:如果读者关心的是成本/稳定性/落地难度,重点不一样。 +- 信息节奏:长段后接短句;密集信息后接一句解释。 + +## 改写动作 + +1. 删除文章自我介绍句,直接进入事件、问题或判断。 +2. 把整齐排比拆成自然句,保留最关键的一组对照即可。 +3. 把泛泛形容词换成具体证据、场景或限制。 +4. 每 3-5 段检查一次节奏:是否需要短句、例子、反问、边界或小结。 +5. 结尾用具体信息收束:适用范围、下一步、风险提醒、未解决问题。 + +## 示例 + +差: + +> 这不仅是一次产品升级,更是对行业生态的重塑。 + +好: + +> 更实际的变化是,开发者需要重新计算调用成本和迁移风险。产品升级本身不难,难的是它会不会改变现有架构的默认选择。 + +差: + +> 总而言之,这一变化值得所有人关注。 + +好: + +> 如果只是轻量试用,影响可能不大;真正需要重新评估的是高频调用、长上下文和多模型切换的场景。 diff --git a/references/publishing-preview-checklist.md b/references/publishing-preview-checklist.md new file mode 100644 index 0000000..3698bdd --- /dev/null +++ b/references/publishing-preview-checklist.md @@ -0,0 +1,45 @@ +# 预览、发布与发布后验证清单 + +## 发布前预览 + +能使用浏览器、文档预览或平台预览时,必须实际查看渲染结果。不能截图或不能访问平台时,明确说明限制,不要声称已预览。 + +检查项: + +- 标题、摘要、封面图、作者、标签、slug、可见性。 +- h2/h3 层级、目录、段落间距、列表缩进。 +- 图片是否加载,是否需要说明文字,是否有版权风险。 +- 表格是否过宽,移动端是否挤压;必要时改为分组列表。 +- 代码块语言、缩进、换行、复制体验。 +- 引用块、脚注、链接文本是否正常。 +- 外链是否指向公开地址,不含内部域名或私有路径。 +- markdown 是否被目标平台错误解析。 + +## 用户确认 + +发布前给出: + +```markdown +标题:... +目标平台:... +可见性:公开/私密/草稿/未知 +包含:外链/图片/表格/代码块 +未验证风险:无/列表 +``` + +只有用户明确授权后才能发布。 + +## 发布动作 + +发布前再次确认目标平台、账号/空间、标题、slug、标签、可见性、发布时间和是否允许发布后编辑。如果发布后编辑受限,必须提醒用户。 + +## 发布后验证 + +发布后打开公开或预览 url,检查: + +- 标题、正文、摘要、作者、发布日期。 +- 图片、表格、代码块、引用、目录、链接。 +- 标签、分类、可见性、分享卡片。 +- 是否存在内部链接、错别字、渲染错位或缺失段落。 + +发现问题时列出影响范围和修复建议。只有在用户授权且平台允许编辑时才修复。 diff --git a/references/review-rubric.md b/references/review-rubric.md new file mode 100644 index 0000000..e2b6488 --- /dev/null +++ b/references/review-rubric.md @@ -0,0 +1,63 @@ +# 12 维审稿标准 + +## 为什么不是单纯 9 维 + +原 9 维覆盖了自然度、结构、深度、可读性、事实、完整性、价值、语气和计算,适合做文章质量检查,但有三个问题: + +1. “ai 味”“可读性”“语气一致性”有重叠,且“ai 味”作为 1-10 分方向不清。 +2. 缺少读者目标、标题分发、版权来源、隐私安全、平台排版等发布前关键项。 +3. 把硬性风险和文风评分混在一起,容易出现“文章写得不错但不该发布”的情况。 + +建议使用“硬性闸门 + 12 维评分”。闸门决定能不能发布;评分决定文章质量。 + +## 硬性闸门 + +| 闸门 | fail 条件 | +|---|---| +| 来源与版权 | 转载/翻译无授权;来源不可追溯;大段复刻第三方内容 | +| 事实准确 | 关键事实无法核实却写成确定结论;原文观点被误读 | +| 计算准确 | 价格、百分比、倍率、费用、单位换算未验算或结果错误 | +| 隐私与内部信息 | 暴露内部链接、内部工具、私有路径、敏感个人信息 | +| 用户确认 | 发布前未展示最终稿,或用户未明确授权发布 | +| 渲染检查 | 严格发布场景下存在阻塞性排版错误,或无法确认关键渲染 | + +## 12 维评分 + +每项 1-10 分,10 分最好。严格发布模式原则上每项不低于 8 分;事实、计算、版权、隐私以闸门为准。 + +| # | 维度 | 10 分标准 | 常见问题 | +|---|---|---|---| +| 1 | 目标与读者匹配 | 明确知道写给谁、解决什么问题、读者读完能得到什么 | 对象模糊;像内部备忘录;读者收益不清 | +| 2 | 中心论点与角度 | 有一句话可概括的核心判断,角度不散 | 只是罗列信息;没有判断;标题和正文重点不一致 | +| 3 | 结构与叙事推进 | 开头有钩子,中段有递进,结尾有具体收束 | 段落可随意调换;跳跃;重复;过渡模板化 | +| 4 | 信息完整性 | 重要事实、限制、背景和反例没有遗漏 | 只写亮点;遗漏限制条件;断章取义 | +| 5 | 事实忠实度 | 准确区分原文事实、作者观点和本文判断 | 误读来源;把推测写成事实;因果倒置 | +| 6 | 数据与计算完整性 | 数字来源清楚,公式可复查,单位和口径一致 | 心算;口径混乱;价格/模型/版本过期 | +| 7 | 来源、版权与引用 | 来源格式清楚,转载/翻译边界合规 | 版权不明;来源缺失;引用过长;链接不可追溯 | +| 8 | 洞察与价值增量 | 相比原文,多出解释、判断、适用边界或读者行动建议 | 纯复述;没有 why;没有取舍 | +| 9 | 可读性与节奏 | 句长有变化,信息密度有起伏,读起来不累 | 每段同样密;长句堆叠;术语无解释 | +| 10 | 自然度与人味 | 像真实作者写作,有具体判断和自然转折 | 排比堆砌;口号化;过度加粗;模板起承转合 | +| 11 | 标题、摘要与分发适配 | 标题准确、有吸引力不过度营销,适合目标平台 | 标题党;太泛;seo/平台摘要缺失 | +| 12 | 排版、可访问性与平台适配 | 表格、代码块、图片、引用、链接适合平台和移动端 | 宽表挤压;代码不可读;图片无说明;链接文本含糊 | + +## 审稿输出格式 + +```markdown +## 发布闸门 +| 闸门 | 结论 | 说明 | 必要动作 | +|---|---|---|---| +| 来源与版权 | pass/fail | ... | ... | + +## 12 维评分 +| 维度 | 分数 | 问题 | 修改动作 | +|---|---:|---|---| +| 目标与读者匹配 | 8 | ... | ... | + +## 结论 +- 可发布:0 阻塞意见。 +- 或不可发布:列出阻塞项和需要用户补充的信息。 +``` + +## 多轮规则 + +第一轮全面审稿,第二轮只检查遗留问题和新引入问题。三轮后仍存在阻塞项时,不继续空转;说明剩余风险和需要补充的材料。 diff --git a/references/source-rights-policy.md b/references/source-rights-policy.md new file mode 100644 index 0000000..c719fb4 --- /dev/null +++ b/references/source-rights-policy.md @@ -0,0 +1,46 @@ +# 来源、版权与转载策略 + +## 来源优先级 + +优先级从高到低: + +1. 官方文档、价格页、产品公告、监管文件、论文、项目仓库、公司博客。 +2. 权威媒体、行业报告、数据库、标准组织。 +3. 作者博客、访谈、播客、会议演讲。 +4. 社交媒体、论坛、二手转载、聚合页面。 + +涉及价格、模型 id、api 参数、法规、版本状态、发布日期、人物职务、公司状态等可能变化的信息,必须尽量核对一手来源。无法核对时不要写成确定结论。 + +## 总结、转载、翻译的边界 + +- **总结/解读**:重组结构、用自己的话表达、加入判断和边界;文末标来源。不要连续大段保留原文表达。 +- **转载**:保留原文主体内容。只有授权、许可、公有领域或用户拥有版权时才可做。必须标“转载”和原文链接。 +- **翻译**:完整翻译第三方长文通常等同于复制表达。权限不清时改为摘要、摘译、评论或要点解读。 +- **编译**:多个来源整合时,分别记录来源,避免把不同来源观点混成一个确定事实。 + +## 文章中的来源格式 + +总结类单一来源: + +```markdown +*来源:作者或机构 - [标题](URL)* +``` + +多来源分析: + +```markdown +## 参考资料 +- 作者或机构:[标题](URL) +- 作者或机构:[标题](URL) +``` + +## 风险处理 + +| 情况 | 处理 | +|---|---| +| 原文禁止转载 | 不转载;改成短摘要和评论 | +| 权限不明但用户要求整篇搬运 | 拒绝整篇搬运;提供总结/解读版本 | +| 无法访问链接 | 要求用户提供正文或截图;不要编造来源内容 | +| 来源互相矛盾 | 标出分歧,优先一手来源,不强行合并 | +| 用户提供内部材料 | 只提取必要信息;不暴露材料路径、内部链接、工具名 | +| 高风险领域 | 使用保守表述,避免给出未经验证的专业建议 | diff --git a/scripts/calc_audit.py b/scripts/calc_audit.py new file mode 100644 index 0000000..32f282c --- /dev/null +++ b/scripts/calc_audit.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +"""Evaluate arithmetic expressions for blog calculation audits. + +Input JSON format: +[ + { + "id": "1", + "claim": "monthly cost is 12.50", + "expression": "0.25 * 50", + "expected": 12.5, + "tolerance": 0.000001, + "note": "unit: usd/month" + } +] + +The script intentionally supports arithmetic only, not arbitrary Python code. +""" + +from __future__ import annotations + +import ast +import json +import math +import operator +import sys +from pathlib import Path +from typing import Any + +ALLOWED_BINOPS = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, + ast.FloorDiv: operator.floordiv, + ast.Mod: operator.mod, + ast.Pow: operator.pow, +} +ALLOWED_UNARYOPS = {ast.UAdd: operator.pos, ast.USub: operator.neg} + + +def eval_expr(expr: str) -> float: + node = ast.parse(expr, mode="eval") + + def walk(n: ast.AST) -> float: + if isinstance(n, ast.Expression): + return walk(n.body) + if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)): + return float(n.value) + if isinstance(n, ast.BinOp) and type(n.op) in ALLOWED_BINOPS: + return float(ALLOWED_BINOPS[type(n.op)](walk(n.left), walk(n.right))) + if isinstance(n, ast.UnaryOp) and type(n.op) in ALLOWED_UNARYOPS: + return float(ALLOWED_UNARYOPS[type(n.op)](walk(n.operand))) + raise ValueError(f"unsupported expression element: {ast.dump(n)}") + + return walk(node) + + +def fmt(value: Any) -> str: + if value is None: + return "" + if isinstance(value, float): + if math.isfinite(value): + return f"{value:.12g}" + return str(value) + + +def main() -> int: + if len(sys.argv) != 2: + print("usage: calc_audit.py calculations.json", file=sys.stderr) + return 2 + + path = Path(sys.argv[1]) + items = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(items, list): + print("input must be a json list", file=sys.stderr) + return 2 + + rows = [] + for item in items: + if not isinstance(item, dict): + raise ValueError("each item must be an object") + result = eval_expr(str(item["expression"])) + expected = item.get("expected") + tolerance = float(item.get("tolerance", 1e-9)) + if expected is None: + status = "computed" + delta = None + else: + expected_float = float(expected) + delta = result - expected_float + status = "pass" if abs(delta) <= tolerance else "fail" + rows.append( + [ + str(item.get("id", "")), + str(item.get("claim", "")), + str(item.get("expression", "")), + fmt(result), + fmt(expected), + fmt(delta), + status, + str(item.get("note", "")), + ] + ) + + headers = ["id", "claim", "expression", "result", "expected", "delta", "status", "note"] + print("| " + " | ".join(headers) + " |") + print("|" + "|".join(["---"] * len(headers)) + "|") + for row in rows: + safe = [cell.replace("|", "\\|").replace("\n", " ") for cell in row] + print("| " + " | ".join(safe) + " |") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())