From 007886976c72f3939f494f66cb45f1f60db61578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B4=AA=E6=B3=BD=E9=91=AB?= Date: Mon, 24 Aug 2026 04:23:04 +0800 Subject: [PATCH 1/2] Update CHEK AI product enrichment skills Add explicit DEV library enrichment, duplicate and cover gates, and stricter capability leaderboard admission rules. Source sidechat: 01a01dc5-afae-7811-953a-5340b8500dfd --- README.md | 4 +- skills/chek-ai-product-sourcing/SKILL.md | 14 ++- .../agents/openai.yaml | 4 +- .../references/dev-library-enrichment.md | 94 +++++++++++++++++++ skills/chek-prod-ai-product-ops/SKILL.md | 3 + .../references/prod-cli-runbook.md | 4 + .../references/robot-database-maintenance.md | 36 +++++++ .../vehicle-database-maintenance.md | 12 +++ 8 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 skills/chek-ai-product-sourcing/references/dev-library-enrichment.md diff --git a/README.md b/README.md index 8767c58..3fe28a0 100644 --- a/README.md +++ b/README.md @@ -531,7 +531,7 @@ chek auth profile import dev-agent --file ./dev-agent.profile.json --activate ## 内置 Skills - [`skills/chek-setup/SKILL.md`](./skills/chek-setup/SKILL.md):帮助 OpenClaw 完成 CHEK CLI setup、浏览器授权、token 兜底和健康检查。 -- [`skills/chek-ai-product-sourcing/SKILL.md`](./skills/chek-ai-product-sourcing/SKILL.md):帮助 Agent 搜索、验证、分类、去重和整理 CHEK AI 产品候选,也能把用户录音、速记、截图和体验材料整理成评审房间内容。它只写本地文件或用户本轮指定的飞书/Lark 候选库,不硬编码默认候选库。 +- [`skills/chek-ai-product-sourcing/SKILL.md`](./skills/chek-ai-product-sourcing/SKILL.md):帮助 Agent 搜索、验证、分类、去重和整理 CHEK AI 产品候选,也能把用户录音、速记、截图和体验材料整理成评审房间内容;用户明确指定 DEV 并授权执行时,还可按受控流程补充机器人、汽车、模型/方法、版本、封面和能力评测资料。它不硬编码默认候选库,也不会把候选整理静默升级成数据库写入。 - [`skills/chek-prod-ai-product-ops/SKILL.md`](./skills/chek-prod-ai-product-ops/SKILL.md):帮助 Agent 在生产环境执行正式 AI 产品评审房间提报、封面溯源、车型/机器人绑定、版本编辑提交、评测证据发布,以及长期智能汽车/机器人数据库维护;这个 skill 明确禁止 DEV/staging 操作。 ## 前端和证据辅助 @@ -611,7 +611,7 @@ CI 也会运行 `scripts/check_registry_drift.py --allow-missing-optional`。如 - `src/service.ts`:后台轮询、浏览器授权同步、mention task 处理、房间回复编排。 - `src/render.ts`:房间上下文压缩、intent 识别、直接回复和本地 prompt 构造。 - `skills/chek-setup/SKILL.md`:随仓库发布的 setup 和社区共建入口 skill。 -- `skills/chek-ai-product-sourcing/SKILL.md`:随仓库发布的 AI 产品候选 sourcing 和评测素材整理 skill。 +- `skills/chek-ai-product-sourcing/SKILL.md`:随仓库发布的 AI 产品候选 sourcing、评测素材整理和显式授权 DEV 资料库增补 skill。 - `skills/chek-prod-ai-product-ops/SKILL.md`:随仓库发布的 prod-only AI 产品提报、评测证据发布和车型/机器人库维护 skill。 - `docs/bootstrap-message.md`:面向用户的一段式引导文案。 - `docs/device-code-auth.md`:浏览器授权链路和 fallback 规则。 diff --git a/skills/chek-ai-product-sourcing/SKILL.md b/skills/chek-ai-product-sourcing/SKILL.md index cfd54ba..dfd6a7d 100644 --- a/skills/chek-ai-product-sourcing/SKILL.md +++ b/skills/chek-ai-product-sourcing/SKILL.md @@ -1,6 +1,6 @@ --- name: chek-ai-product-sourcing -description: Source, verify, classify, and package CHEK AI product candidates for either local output or a user-specified Feishu/Lark candidate base, including optional Zhihu Developer on-site search evidence and user review-material processing. Use when the user asks to search for AI products, fill or update a CHEK candidate pool, apply monthly or quarterly release windows, assess domestic availability/borrowability, prepare fields for AI product submission, check duplicate product candidates, use developer.zhihu.com/Zhihu site search, turn recordings/transcripts/notes into user-friendly AI product reviews, or decide which candidates/reviews should be submitted later through the CHEK CLI. +description: Source, verify, classify, and package CHEK AI product candidates for local output, a user-specified Feishu/Lark candidate base, or an explicitly authorized CHEK DEV robot/vehicle/model enrichment run. Includes optional Zhihu Developer evidence and user review-material processing. Use when the user asks to search for AI products, maintain a candidate pool, enrich CHEK DEV robot/vehicle/model entries and versions, fill covers, deduplicate candidates, apply release windows, prepare formal submission fields, process review materials, or decide which candidates/reviews should be submitted later through the CHEK CLI. --- # CHEK AI Product Sourcing @@ -13,6 +13,8 @@ Read [references/candidate-base.md](references/candidate-base.md) before writing Read [references/zhihu-developer-search.md](references/zhihu-developer-search.md) before using `developer.zhihu.com`, the Zhihu search API, or Zhihu on-site search results as evidence. +Read [references/dev-library-enrichment.md](references/dev-library-enrichment.md) before reading or mutating the CHEK DEV robot, vehicle, benchmark-method, version, cover, or capability-evaluation library. + ## Operating Rules - Browse the web for current product facts, release dates, versions, prices, official pages, App Store listings, and availability. Prefer official product pages, App Store pages, manufacturer pages, store pages, and reputable media. @@ -24,6 +26,8 @@ Read [references/zhihu-developer-search.md](references/zhihu-developer-search.md - Do not publish user review material to a CHEK room unless the user explicitly confirms the target room or exact product tuple and authorizes posting. - Keep the Base simple. Do not add columns unless the user explicitly asks. - Deduplicate before writing. Check existing product names and, for final submission, also check product name + hardware model + software version. +- Treat a new main entity, a new hardware/config version, and an update to an existing entity as three different actions. Do not create a new main entity when the evidence only describes a version, SDK, delivery milestone, or configuration change. +- A newly created DEV robot, vehicle, or benchmark-method entry is incomplete until it has a verified CHEK-hosted cover or an explicit blocked reason. Keep the original cover source URL in the audit trail. - Be honest about domestic access. Do not say a product can be borrowed or tested unless a source supports that. Use `待渠道确认` or `待实测材料` when access is plausible but unproven. - For pure software products, hardware model may be empty, but software version must be specific. For hardware, cars, robots, and glasses, capture both hardware model and software/firmware/app/vehicle version whenever possible. - For user recordings, transcripts, or rough notes, preserve the user's actual experience while removing private information, license plates, phone numbers, exact addresses, account identifiers, and unrelated personal details. @@ -68,6 +72,12 @@ Read [references/zhihu-developer-search.md](references/zhihu-developer-search.md - For Feishu output, use `+data-query` for counts by `状态`, `类别`, and `统计时间窗口口径`. - State explicitly that CLI product submission has not been performed unless it actually has. +## Explicit DEV Library Enrichment + +Candidate sourcing and DEV library enrichment are separate modes. Enter DEV library enrichment only when the user explicitly names DEV and asks to create, update, enrich, approve, or backfill library records. Follow [references/dev-library-enrichment.md](references/dev-library-enrichment.md) for environment checks, duplicate resolution, entity/version decisions, cover upload, governed edits, capability-benchmark admission, and readback. + +Do not silently turn a candidate-pool request into a DEV mutation. Do not use this DEV mode for production. + ## Review Material To Room Workflow Use this workflow when the user wants Agent help turning a product test, voice memo, transcript, shorthand notes, screenshots, videos, or links into a CHEK review-room contribution. @@ -144,7 +154,7 @@ Use this checklist when the user asks whether anything is missing: - AI 健康应用: search independent apps plus platform entrances and mini-programs: 百度健康, 支付宝健康, 微信生态, AI+真人, 家庭医生, 报告解读, 症状自查, 免责声明. - 其他消费级 AI 产品: search AI glasses, AI recorder cards, AI recorder pens, wearable assistants, cameras, creator tools, video/image apps, phone-vendor ecosystem hardware. -## Final Submission Boundary +## Production Formal Submission Boundary Candidate sourcing and formal product submission are separate phases. diff --git a/skills/chek-ai-product-sourcing/agents/openai.yaml b/skills/chek-ai-product-sourcing/agents/openai.yaml index 0a5e946..a9ca6fc 100644 --- a/skills/chek-ai-product-sourcing/agents/openai.yaml +++ b/skills/chek-ai-product-sourcing/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "CHEK AI Product Sourcing" - short_description: "维护 AI 产品候选、知乎证据、评测素材整理与本地/飞书输出" - default_prompt: "Use $chek-ai-product-sourcing to source AI product candidates, enrich them with official and Zhihu Developer evidence, process user recordings/transcripts into review-room drafts, and mark P0/P1 evidence-collection tasks in local or user-specified Feishu output." + short_description: "维护 AI 产品候选,并按授权丰富 CHEK DEV 机器人、汽车和模型资料库" + default_prompt: "Use $chek-ai-product-sourcing to source and deduplicate AI product candidates, enrich them with official evidence and verified covers, and, only when explicitly authorized for DEV, create or update CHEK robot, vehicle, model/method, version, and capability-evaluation records with governed readback." diff --git a/skills/chek-ai-product-sourcing/references/dev-library-enrichment.md b/skills/chek-ai-product-sourcing/references/dev-library-enrichment.md new file mode 100644 index 0000000..0b09af0 --- /dev/null +++ b/skills/chek-ai-product-sourcing/references/dev-library-enrichment.md @@ -0,0 +1,94 @@ +# CHEK DEV Library Enrichment + +## Scope And Environment + +Use this workflow only when the user explicitly asks to enrich the CHEK DEV robot, vehicle, model/method, version, cover, or capability-evaluation database. + +Before any read or write: + +```bash +chek --json config show +chek auth status --check +``` + +Require `env=dev` and `api_origin=https://api-dev.chekkk.com`. Stop if the target is production or ambiguous. Never print access tokens, cookies, Authorization headers, or used SMS codes. + +## Evidence Package + +For every proposed create or update, capture: + +- canonical name, brand/owner, category, entry type, and aliases; +- event and date, with the change classified as main entity, hardware/config version, SDK/model/firmware version, delivery/production fact, or onsite showcase; +- exact source-backed facts and precise missing evidence; +- official product, release, paper, repository, or conference URLs; +- domestic access status and confidence; +- official cover page/image candidates; +- suggested action: `create`, `update`, `create_version`, `link_method`, `pending`, or `skip`. + +Use discovery sites only to find leads. Resolve final fields and cover provenance to official pages, papers, conference organizers, regulatory sources, or reputable original reporting. Do not expose an intermediary discovery site as the factual source when primary evidence is available. + +## Mandatory Duplicate Resolution + +Before creating anything, query DEV by canonical name, normalized punctuation/case, brand, model code, and every known alias. Inspect the detail and version list of plausible matches. + +Use this decision order: + +1. Same real product and same generation: update the existing main entity. +2. Same product family but a distinct SKU, hardware generation, or experimental configuration: create or update a version/variant under the existing main entity when the schema supports it. +3. Same string but a different owner, embodiment, or method: keep separate entities and record disambiguating aliases/owner. +4. SDK, firmware, foundation model, delivery milestone, or showcase only: update the related entity/version/fact; do not create a physical robot. +5. Benchmark model/method with independent evaluation identity: use `entryType=benchmark_method`. It may appear as a robot-like leaderboard subject in the product UI, but must not be mislabeled as physical hardware. + +Record the matched entity ID for every update and the negative search evidence for every create. Re-run the duplicate lookup immediately before submission. + +## Main Entry And Version Completeness + +For a physical robot or vehicle, separate stable identity from versioned configuration: + +- Main entry: canonical name, aliases, brand, category, description, market status, official URL, cover. +- Version/config: hardware SKU, dimensions/specs, controller/compute, firmware, SDK, model version, release date, source snapshot. +- Market changes: production, delivery, preorder, or sales facts with exact scope; never assign company-wide totals to one model without evidence. +- Onsite appearance: store as an event/evidence fact unless the event is the actual first public release. + +Enrich an existing record when new evidence fills previously empty parameters. Do not limit a run to creating missing entries. + +## Cover Completion Gate + +Every newly created main entry must finish with a verified cover. Existing entries with external-only or dead covers should be migrated when they are in scope. + +1. Prefer an official product hero, official announcement image, official conference exhibit image, paper/project visual for a benchmark method, or reputable original reporting when no official usable image exists. +2. Visually confirm the exact model. Reject wrong-generation images, generic brand photos, group shots where the subject is unclear, unrelated robots, logos alone, watermarks, and synthetic replacements for a real product. +3. Upload the local image with `chek media +upload-cover`, preserving the original source URL and a readable asset title. +4. Store the returned `https://img.chekkk.com/...` URL through a governed edit. Keep the original source URL in the media metadata and edit description/evidence. +5. Approve only when the user authorized approval. Read back the entity and verify the saved cover uses the CHEK media domain. +6. Poll the media URL until it returns HTTP 200. Object storage may briefly return 404 after a successful upload; do not declare failure or success from the first request alone. If it never becomes available, re-upload and replace the edit. + +## Capability Evaluation Admission + +Do not put every discovered standard into the public capability leaderboard. Keep three states: candidate registry, governed definition, and formally published leaderboard. + +A definition may enter the governed library when it has a public protocol, stable version, task/dataset description, metric and ranking direction, environment/embodiment scope, aggregation rule, required trials, and evidence requirements. + +A definition may enter the formal capability leaderboard only when all of these gates pass: + +- at least 10 distinct comparable subjects from at least 5 independent organizations under the same protocol partition; +- every ranked result identifies the exact method/robot configuration, protocol version, environment, metric, and source; +- trial count or the protocol's required sample basis is present; do not infer counts from percentages; +- one common primary metric with an explicit ascending/descending direction; no cross-project total score; +- reproducible official, paper, or independently verifiable evidence, with fixtures and illustrative demos excluded; +- no mixture of simulation and real-world results, seen and unseen settings, or materially different hardware scopes in one rank partition. + +Definitions below this threshold remain visible only in the evaluation library or candidate queue. Paper-specific real-robot protocols may be stored and shown on entity details as evidence without becoming a public cross-subject leaderboard. + +## Governed Write And Readback + +Prefer edit submissions over direct writes. Use CLI schema/route discovery and `--dry-run` before unfamiliar mutations. For each batch: + +1. submit the smallest entity/version/fact/cover diff; +2. record submission IDs and status; +3. approve only with explicit authority and after reading before/after snapshots; +4. read back entity detail and versions; +5. verify source URLs, cover HTTP status, entry type, identity, and changed fields; +6. re-run duplicate search and report zero unintended duplicates. + +Final reporting must distinguish created main entries, created versions, enriched existing entries, pending evidence, skipped duplicates, covers completed, approvals performed, and failed readbacks. diff --git a/skills/chek-prod-ai-product-ops/SKILL.md b/skills/chek-prod-ai-product-ops/SKILL.md index 640140f..f79e2d0 100644 --- a/skills/chek-prod-ai-product-ops/SKILL.md +++ b/skills/chek-prod-ai-product-ops/SKILL.md @@ -57,6 +57,8 @@ Read [references/vehicle-database-maintenance.md](references/vehicle-database-ma - Maintain vehicle profile, model/trim identity, hardware/software version lists, raw parameters, intelligent-driving capability facts, and evidence quality. - Prefer edit submissions over direct writes unless the CLI command is explicitly a governed admin action. - Keep leaderboard support explainable: sales or delivery facts for sales ranking, community rooms for heat ranking, open-source resources plus room activity for open-source ranking, and source-backed vehicle metrics for car rankings. + - Treat duplicate resolution and cover completion as release gates for a new main entity. Preserve the original cover source and verify the CHEK-hosted asset after writeback. + - Do not publish every discovered evaluation standard as a capability leaderboard. Apply the formal admission gates in the robot maintenance reference; keep sub-threshold standards in the evaluation library or evidence queue. 7. **User material extraction** - Accept user-supplied PDFs, screenshots, photos, spreadsheets, release notes, spec sheets, test notes, transcripts, or links. @@ -115,6 +117,7 @@ Avoid titles like `资料整理`, `榜单支撑`, `开源材料`, `2026-xx-xx - Use official pages, product pages, release notes, app stores, manufacturer media, store pages, GitHub repos, papers, trusted media, and first-hand test evidence. - Use Zhihu Developer search as Chinese discussion/evaluation evidence, not as the sole source for release date, version, availability, or sales facts. - For cover images, record both the CHEK media URL and the original web source URL. +- Reject a cover if it shows the wrong model/generation, an unclear group scene, an unrelated robot/vehicle, a logo-only placeholder, or a method-to-hardware mismatch. Poll the CHEK media URL to HTTP 200 before closing the operation. - For sales facts, record month, units, confidence, source title, source URL, and whether the source is official, channel estimate, or media/reporting. - For open-source resources, record resource type, platform, URL, repo name if applicable, stars/forks if available, version linkage, confidence, and source date. diff --git a/skills/chek-prod-ai-product-ops/references/prod-cli-runbook.md b/skills/chek-prod-ai-product-ops/references/prod-cli-runbook.md index b757433..d76d86f 100644 --- a/skills/chek-prod-ai-product-ops/references/prod-cli-runbook.md +++ b/skills/chek-prod-ai-product-ops/references/prod-cli-runbook.md @@ -86,6 +86,10 @@ Upload to CHEK prod media. If the generated `backend-app media images` command c - local downloaded file path until upload verification; - CHEK media URL returned by prod. +After upload, poll the returned URL until it serves HTTP 200. Object storage/CDN propagation may briefly return 404. Do not approve or publish a record with a URL that has not passed readback, and do not mistake one early 404 for a permanent failure. + +For robot, vehicle, and benchmark-method covers, visually verify the exact identity. A paper/project figure is appropriate for a method; it must not be presented as a physical robot cover. Reject wrong generations, ambiguous group shots, logo-only placeholders, and unrelated product imagery. + Do not use DEV media URLs for prod room covers. ## Robot And Vehicle Version Sync diff --git a/skills/chek-prod-ai-product-ops/references/robot-database-maintenance.md b/skills/chek-prod-ai-product-ops/references/robot-database-maintenance.md index 5321220..a26baa1 100644 --- a/skills/chek-prod-ai-product-ops/references/robot-database-maintenance.md +++ b/skills/chek-prod-ai-product-ops/references/robot-database-maintenance.md @@ -67,6 +67,40 @@ Create a config version when a review room targets a new real hardware/software Do not overwrite an old version when the product has materially changed. Add a new version or a dated re-review package. +## Duplicate And Entity-Type Gate + +Search canonical name, owner/brand, normalized model code, and aliases before a create and again immediately before approval. Read plausible matches and their version lists. + +- Update the existing main entity for the same real product and generation. +- Use a config version/variant for a distinct SKU, generation, SDK, firmware, or experimental hardware scope when the schema supports it. +- Keep same-name products from different owners separate. +- Store an independently evaluated model/method as `benchmark_method`, not as physical hardware. It may be presented as a leaderboard subject but must retain its method identity. +- Do not create a new physical robot for a delivery milestone, showcase, SDK, foundation-model release, or component unless it is independently modeled by the schema. + +## Cover Completion Gate + +A new main entry is not complete without a verified cover or an explicit blocked reason. + +- Prefer official product heroes, official release images, official conference exhibit images, or reputable original reporting. For `benchmark_method`, use the paper/project visual rather than an unrelated robot photo. +- Visually verify the exact model and generation. Reject ambiguous group shots, wrong variants, logo-only placeholders, watermarks, and generated substitutes for real products. +- Upload through CHEK media, preserve the original source URL, and store the returned CHEK media URL through governed edits. +- Read back the robot and poll the media URL until it returns HTTP 200. A successful upload response alone is not completion because object storage may propagate asynchronously. + +## Capability Leaderboard Admission + +Do not expose every stored evaluation definition as a formal capability leaderboard. Maintain candidate, governed-definition, and formal-published states. + +Formal publication requires all of the following: + +- at least 10 distinct comparable subjects from at least 5 independent organizations in the same protocol partition; +- a public stable protocol/version, task or dataset definition, environment and embodiment scope, aggregation rule, required trials, and evidence requirements; +- exact subject/config identity, metric, unit, ranking direction, source, and trial/sample basis for every ranked result; +- a common primary metric and no cross-benchmark aggregate score; +- independently checkable official or paper evidence; fixtures and illustrative demos are excluded; +- separate partitions for simulation versus real world, seen versus unseen, and materially different hardware scopes. + +Sub-threshold or paper-specific real-robot evaluations may remain in the evaluation library and appear as evidence on an entity detail page, but must not be promoted as a public cross-subject rank. Never infer trial counts from percentages. + ## Sales Facts Sales leaderboard support should come from `salesFacts` and `leaderboardMetrics.sales`, not from vague popularity claims. @@ -143,6 +177,7 @@ Before submitting an edit: - Confirm the fact belongs to the robot and version, not just the brand. - Use source-backed uncertainty instead of overclaiming. - Keep notes concise and audit-friendly. +- Confirm a new entry has a verified cover plan and no unresolved duplicate match. Before approving an edit: @@ -150,6 +185,7 @@ Before approving an edit: - Confirm no product/version identity mismatch. - Confirm no DEV room/media/source URL is referenced. - Confirm the submitted evidence is production-safe and user-readable. +- Confirm the saved cover uses a production CHEK media URL, the original source is retained, and the asset returns HTTP 200. ## Reporting Format diff --git a/skills/chek-prod-ai-product-ops/references/vehicle-database-maintenance.md b/skills/chek-prod-ai-product-ops/references/vehicle-database-maintenance.md index abeef2a..98b8f23 100644 --- a/skills/chek-prod-ai-product-ops/references/vehicle-database-maintenance.md +++ b/skills/chek-prod-ai-product-ops/references/vehicle-database-maintenance.md @@ -68,6 +68,17 @@ Create or update a version record when a room or ranking refers to a new real ha If the main vehicle entity is missing, do not publish an unbound formal AI product room. Create a missing-main-entry task with source evidence and ask for the governed creation path. +## Duplicate And Cover Gate + +Before creating a vehicle main entry, search canonical model, brand/series, model year, trim, aliases, and known supplier naming. A new trim, model year, intelligent-driving package, OTA, SDK, or delivery event normally updates the existing vehicle/version structure rather than creating another main entry. + +Every new main entry must have a verified cover or an explicit blocked reason: + +- prefer the exact official model/trim hero or official launch image; +- reject wrong model years, concept cars presented as production cars, group scenes with unclear subject, logos, watermarks, and unrelated supplier imagery; +- upload to CHEK media, preserve the original cover source, read back the vehicle, and poll the asset to HTTP 200 before closing; +- migrate in-scope external-only or dead covers to CHEK-hosted assets. + ## Evidence Standards Use official and high-confidence sources first: @@ -109,6 +120,7 @@ Before submitting an edit: - Confirm the source is current and reachable. - Preserve uncertainty with confidence notes instead of guessing. - Redact personal data, license plates, VINs, phone numbers, exact home/work locations, and private account information from user materials. +- Confirm the exact vehicle/trim has no unresolved duplicate and has a verified cover plan when creating a main entry. Before approving an edit: From f3425e9a0672ef32d0044ede06693367f1313b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B4=AA=E6=B3=BD=E9=91=AB?= Date: Thu, 27 Aug 2026 16:10:39 +0800 Subject: [PATCH 2/2] Add formal Chinese PRD skill Package the user-provided formal-chinese-prd skill with its references, template, checker, and agent metadata, and document the repository entry point.\n\nSource sidechat: 01a033a4-0e1c-7ba0-8ad0-65fb24652401\nRecovered from: 0078869 --- README.md | 2 + skills/formal-chinese-prd/SKILL.md | 85 ++++ skills/formal-chinese-prd/agents/openai.yaml | 4 + .../formal-chinese-prd/assets/prd-template.md | 174 +++++++ .../references/document-structure.md | 169 +++++++ .../references/formal-prd-style.md | 165 +++++++ .../references/review-checklist.md | 84 ++++ .../references/visual-and-layout.md | 120 +++++ .../formal-chinese-prd/scripts/check_prd.py | 447 ++++++++++++++++++ 9 files changed, 1250 insertions(+) create mode 100644 skills/formal-chinese-prd/SKILL.md create mode 100644 skills/formal-chinese-prd/agents/openai.yaml create mode 100644 skills/formal-chinese-prd/assets/prd-template.md create mode 100644 skills/formal-chinese-prd/references/document-structure.md create mode 100644 skills/formal-chinese-prd/references/formal-prd-style.md create mode 100644 skills/formal-chinese-prd/references/review-checklist.md create mode 100644 skills/formal-chinese-prd/references/visual-and-layout.md create mode 100755 skills/formal-chinese-prd/scripts/check_prd.py diff --git a/README.md b/README.md index 3fe28a0..0e01428 100644 --- a/README.md +++ b/README.md @@ -531,6 +531,7 @@ chek auth profile import dev-agent --file ./dev-agent.profile.json --activate ## 内置 Skills - [`skills/chek-setup/SKILL.md`](./skills/chek-setup/SKILL.md):帮助 OpenClaw 完成 CHEK CLI setup、浏览器授权、token 兜底和健康检查。 +- [`skills/formal-chinese-prd/SKILL.md`](./skills/formal-chinese-prd/SKILL.md):将零散材料、现有文档或实现证据整理为正式、可评审、可验收的中文产品需求说明书,并提供结构、视觉布局与自动检查规范。 - [`skills/chek-ai-product-sourcing/SKILL.md`](./skills/chek-ai-product-sourcing/SKILL.md):帮助 Agent 搜索、验证、分类、去重和整理 CHEK AI 产品候选,也能把用户录音、速记、截图和体验材料整理成评审房间内容;用户明确指定 DEV 并授权执行时,还可按受控流程补充机器人、汽车、模型/方法、版本、封面和能力评测资料。它不硬编码默认候选库,也不会把候选整理静默升级成数据库写入。 - [`skills/chek-prod-ai-product-ops/SKILL.md`](./skills/chek-prod-ai-product-ops/SKILL.md):帮助 Agent 在生产环境执行正式 AI 产品评审房间提报、封面溯源、车型/机器人绑定、版本编辑提交、评测证据发布,以及长期智能汽车/机器人数据库维护;这个 skill 明确禁止 DEV/staging 操作。 @@ -611,6 +612,7 @@ CI 也会运行 `scripts/check_registry_drift.py --allow-missing-optional`。如 - `src/service.ts`:后台轮询、浏览器授权同步、mention task 处理、房间回复编排。 - `src/render.ts`:房间上下文压缩、intent 识别、直接回复和本地 prompt 构造。 - `skills/chek-setup/SKILL.md`:随仓库发布的 setup 和社区共建入口 skill。 +- `skills/formal-chinese-prd/SKILL.md`:随仓库发布的正式中文产品需求文档整理、评审与验收规范。 - `skills/chek-ai-product-sourcing/SKILL.md`:随仓库发布的 AI 产品候选 sourcing、评测素材整理和显式授权 DEV 资料库增补 skill。 - `skills/chek-prod-ai-product-ops/SKILL.md`:随仓库发布的 prod-only AI 产品提报、评测证据发布和车型/机器人库维护 skill。 - `docs/bootstrap-message.md`:面向用户的一段式引导文案。 diff --git a/skills/formal-chinese-prd/SKILL.md b/skills/formal-chinese-prd/SKILL.md new file mode 100644 index 0000000..a86dcb5 --- /dev/null +++ b/skills/formal-chinese-prd/SKILL.md @@ -0,0 +1,85 @@ +--- +name: formal-chinese-prd +description: Create or thoroughly rewrite Chinese product requirement documents for formal review and reporting. Use when source material is verbose, AI-sounding, implementation-centric, or needs a clear product structure, testable requirements, business diagrams, interface prototypes, and acceptance criteria. Do not use for technical design specifications or repository documentation unless the user explicitly asks to translate them into product behavior. +--- + +# 正式中文产品需求文档 + +将零散材料、现有文档或实现证据整理为可供产品、业务、设计、测试和管理人员直接评审的中文产品需求说明书。 + +## 交付标准 + +- 先说明产品对象、业务边界、流程和验收依据,再展开功能细节。 +- 使用正式、简洁、可核验的中文;删除宣传语、过渡废话、写作过程说明和模板化 AI 表达。 +- 只写用户可感知的产品行为、业务规则、状态、责任和验收结果。 +- 不在产品文档中暴露代码仓库、文件路径、技术栈、内部接口、数据库结构或当前实现方式。 +- 区分已确认事实、目标需求和待决策事项。缺少依据时不得补造精确阈值或产品承诺。 +- 功能需求以“编号|优先级|需求|验收要点”表达,一条需求只承载一个主要能力。 +- 图表必须承担明确的信息任务,并与正文中的范围、状态和流程保持一致。 + +## 工作流程 + +1. 阅读用户提供的文档、材料和必要证据,建立术语、角色、业务对象、流程、范围和约束清单。 +2. 将实现证据转译为产品行为。仓库或代码可以用于核实事实,不得直接进入正文。 +3. 标记信息性质: + - 已确认事实:直接陈述; + - 目标需求:使用“应、须、必须、不得、支持、可以”; + - 待决策事项:单独列出,说明影响和需要确认的主体。 +4. 选择合适的章节,不机械保留空章节。材料较少时至少覆盖产品概述、目标与范围、角色、核心流程、功能需求、必要的数据或状态规则、验收和待决策事项;证据不足的章节直接省略。完整文档优先采用 [document-structure.md](references/document-structure.md);新建文档可复制 [prd-template.md](assets/prd-template.md) 后填充。 +5. 按 [formal-prd-style.md](references/formal-prd-style.md) 重写。段首先给结论,同一术语全文保持一致。 +6. 编写需求表。需求列描述产品行为,验收列描述可观察结果、阻断条件、输出或规范依据,禁止互相复述。 +7. 需要图示时按 [visual-and-layout.md](references/visual-and-layout.md) 制作产品全景图、PlantUML 核心流程图和必要的界面原型。 +8. 交付前运行自动检查,并按 [review-checklist.md](references/review-checklist.md) 完成人工复核。 + +## 表达约束 + +避免下列写法: + +- “一句话说明”“一句话概述”等不正式标题; +- “不是……而是……”“不等于……”“而非……”等模板化对比句; +- “下面将介绍”“综上所述”“值得注意的是”等写作过程或过渡套话; +- “一站式、赋能、打通、闭环、抓手、领先、强大、无缝”等宣传词和项目黑话; +- “根据仓库分析”“当前代码已经实现”等面向作者或研发过程的说明。 + +将对比句改为直接定义,将抽象价值改为可观察结果。例如: + +- 将“打通全链路形成高效闭环”改为“每个样本的状态、责任人和交付凭证均可查询”。 +- 将“系统支持失败重试”改为“网络恢复后继续未完成的上传;平台已确认接收的内容不得重复提交”。 +- 将“后台采用任务队列执行处理”改为“处理任务在统一列表中管理,并显示等待、运行、暂停、失败和完成状态”。 + +## 需求强度 + +| 用词 | 使用场景 | +| --- | --- | +| 必须、须、不得、仅在……后方可 | 安全、权限、质量门槛、数据一致性和交付准入 | +| 应 | 正常情况下必须满足、但可能由适用范围限定的产品行为 | +| 支持、可以、允许 | 用户能力或可配置能力,必须补充适用条件和验收结果 | +| 建议、可选 | 非承诺性指导,不得混入 P0 验收条件 | + +明确区分“处理完成、自动检查通过、审批通过、已冻结、已上传、平台验收通过、已完成”。状态不得合并,也不得只凭页面文案判定完成。 + +## 图表与原型 + +- 产品全景图回答产品覆盖范围、主要阶段、产品形态和核心价值。 +- 核心业务流程默认使用 PlantUML,保留 `.puml` 源文件并转换为同名 `.png` 后插入文档。 +- 流程图必须显示正常路径、判断条件、失败回退、重新审批、版本冻结和最终凭证;回退箭头落到实际需要重做的步骤。 +- 界面原型用于说明信息层级和关键交互,不得宣称为现有界面或已经交付的能力。 +- 界面采用浅色、克制、信息优先的工业软件风格。参考 CVAT 时只借鉴媒体工作台的信息结构,不复制品牌、截图、CSS、图标组合或产品术语。 +- 文档包含本地处理等独立工作区时,应为其提供对应原型,避免只描述而无界面证据。 +- 图中导航高亮、任务状态、进度、按钮和检查结论必须一致;进行中的检查不得同时显示最终通过报告。 + +## 自动检查 + +在技能目录下运行: + +```bash +python scripts/check_prd.py /absolute/or/relative/path/to/document.md +``` + +`ERROR` 必须修正;`WARN` 必须人工确认。正式交付前使用 `--strict` 将警告也作为失败处理: + +```bash +python scripts/check_prd.py document.md --strict +``` + +脚本只负责可确定的结构、链接和表达检查,不能替代事实核验、范围决策、流程判断或视觉审查。 diff --git a/skills/formal-chinese-prd/agents/openai.yaml b/skills/formal-chinese-prd/agents/openai.yaml new file mode 100644 index 0000000..a81486d --- /dev/null +++ b/skills/formal-chinese-prd/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "正式中文产品需求文档" + short_description: "编写和重构适合正式评审的中文产品需求说明书,并完成图表与质量校验" + default_prompt: "使用 $formal-chinese-prd 将这份材料整理为简洁、正式、可评审的中文产品需求说明书。" diff --git a/skills/formal-chinese-prd/assets/prd-template.md b/skills/formal-chinese-prd/assets/prd-template.md new file mode 100644 index 0000000..ef2d5dd --- /dev/null +++ b/skills/formal-chinese-prd/assets/prd-template.md @@ -0,0 +1,174 @@ +# {{产品名称}}产品需求说明书 + +> 文档版本:{{版本}} +> 文档状态:{{状态}} +> 更新日期:{{日期}} +> 适用范围:{{产品形态与业务范围}} + +{{用一段话说明文档目的及不包含的管理内容}} + +## 1. 产品概述 + +### 1.1 产品定义 + +{{说明目标用户、核心作业对象、业务环节和最终结果}} + +![图 1 {{产品名称}}产品全景](assets/product-overview.svg) + +
图 1 {{产品名称}}产品全景
+ +### 1.2 业务背景与主要问题 + +- {{影响产品范围、流程或验收的问题}} + +### 1.3 产品定位 + +产品负责: + +- {{产品职责}} + +产品范围不包含: + +- {{明确边界}} + +## 2. 产品目标 + +### 2.1 用户目标 + +- {{用户可感知目标}} + +### 2.2 业务目标 + +- {{业务结果}} + +### 2.3 强制性质量要求 + +- {{触发阻断的安全、权限、质量或交付条件}} + +## 3. 产品范围 + +### 3.1 首期交付范围 + +1. {{首期完整流程步骤}} + +### 3.2 扩展能力范围 + +- {{能力及启用条件}} + +### 3.3 产品形态与平台范围 + +| 产品形态 | 首期范围 | 后续规划或开放条件 | +| --- | --- | --- | +| {{形态}} | {{首期范围}} | {{条件}} | + +## 4. 用户角色与职责 + +| 角色 | 主要职责 | 职责限制 | +| --- | --- | --- | +| {{角色}} | {{职责}} | {{限制}} | + +## 5. 产品信息架构 + +### 5.1 一级导航规划 + +| 功能入口 | 功能定位 | 主要内容 | +| --- | --- | --- | +| {{入口}} | {{定位}} | {{内容}} | + +![图 2 {{页面名称}}界面原型](assets/{{prototype-file}}.png) + +
图 2 {{页面名称}}界面原型
+ +### 5.2 页面结构规范 + +1. {{稳定信息层级}} + +## 6. 核心业务流程 + +![图 3 {{核心业务流程名称}}](assets/core-workflow.png) + +
图 3 {{核心业务流程名称}}
+ +### 6.1 {{流程阶段}} + +{{说明入口、条件、产品行为、结果和失败处理}} + +## 7. 功能需求 + +优先级说明:P0 为首个正式交付版本必须满足的能力;P1 为重要增强;P2 为特定任务或后续版本能力。 + +### 7.1 {{业务域}} + +| 编号 | 优先级 | 需求 | 验收要点 | +| --- | --- | --- | --- | +| {{PREFIX-01}} | {{P0}} | {{产品行为}} | {{可观察结果、阻断条件或规范依据}} | + +## 8. 数据与质量规则 + +### 8.1 业务对象 + +| 对象 | 含义 | 与最终结果的关系 | +| --- | --- | --- | +| {{对象}} | {{产品定义}} | {{关系}} | + +### 8.2 状态定义与边界 + +| 状态 | 判定条件 | 后续操作 | +| --- | --- | --- | +| {{状态}} | {{可验证条件}} | {{下一步}} | + +### 8.3 完整性与可追溯性 + +- {{版本、历史记录、内容一致性和凭证要求}} + +### 8.4 质量准入标准 + +| 类别 | 通用要求 | +| --- | --- | +| {{类别}} | {{要求或规范依据}} | + +### 8.5 生命周期 + +| 阶段或对象 | 存储原则 | 清理规则 | +| --- | --- | --- | +| {{对象}} | {{原则}} | {{规则}} | + +## 9. 安全、隐私、可靠性与运营保障 + +### 9.1 安全与隐私 + +- {{受保护对象、触发条件和产品行为}} + +### 9.2 可靠性 + +- {{异常恢复和完整性要求}} + +### 9.3 性能与资源 + +- {{带测试条件或规范依据的要求}} + +### 9.4 运行监测与支持 + +- {{用户提示、问题编号和脱敏记录}} + +### 9.5 发布管理 + +- {{发布准入和说明范围}} + +## 10. 分阶段验收 + +### 10.1 {{阶段名称}} + +- {{角色、设备、代表性数据、异常场景和通过证据}} + +## 11. 运营评价指标 + +| 指标 | 统计口径 | 数据来源 | 目标或确定方式 | +| --- | --- | --- | --- | +| {{指标}} | {{分子、分母或统计范围}} | {{来源}} | {{目标或建立基线的方式}} | + +## 12. 术语 + +| 术语 | 含义 | +| --- | --- | +| {{术语}} | {{正式产品定义}} | diff --git a/skills/formal-chinese-prd/references/document-structure.md b/skills/formal-chinese-prd/references/document-structure.md new file mode 100644 index 0000000..b4ebca5 --- /dev/null +++ b/skills/formal-chinese-prd/references/document-structure.md @@ -0,0 +1,169 @@ +# PRD 章节与表格结构 + +## 使用原则 + +以下为完整产品需求说明书的推荐结构。根据产品规模选择章节,删除不适用内容并保持剩余编号连续。不得保留空章节、占位文字或只为凑齐结构而编写的泛化内容。 + +材料较少时采用最小结构:产品概述、目标与范围、角色、核心流程、功能需求、必要的数据或状态规则、验收、待决策事项。术语仅在正文存在歧义时保留;信息架构、运营指标、安全和发布等章节仅在事实或明确需求支持时加入。缺少内容时省略章节,不用常识补写。 + +## 文档首页 + +- 产品名称与文档名称 +- 文档版本 +- 文档状态 +- 更新日期 +- 适用范围 +- 文档目的,一段即可 + +## 1. 产品概述 + +### 1.1 产品定义 + +用一段话说明目标用户、核心作业对象、覆盖的业务环节和最终结果。产品全景图放在本节后。 + +### 1.2 业务背景与主要问题 + +只列会影响产品范围、流程或验收的问题。每项问题应可在后续需求中得到对应处理。 + +### 1.3 产品定位 + +分别列出产品负责和不负责的事项,避免边界依赖读者推断。 + +## 2. 产品目标 + +- 用户目标:完成任务、判断状态、处理异常、复核结果; +- 业务目标:质量、效率、一致性、规模化和追溯; +- 强制性质量要求:触发阻断的安全、隐私、审批和交付条件。 + +目标必须能映射到功能、质量规则或运营指标。 + +## 3. 产品范围 + +- 首期交付范围:按完整业务流程列出; +- 扩展能力范围:说明何时启用和开放条件; +- 产品形态与平台范围:区分首期范围、后续规划和验收条件。 + +推荐表格: + +| 产品形态 | 首期范围 | 后续规划或开放条件 | +| --- | --- | --- | + +## 4. 用户角色与职责 + +| 角色 | 主要职责 | 职责限制 | +| --- | --- | --- | + +说明提交、审批、质量复核和交付是否需要职责分离。 + +## 5. 产品信息架构 + +### 5.1 一级导航规划 + +| 功能入口 | 功能定位 | 主要内容 | +| --- | --- | --- | + +说明默认入口、任务上下文、全局信息和按条件显示的入口。信息架构原型放在本节后。 + +### 5.2 页面结构规范 + +说明每个工作页面的稳定信息顺序、主要操作、状态、结果、错误和详情展开方式。 + +## 6. 核心业务流程 + +流程图置于章节开头,正文按阶段解释: + +- 入口和任务准备; +- 数据或业务对象进入; +- 处理与自动检查; +- 人工确认、审批和冻结; +- 交付、回执和完成; +- 失败、退回、恢复和重新提交。 + +流程正文必须与流程图一一对应。 + +## 7. 功能需求 + +首次出现时定义优先级: + +- P0:首个正式交付版本必须满足; +- P1:重要增强; +- P2:特定任务或后续版本能力。 + +按业务域分节,每节使用统一表格: + +| 编号 | 优先级 | 需求 | 验收要点 | +| --- | --- | --- | --- | + +编号前缀按业务域定义并保持唯一,例如 `TASK-01`、`CAP-01`。同一前缀从 01 连续递增。界面原型紧跟对应需求表。 + +## 8. 数据与质量规则 + +### 8.1 业务对象 + +| 对象 | 含义 | 与最终结果的关系 | +| --- | --- | --- | + +### 8.2 状态定义与边界 + +| 状态 | 判定条件 | 后续操作 | +| --- | --- | --- | + +将过程完成、质量通过、审批通过、冻结、上传和业务完成分别定义。 + +### 8.3 完整性与可追溯性 + +说明版本关系、历史记录、内容一致性、缺失数据表达和最终凭证。 + +### 8.4 质量准入标准 + +| 类别 | 通用要求 | +| --- | --- | + +没有可靠依据时引用任务规范或专项验收标准,不编造阈值。 + +### 8.5 生命周期 + +| 阶段或对象 | 存储原则 | 清理规则 | +| --- | --- | --- | + +## 9. 安全、隐私、可靠性与运营保障 + +根据产品范围选择: + +- 安全与隐私; +- 可靠性与异常恢复; +- 性能与资源; +- 运行监测与支持; +- 发布管理。 + +每条规则写明受保护对象、触发条件和产品行为。 + +## 10. 分阶段验收 + +阶段以可验收成果命名。每个阶段说明实际角色、目标设备、代表性数据、异常场景和通过证据。避免只写研发活动或完成百分比。 + +## 11. 运营评价指标 + +| 指标 | 统计口径 | 数据来源 | 目标或确定方式 | +| --- | --- | --- | --- | + +百分比指标必须给出分子、分母和统计范围。尚无基线时写明建立基线和确定目标的时间点。 + +## 12. 术语 + +| 术语 | 含义 | +| --- | --- | + +只收录正文中确实使用、且容易产生歧义的术语。定义使用产品语言,不写类名、字段名或代码结构。 + +## 可选章节 + +仅在用户需要时增加: + +- 待决策事项; +- 风险与依赖; +- 版本计划; +- 合规要求; +- 商业规则。 + +可选章节不得取代核心范围、流程、需求和验收内容。 diff --git a/skills/formal-chinese-prd/references/formal-prd-style.md b/skills/formal-chinese-prd/references/formal-prd-style.md new file mode 100644 index 0000000..87c6472 --- /dev/null +++ b/skills/formal-chinese-prd/references/formal-prd-style.md @@ -0,0 +1,165 @@ +# 正式中文 PRD 写作规范 + +## 1. 文档对象 + +正文面向产品、业务、设计、测试、运营和管理人员。读者应能直接回答以下问题: + +- 产品解决什么业务问题; +- 哪些内容属于本期范围; +- 谁在什么条件下执行什么操作; +- 系统产生什么可观察结果; +- 失败后在哪里停止、如何恢复、保留哪些数据; +- 以什么证据确认审批、交付和最终完成。 + +产品文档不承担代码说明、技术设计、研发周报或项目排期的职责。上述内容如有必要,应放入独立材料。 + +## 2. 信息边界 + +### 已确认事实 + +直接陈述,避免“可能、应该、大概”等弱化词。 + +示例: + +> 来源素材导入为默认作业路径。现场采集仅在任务需要实时记录时开放。 + +### 目标需求 + +使用明确的规范词,并提供验收方式。 + +示例: + +> 上传完成后,系统必须取得业务平台对指定交付对象的有效回执,方可将样本标记为已上传。 + +### 待决策事项 + +单独记录需要确认的内容、影响范围和决策主体。不得把猜测写成正式承诺,也不得用虚构数值填补空白。 + +示例: + +> 待决策:移动协作端是否纳入首期范围。该决定影响设备配对、权限、断线恢复和发布验收范围,由产品负责人和交付负责人共同确认。 + +## 3. 句子结构 + +优先使用下列结构: + +`对象或角色 + 条件 + 行为 + 结果或限制` + +示例: + +> 来源文件被移动、删除或修改时,系统停止后续处理并提示用户重新选择。 + +> 审批通过后生成冻结版本。后续内容变化须另建版本并重新审批。 + +> 网络恢复后继续未完成的上传;业务平台已确认接收的内容不得重复提交。 + +段落首句先给结论。一个段落只说明一个主题,通常控制在二至四句。并列信息超过三项时使用列表或表格。 + +## 4. 标题 + +使用陈述式名词短语: + +- 产品定义 +- 业务背景与主要问题 +- 用户角色与职责 +- 核心业务流程 +- 数据与质量规则 +- 质量准入标准 + +避免: + +- 一句话说明 +- 用户怎么完成交付 +- 为什么需要这个产品 +- 我们接下来要做什么 +- 核心结论先说 + +## 5. 用词 + +### 推荐用词 + +| 意图 | 推荐表达 | +| --- | --- | +| 强制准入 | 必须、不得、仅在……后方可、未通过时停止 | +| 常规产品行为 | 应、系统显示、系统记录、系统保留 | +| 用户能力 | 用户可以、支持、允许 | +| 适用边界 | 仅在任务要求时启用、暂不纳入首期、通过专项验收后开放 | +| 失败处理 | 停止后续流程、说明影响范围、提供恢复入口、保留已完成数据 | +| 可追溯性 | 对应指定版本、记录责任人和时间、保留历史记录 | + +### 避免的表达 + +| 类型 | 避免 | 改写方向 | +| --- | --- | --- | +| 模板化 AI 表达 | 一句话说明;不是……而是……;不等于;而非 | 直接定义对象或分别说明两个状态 | +| 写作过程 | 下面将介绍;接下来我们讨论;综上所述;值得注意的是 | 删除,直接进入事实或规则 | +| 宣传词 | 一站式;领先;革命性;强大;智能化赋能;极致体验;无缝衔接 | 改为范围、能力、指标或可观察结果 | +| 项目黑话 | 打通;拉通;闭环;抓手;沉淀;兜底;贯通 | 改为明确动作、责任人和结果 | +| 翻译腔 | 业务化错误;原始事实;原地修改;容量投影;完整链路;鉴权 | 使用“错误说明、来源数据、直接修改、空间估算、业务流程、身份验证或授权” | +| 无依据形容词 | 友好;高效;快速;稳定;显著提升 | 补充可观察行为、适用条件或测量口径 | + +“不是……而是……”一类表达通常掩盖定义不清。分别写出两个对象的正式定义。 + +反例: + +> 冻结不是完成,而是交付前的一个状态。 + +改写: + +> 已冻结表示内容与审批记录完成绑定。下一步执行兼容性校验;取得有效平台回执后方可标记为已上传。 + +## 6. 需求与验收 + +需求表固定使用: + +| 编号 | 优先级 | 需求 | 验收要点 | +| --- | --- | --- | --- | + +需求列写产品行为,验收列写可观察证据。 + +反例: + +| 需求 | 验收要点 | +| --- | --- | +| 支持失败重试 | 可以失败重试 | + +改写: + +| 需求 | 验收要点 | +| --- | --- | +| 网络恢复后继续未完成的上传 | 已完成文件不重复传输;平台已确认接收的对象不重复提交 | + +每条需求遵循以下规则: + +- 一个编号只描述一个主要能力; +- “支持”后补充对象、条件和结果; +- 异常路径说明停止条件、提示内容、恢复入口和数据保留方式; +- 绝对承诺必须有验证条件; +- 数值阈值必须说明数据来源、测试环境或规范依据; +- 同一概念全文只保留一个正式名称; +- 验收要点不得只复述需求原句。 + +## 7. 从实现证据到产品语言 + +| 实现型写法 | 产品型写法 | +| --- | --- | +| 根据当前仓库,系统已经实现上传能力 | 首期版本应支持上传、回执确认和任务状态同步 | +| 后台采用任务队列执行处理 | 处理任务在统一列表中管理,并显示等待、运行、暂停、失败和完成状态 | +| 调用接口后写入数据库 | 提交成功后记录交付对象、时间和平台回执,并可按样本查询 | +| 代码在异常时保存 checkpoint | 异常中断后保留最近有效进度,恢复时无需重复执行已通过检查的阶段 | +| 使用哈希判断文件变化 | 处理与交付前确认来源内容与导入时一致;内容变化时停止后续流程 | + +转换时保留可验证的行为和约束,删除实现机制。若技术名词本身构成产品兼容范围或交付格式,可以保留,例如操作系统、设备接口或标准数据格式。 + +## 8. 压缩方法 + +重写现有文档时按以下顺序删减: + +1. 删除写作过程、重复结论、泛化背景和宣传用语; +2. 合并表达相同规则的段落; +3. 将角色、状态、数据对象和指标改为表格; +4. 将实现描述转译为用户行为和验收结果; +5. 将未确认的精确承诺移入待决策事项; +6. 删除无法影响产品决策、验收或操作的内容。 + +删减后仍须保留范围边界、异常路径、质量门槛、职责分离和交付证据。 diff --git a/skills/formal-chinese-prd/references/review-checklist.md b/skills/formal-chinese-prd/references/review-checklist.md new file mode 100644 index 0000000..ee3999a --- /dev/null +++ b/skills/formal-chinese-prd/references/review-checklist.md @@ -0,0 +1,84 @@ +# PRD 交付前检查清单 + +## 1. 事实与范围 + +- 产品定义、目标用户、业务对象和最终结果有可靠来源; +- 首期范围、扩展范围和明确不做的内容已经区分; +- 未确认能力未被写成正式承诺; +- 精确阈值、平台、设备和格式有任务规范或验收依据; +- 项目计划、研发进度和技术方案未混入产品正文。 + +## 2. 结构与可读性 + +- 一条 H1,章节编号连续; +- 章节顺序符合产品理解路径; +- 没有空章节、占位内容或重复结论; +- 段首先给结论,标题使用陈述式名词短语; +- 角色、状态、对象、指标和对比信息优先用表格; +- 术语全文一致,无同义替换或代码名称混用。 + +## 3. 语言 + +- 没有“一句话说明”等不正式标题; +- 没有“不是……而是……”等模板化对比句; +- 没有写作过程、宣传词、项目黑话和无依据形容词; +- 没有“根据仓库”“当前代码”等实现来源说明; +- “必须、不得、应、支持、可以”的强度使用准确; +- 每句话都能影响产品决策、用户操作、业务规则或验收。 + +## 4. 需求 + +- 每条需求编号唯一且连续; +- 优先级已经定义,P0 能力构成完整业务流程; +- 一条需求只描述一个主要能力; +- 需求列写行为,验收列写可观察结果; +- 异常路径包含停止条件、提示内容、恢复入口和数据保留; +- “支持”类需求给出适用条件和验收结果; +- 绝对承诺具有测试条件或规范依据; +- 角色权限、职责分离和高风险操作边界明确。 + +## 5. 流程、状态和数据 + +- 正常路径从入口走到最终业务凭证; +- 所有判断节点都有通过和未通过路径; +- 失败回退到真正需要重新执行的阶段; +- 内容变化后重新处理、审批、冻结和校验; +- 处理完成、检查通过、审批通过、冻结、上传和完成没有混用; +- 来源、处理版本、冻结结果、交付对象和平台回执关系清晰; +- 最终状态具有可验证证据。 + +## 6. 图表和原型 + +- 产品全景图、核心流程图和界面原型各自承担单一信息任务; +- PlantUML 源文件与 PNG 一致; +- 图号、替代文本、图题和文件链接一致且连续; +- 所有图片存在,字号可读,未裁切; +- 原型导航、状态、进度、按钮和检查结果一致; +- 每个重要独立工作区均有必要的界面证据; +- 图片不含技术架构、仓库信息、凭据或隐私数据。 + +## 7. 输出格式 + +- Markdown 链接可用; +- Word 使用 A4,中文目录已更新; +- 图片完整嵌入,图像与图题同页; +- 表格表头重复,单行未跨页拆分; +- 流程图需要时使用单独 A4 横向页; +- PDF 逐页检查后无空白孤页、裁切、续表碎片或英文目录标题; +- 最终文件名、版本、日期和状态正确。 + +## 8. 自动检查 + +先运行: + +```bash +python scripts/check_prd.py document.md +``` + +修正错误并逐项复核警告。正式交付前再运行: + +```bash +python scripts/check_prd.py document.md --strict +``` + +脚本通过后仍须完成上述人工检查,尤其是事实、范围、流程和图片状态一致性。 diff --git a/skills/formal-chinese-prd/references/visual-and-layout.md b/skills/formal-chinese-prd/references/visual-and-layout.md new file mode 100644 index 0000000..bbbaab7 --- /dev/null +++ b/skills/formal-chinese-prd/references/visual-and-layout.md @@ -0,0 +1,120 @@ +# 产品图、界面原型与文档排版规范 + +## 1. 统一视觉语言 + +采用浅色工业软件风格,正式、克制、信息优先。 + +| 项目 | 建议 | +| --- | --- | +| 背景 | 浅灰或浅灰蓝,例如 `#F4F7FA`、`#F7FAFD` | +| 面板 | 白色或极浅灰,蓝灰细边框,少量低透明阴影 | +| 主文字 | 深蓝灰,例如 `#17324D`、`#183B56` | +| 次文字 | 灰蓝,例如 `#60788E`、`#657C90` | +| 主操作 | 蓝色;单页只保留一个最高视觉优先级 | +| 通过 | 绿色或青绿色,并配文字 | +| 警告和回退 | 橙色,用于退回、修正和重新提交 | +| 阻断 | 红色,仅用于明确失败或禁止继续 | +| 字体 | Inter 配合苹方、思源黑体、微软雅黑回退;PlantUML 使用 Noto Sans CJK SC | + +状态不得只用颜色区分,须同时使用文字、图标或线型。 + +## 2. 产品全景图 + +用于说明产品定义、主要阶段、产品形态和核心价值。 + +- 优先采用 1600×900、16:9 SVG,确保中文文字准确且可编辑; +- 结构采用“标题区 → 单向产品主线 → 产品形态 → 核心价值”; +- 主流程控制在五至七个阶段; +- 每张卡片只保留标题和一至两行短说明; +- 后续能力使用灰色、虚线或“后续”标签,不与正式范围混淆; +- 使用线性图标,避免装饰性照片、复杂插画和技术架构; +- SVG 包含 ``、`<desc>` 和中文字体回退; +- 审批回退和异常分支放入核心流程图,不塞入全景图。 + +## 3. PlantUML 核心业务流程图 + +用于说明业务如何推进、什么条件可以放行、失败后回到哪里。 + +- 保留 `.puml` 源文件,生成同名 `.png` 插入文档; +- PNG 宽高比建议为 1.35–1.8,适合 A4 页面; +- 按业务阶段分组,使用正交连线、短动宾节点和明确判断节点; +- 蓝灰表示主流程,绿色表示通过,橙色表示退回和重做; +- 默认路径和按条件启用的路径直接标注; +- 回退箭头必须落到实际需要重新执行的步骤,不写笼统的“返回上一步”; +- 内容变化后需要重新处理、审批和冻结时,必须画出完整回路; +- 只有不改变业务内容的封装问题,才可直接返回封装或兼容性检查; +- 最终完成节点前必须显示产品定义的质量门槛和业务凭证; +- 图例只解释必要语义; +- 不出现代码模块、仓库路径、技术栈、研发状态或实现方案。 + +渲染后检查 PNG 尺寸、文字可读性和完整路径。竖版文档中字号偏小时,为流程图单独使用 A4 横向页,不得裁切。 + +## 4. 软件界面原型 + +原型用于表达信息层级和关键交互,不代表最终视觉稿或现有实现。 + +通用页面骨架: + +1. 顶部保持统一品牌与一级导航; +2. 工作区顶部持续显示任务、样本、版本和当前状态; +3. 左侧放对象列表、处理队列或工具栏; +4. 中央区域承载当前页面最重要的内容或媒体; +5. 右侧放配置、质量结论、问题和审批信息; +6. 底部放长任务状态、时间轴或唯一主要操作; +7. 详情、记录和排查信息默认折叠。 + +原型宽高比建议为 1.55–1.65,便于竖版报告按页宽插入。 + +### 状态一致性 + +- 一级导航名称与信息架构保持一致,当前页面高亮正确; +- 状态、进度、按钮和右侧结论互相匹配; +- 预检未开始时不显示正在记录; +- 质量检查进行中时显示实时检查结果,不显示最终质量报告; +- 本地校验失败时,尚未提交的平台结果保持空白或中性状态; +- 自动结果默认“待确认”,人工确认后方可进入审批; +- 审批界面明确展示对象、差异、风险和操作后果。 + +文档定义独立工作区时,提供对应界面原型。例如包含“本地处理”一级入口时,不得只提供任务、采集、标注和交付原型。 + +## 5. CVAT 参考边界 + +可借鉴媒体标注工作台的信息结构: + +- 中央媒体画布; +- 左侧垂直工具栏; +- 顶部样本、帧和视角导航; +- 底部播放控制与时间轴; +- 右侧对象、问题、修改记录和审批区; +- 问题可跳转到对应位置。 + +必须使用当前产品自己的品牌、导航、色板和中文业务术语。不得复制 CVAT 品牌、Logo、截图、CSS、图标组合、快捷键、按钮文案或未在当前产品中定义的功能。CVAT 的参考仅适用于媒体标注工作区,不应机械套用到任务中心、采集或交付页面。 + +## 6. 图片插入 + +每组图片使用以下格式: + +```markdown +![图 1 产品全景](assets/product-overview.svg) + +<div align="center">图 1 产品全景</div> +``` + +- 图号按出现顺序连续; +- 替代文本与图题一致; +- 图片紧跟首次解释该概念的正文或需求表; +- 一张图只回答一个层级的问题; +- 图片和图题在 Word/PDF 中保持同页; +- 缩放后正文、状态和箭头标签无需放大即可辨认; +- 图片不得包含代码、路径、凭据、内部接口或未脱敏数据。 + +## 7. Word 与 PDF + +- 使用 A4;流程图确需横向时只切换对应页面; +- 封面、中文目录和正文分页明确; +- 更新目录字段并检查页码; +- 标题与下段同页,图像与图题同页; +- 表格保留重复表头,禁止单行跨页拆分; +- 图片必须嵌入文件,不能只保留外部链接; +- 转为 PDF 后逐页检查封面、目录、所有图片、长表格和最后一页; +- 不以“文件成功生成”替代视觉验收。 diff --git a/skills/formal-chinese-prd/scripts/check_prd.py b/skills/formal-chinese-prd/scripts/check_prd.py new file mode 100755 index 0000000..91f8b45 --- /dev/null +++ b/skills/formal-chinese-prd/scripts/check_prd.py @@ -0,0 +1,447 @@ +#!/usr/bin/env python3 +"""Deterministic structural and wording checks for formal Chinese PRDs.""" + +from __future__ import annotations + +import argparse +import json +import re +import sys +from collections import defaultdict +from dataclasses import asdict, dataclass +from pathlib import Path, PurePosixPath + + +@dataclass(frozen=True) +class Finding: + level: str + code: str + line: int + message: str + snippet: str = "" + + +def add( + findings: list[Finding], + level: str, + code: str, + line: int, + message: str, + snippet: str = "", +) -> None: + findings.append( + Finding(level, code, line, message, snippet.strip()[:180]) + ) + + +def visible_lines(lines: list[str]) -> list[tuple[int, str]]: + result: list[tuple[int, str]] = [] + in_fence = False + fence = "" + in_comment = False + + for line_number, raw in enumerate(lines, 1): + stripped = raw.strip() + fence_match = re.match(r"^(```+|~~~+)", stripped) + if fence_match: + marker = fence_match.group(1)[0] + if not in_fence: + in_fence = True + fence = marker + elif marker == fence: + in_fence = False + fence = "" + continue + if in_fence: + continue + + line = raw + if in_comment: + if "-->" in line: + line = line.split("-->", 1)[1] + in_comment = False + else: + continue + while "<!--" in line: + before, after = line.split("<!--", 1) + if "-->" in after: + after = after.split("-->", 1)[1] + line = before + after + else: + line = before + in_comment = True + break + result.append((line_number, line)) + return result + + +def parse_cells(line: str) -> list[str] | None: + stripped = line.strip() + if not (stripped.startswith("|") and stripped.endswith("|")): + return None + return [cell.strip() for cell in stripped[1:-1].split("|")] + + +def is_separator_row(cells: list[str] | None) -> bool: + if not cells: + return False + return all(bool(re.fullmatch(r":?-{3,}:?", cell.replace(" ", ""))) for cell in cells) + + +def plain_text(value: str) -> str: + value = re.sub(r"[`*_~]", "", value) + value = re.sub(r"<[^>]+>", "", value) + return re.sub(r"\s+", "", value) + + +def check_headings( + visible: list[tuple[int, str]], findings: list[Finding] +) -> None: + nonempty = [(n, line) for n, line in visible if line.strip()] + h1 = [(n, line) for n, line in visible if re.match(r"^#(?!#)\s+\S", line)] + if len(h1) != 1: + add(findings, "ERROR", "HEAD001", 1, f"文档必须且只能有一个 H1,当前为 {len(h1)} 个") + if nonempty and not re.match(r"^#(?!#)\s+\S", nonempty[0][1]): + add( + findings, + "ERROR", + "HEAD002", + nonempty[0][0], + "第一条非空内容必须是未编号的 H1 文档标题", + nonempty[0][1], + ) + + expected_h2 = 1 + current_h2: int | None = None + expected_h3 = 1 + for line_number, line in visible: + if re.match(r"^#{4,}\s+", line): + add( + findings, + "WARN", + "HEAD003", + line_number, + "默认结构不建议使用 H4 或更深标题,请确认层级确有必要", + line, + ) + if re.match(r"^##(?!#)\s+", line): + match = re.match(r"^##\s+(\d+)\.\s+(.+)$", line) + if not match: + add( + findings, + "ERROR", + "HEAD004", + line_number, + "H2 必须使用“## N. 标题”格式", + line, + ) + current_h2 = None + continue + number = int(match.group(1)) + if number != expected_h2: + add( + findings, + "ERROR", + "HEAD005", + line_number, + f"H2 编号应为 {expected_h2},实际为 {number}", + line, + ) + expected_h2 = number + current_h2 = number + expected_h2 += 1 + expected_h3 = 1 + elif re.match(r"^###(?!#)\s+", line): + match = re.match(r"^###\s+(\d+)\.(\d+)\s+(.+)$", line) + if not match: + add( + findings, + "ERROR", + "HEAD006", + line_number, + "H3 必须使用“### N.M 标题”格式", + line, + ) + continue + parent, number = int(match.group(1)), int(match.group(2)) + if current_h2 is None or parent != current_h2: + add( + findings, + "ERROR", + "HEAD007", + line_number, + "H3 的一级编号必须与当前 H2 一致", + line, + ) + if number != expected_h3: + add( + findings, + "ERROR", + "HEAD008", + line_number, + f"当前章节的 H3 编号应为 {expected_h3},实际为 {number}", + line, + ) + expected_h3 = number + expected_h3 += 1 + + +def check_style(visible: list[tuple[int, str]], findings: list[Finding]) -> None: + hard_rules = [ + ("STYLE001", re.compile(r"一句话(?:说明|概述|总结|介绍)"), "删除不正式的“一句话……”标题,直接使用正式主题名称"), + ("STYLE002", re.compile(r"不是.{0,40}?而是"), "删除“不是……而是……”模板句,直接定义对象或分别说明状态"), + ("STYLE003", re.compile(r"\b(?:TODO|FIXME|TBD)\b|待补充|待完善|占位内容|\{\{[^{}]+\}\}", re.I), "交付文档不得保留占位内容"), + ("STYLE004", re.compile(r"作为一个\s*(?:AI|人工智能)|由\s*AI\s*生成", re.I), "产品正文不得暴露生成过程"), + ] + warning_rules = [ + ("STYLE101", re.compile(r"不等于|而非"), "避免模板化对比,改为直接说明各对象的定义和条件"), + ("STYLE102", re.compile(r"下面将|接下来将|本节将|综上所述|总而言之|由此可见|不难发现|显而易见|值得注意的是|需要指出的是"), "删除写作过程和过渡套话"), + ("STYLE103", re.compile(r"一站式|革命性|颠覆性|行业领先|业界领先|极致体验|无缝衔接|全面赋能|智能化赋能"), "将宣传词改为可观察的产品范围、行为或指标"), + ("STYLE104", re.compile(r"打通|拉通|抓手|形成闭环|高效闭环|兜底|贯通全链路"), "将项目黑话改为明确动作、责任人和结果"), + ("STYLE105", re.compile(r"业务化错误|原始事实|原地修改|容量投影|完整链路|鉴权"), "使用符合中文业务语境的正式词汇"), + ("STYLE106", re.compile(r"我们将|我们可以"), "删除作者视角,直接陈述产品要求"), + ] + implementation_rules = [ + ("TECH101", re.compile(r"仓库结构|代码仓库|源码|代码实现|代码提交记录|Git\s*提交|\bcommit\b|\bpull request\b|\bPR\b|\bissue\b", re.I), "产品正文疑似包含仓库或研发过程信息"), + ("TECH102", re.compile(r"(?:^|[\s`])(?:src|packages)/|package\.json|Cargo\.toml|pyproject\.toml|[\w./-]+\.(?:ts|tsx|js|jsx|py|rs|go|java|cpp|proto)(?:\b|`)", re.I), "产品正文疑似包含工程路径或源文件"), + ("TECH103", re.compile(r"Electron|Tauri|React|Vue|Node\.js|SQLite|PostgreSQL|Redis|Docker|Kubernetes", re.I), "请确认技术栈名称是否构成对外产品约束;否则移出 PRD"), + ("TECH104", re.compile(r"数据库表|API\s*(?:路由|端点|地址)|endpoint|消息队列|线程池|类名|函数名|方法名|采用.{0,20}框架|基于.{0,20}实现|调用.{0,20}API|写入.{0,20}表", re.I), "请将实现机制转译为用户可感知的产品行为"), + ("TECH105", re.compile(r"(?:^|[\s`])/(?:home|Users|workspace|opt|var)/[^\s`]+"), "产品正文不得包含本地绝对路径"), + ] + + for line_number, line in visible: + scan = re.sub(r"\]\([^)]*\)", "]", line) + for code, pattern, message in hard_rules: + if pattern.search(scan): + add(findings, "ERROR", code, line_number, message, line) + for code, pattern, message in warning_rules + implementation_rules: + if pattern.search(scan): + add(findings, "WARN", code, line_number, message, line) + + +def check_requirement_tables( + visible: list[tuple[int, str]], findings: list[Finding] +) -> None: + header = ["编号", "优先级", "需求", "验收要点"] + id_pattern = re.compile(r"^[A-Z][A-Z0-9]{1,8}-\d{2,3}$") + all_id_pattern = re.compile(r"\b[A-Z][A-Z0-9]{1,8}-\d{2,3}\b") + vague = re.compile(r"尽可能|适当|合理|尽快|大约|若干|视情况|原则上|友好|高效|快速|稳定运行|显著提升") + definitions: dict[str, int] = {} + prefix_numbers: dict[str, list[tuple[int, int]]] = defaultdict(list) + + index = 0 + while index < len(visible): + line_number, line = visible[index] + cells = parse_cells(line) + if cells != header: + index += 1 + continue + if index + 1 >= len(visible) or not is_separator_row(parse_cells(visible[index + 1][1])): + add(findings, "ERROR", "REQ001", line_number, "需求表头后必须紧跟 Markdown 分隔行", line) + index += 1 + continue + + row_index = index + 2 + while row_index < len(visible): + row_line_number, row_line = visible[row_index] + row_cells = parse_cells(row_line) + if row_cells is None or is_separator_row(row_cells): + break + if len(row_cells) != 4: + add(findings, "ERROR", "REQ002", row_line_number, "需求表每行必须恰好包含四列", row_line) + row_index += 1 + continue + requirement_id, priority, requirement, acceptance = row_cells + if not all(row_cells): + add(findings, "ERROR", "REQ003", row_line_number, "需求表不得包含空单元格", row_line) + if not id_pattern.fullmatch(requirement_id): + add(findings, "ERROR", "REQ004", row_line_number, "需求编号应使用 PREFIX-01 格式", requirement_id) + else: + if requirement_id in definitions: + add( + findings, + "ERROR", + "REQ005", + row_line_number, + f"需求编号重复,首次出现在第 {definitions[requirement_id]} 行", + requirement_id, + ) + else: + definitions[requirement_id] = row_line_number + prefix, number = requirement_id.rsplit("-", 1) + prefix_numbers[prefix].append((int(number), row_line_number)) + if priority not in {"P0", "P1", "P2"}: + add(findings, "ERROR", "REQ006", row_line_number, "优先级只能使用 P0、P1 或 P2", priority) + if vague.search(requirement) or vague.search(acceptance): + add(findings, "WARN", "REQ101", row_line_number, "需求包含难以验收的模糊词,请补充条件、结果或测量口径", row_line) + if requirement and acceptance and plain_text(requirement) == plain_text(acceptance): + add(findings, "WARN", "REQ102", row_line_number, "验收要点不得只复述需求原句", row_line) + row_index += 1 + index = row_index + + for prefix, numbered in sorted(prefix_numbers.items()): + numbers = sorted({number for number, _ in numbered}) + expected = list(range(1, max(numbers) + 1)) if numbers else [] + if numbers != expected: + line_number = min(line for _, line in numbered) + add( + findings, + "ERROR", + "REQ007", + line_number, + f"{prefix} 编号应从 01 连续递增,实际为 {numbers}", + ) + + defined_prefixes = {requirement_id.rsplit("-", 1)[0] for requirement_id in definitions} + for line_number, line in visible: + for reference in all_id_pattern.findall(line): + prefix = reference.rsplit("-", 1)[0] + if prefix in defined_prefixes and reference not in definitions: + add(findings, "WARN", "REQ103", line_number, f"正文引用了未定义的需求编号 {reference}", line) + + +def normalize_caption(value: str) -> str: + return re.sub(r"\s+", "", value.replace(" ", " ")) + + +def check_images( + document_path: Path, + visible: list[tuple[int, str]], + findings: list[Finding], +) -> None: + image_pattern = re.compile(r"!\[([^\]]*)\]\(([^)]+)\)") + caption_pattern = re.compile(r"^<div\s+align=[\"']center[\"']>(.*?)</div>$", re.I) + figure_pattern = re.compile(r"^图\s*(\d+)[\s ]+(.+)$") + allowed_extensions = {".png", ".jpg", ".jpeg", ".svg", ".webp"} + figures: list[tuple[int, int]] = [] + referenced_paths: dict[str, int] = {} + + for position, (line_number, line) in enumerate(visible): + for match in image_pattern.finditer(line): + alt, target = match.group(1).strip(), match.group(2).strip() + if not alt: + add(findings, "ERROR", "IMG001", line_number, "图片必须包含描述性替代文本", line) + + if re.match(r"^[a-z]+://", target, re.I) or target.startswith("data:"): + add(findings, "ERROR", "IMG002", line_number, "产品文档图片必须使用 assets/ 下的本地相对路径", target) + continue + normalized_target = target.replace("\\", "/") + pure = PurePosixPath(normalized_target) + if pure.is_absolute() or ".." in pure.parts or not pure.parts or pure.parts[0] != "assets": + add(findings, "ERROR", "IMG003", line_number, "图片路径必须位于当前文档的 assets/ 目录内", target) + continue + extension = Path(normalized_target).suffix.lower() + if extension not in allowed_extensions: + add(findings, "ERROR", "IMG004", line_number, f"不支持的图片格式 {extension}", target) + asset = (document_path.parent / normalized_target).resolve() + try: + asset.relative_to(document_path.parent.resolve()) + except ValueError: + add(findings, "ERROR", "IMG005", line_number, "图片路径逃逸出文档目录", target) + if not asset.exists(): + add(findings, "ERROR", "IMG006", line_number, "图片文件不存在", target) + if normalized_target in referenced_paths: + add( + findings, + "WARN", + "IMG101", + line_number, + f"图片已在第 {referenced_paths[normalized_target]} 行引用", + target, + ) + else: + referenced_paths[normalized_target] = line_number + + figure_match = figure_pattern.fullmatch(alt) + if not figure_match: + add(findings, "ERROR", "FIG001", line_number, "图片替代文本必须使用“图 N 标题”格式", alt) + else: + figures.append((int(figure_match.group(1)), line_number)) + + next_nonempty: tuple[int, str] | None = None + for candidate in visible[position + 1 :]: + if candidate[1].strip(): + next_nonempty = candidate + break + if next_nonempty is None: + add(findings, "ERROR", "FIG002", line_number, "图片后缺少居中图题", alt) + else: + caption_match = caption_pattern.fullmatch(next_nonempty[1].strip()) + if not caption_match: + add(findings, "ERROR", "FIG003", next_nonempty[0], "图片后第一条非空内容必须是居中图题", next_nonempty[1]) + elif normalize_caption(caption_match.group(1)) != normalize_caption(alt): + add(findings, "ERROR", "FIG004", next_nonempty[0], "图片替代文本与图题不一致", next_nonempty[1]) + + if "核心业务流程" in alt: + if extension != ".png": + add(findings, "WARN", "FIG101", line_number, "核心业务流程建议使用 PlantUML 转换的 PNG", target) + elif not asset.with_suffix(".puml").exists(): + add(findings, "WARN", "FIG102", line_number, "核心业务流程 PNG 缺少同名 PlantUML 源文件", target) + + numbers = [number for number, _ in figures] + expected = list(range(1, len(numbers) + 1)) + if numbers != expected: + line_number = figures[0][1] if figures else 1 + add(findings, "ERROR", "FIG005", line_number, f"图号应从 1 连续递增,实际为 {numbers}") + + +def main() -> int: + parser = argparse.ArgumentParser(description="检查正式中文产品需求说明书的结构、用词、需求表和图片") + parser.add_argument("document", type=Path, help="Markdown PRD 路径") + parser.add_argument("--strict", action="store_true", help="存在 WARN 时也返回失败") + parser.add_argument("--format", choices=("text", "json"), default="text", help="输出格式") + args = parser.parse_args() + + document_path = args.document.expanduser().resolve() + if not document_path.is_file(): + print(f"ERROR FILE001: 文件不存在:{document_path}", file=sys.stderr) + return 2 + + try: + text = document_path.read_text(encoding="utf-8") + except UnicodeDecodeError: + print("ERROR FILE002: 文档必须使用 UTF-8 编码", file=sys.stderr) + return 2 + + lines = text.splitlines() + visible = visible_lines(lines) + findings: list[Finding] = [] + + check_headings(visible, findings) + check_style(visible, findings) + check_requirement_tables(visible, findings) + check_images(document_path, visible, findings) + + severity = {"ERROR": 0, "WARN": 1} + findings.sort(key=lambda item: (item.line, severity[item.level], item.code)) + errors = sum(item.level == "ERROR" for item in findings) + warnings = sum(item.level == "WARN" for item in findings) + + if args.format == "json": + print( + json.dumps( + { + "document": str(document_path), + "errors": errors, + "warnings": warnings, + "findings": [asdict(item) for item in findings], + }, + ensure_ascii=False, + indent=2, + ) + ) + else: + for item in findings: + location = f"第 {item.line} 行" if item.line else "文档" + print(f"{item.level} {item.code} {location}: {item.message}") + if item.snippet: + print(f" {item.snippet}") + if not findings: + print("PASS:未发现结构、链接或禁用表达问题。") + print(f"检查完成:{errors} 个错误,{warnings} 个警告。") + + return 1 if errors or (args.strict and warnings) else 0 + + +if __name__ == "__main__": + raise SystemExit(main())