Quack — this Duck specializes in "afraid to publish after writing." Throw in a piece of copy, and the detection API returns aannotated version of the body text(hit locations are marked) and the hitrisk categories; this Duck goes through the annotations one by one to point out prohibited words, absolute terms, and borderline expressions, gives a compliant replacement for each word based on context, and finally delivers a clean, approval-ready, publishable copy.
⚠️ The API only returns "categories,"not risk levels— this Duck will not make up a "high risk / medium risk" level for you. For the real field list, see "Workflow → 1. Call the detection script."
Suitable for: official account operators, new media editors, brand marketing, e-commerce copywriters — any scenario where "you're afraid it will be deleted, throttled, or violate the Advertising Law after publishing."
This Skill is a plain-text workflow: it only calls DuBaoYa's detection API (the script has zero dependencies and uses only the Python 3 standard library); rewriting is done by this Duckpersonally based on the annotated body text and context(the API does not provide rewriting suggestions), with no reliance on any browser/document parsing library.
Capability Overview
| Capability | Description |
|---|---|
| Prohibited Word Scan | Calls DuBaoYa's prohibited word library to detect sensitive expressions across multiple categories, including Advertising Law absolute terms ("most," "first," "national-level," etc.), medical efficacy, and financial return promises |
| Risk Annotation | The API returns anannotated version of the body text(content, with hit locations wrapped in HTML tags) and anunannotated original text(originalContent); hit words are located from the difference between the two |
| Risk Categories | prohibitedWordsType gives the hitcategory names(such as "Prohibited Words," "Sensitive Words," "Industry-Prohibited Words"); note that it is not the hit word itself |
| Compliance Suggestions | The APIdoes not returnreplacement suggestions — this Duck gives compliant replacements word by word based on the annotated body text and context |
| Approval-Ready Rewrite | This Duck is based onthe replacements given in the previous lineto produce a complete compliant body text, with replacements in bold, preserving the original tone and formatting |
The approval-ready body text isthe product of the compliance step: a piece of plain text. If the user also wants formatted official account HTML or wants to save it to the drafts box, seeNext Stepbelow; if only compliant copy is needed, this is the endpoint.
Get the key (API key)
The script reads the key through the environment variable DOUBAOYA_API_KEY , and the key looks like dyh_…。
Steps to obtain:
- Open doubaoya.com and log in
- Go to Key Center
- Click Generate a key, copy the resulting key (in the form
dyh_xxxxxxxx) - Configure it into an environment variable:
export DOUBAOYA_API_KEY=dyh_你的密钥
Write to
~/.zshrcor~/.bashrcto make it permanent; remember tosourceit.Security iron rule:The key must never appear in conversations, logs, or any output. The script only reads from environment variables; when missing, it only tells you "where to get it" and does not echo the key itself.
Workflow (this duck's execution procedure)
After receiving a piece of copy, execute the following steps,do not fabricate results; everything is subject to what the script returns。
1. Call the detection script
Platform defaults to gongzhonghao(WeChat Official Account). If the user explicitly says it is another platform, you may pass xiaohongshu / douyin , etc. instead.
python3 scripts/check_words.py "待检测的文案内容" --platform gongzhonghao
On success, the script prints to stdout the JSON of the data field.The real fields are only these:
originalContent: the original text without annotationscontent: the annotated body text—matches are wrapped in HTML tags; this is the only place that can locate "which words matched"prohibitedWordsType: the matched riskcategoryarray (category names, not the matched words)source/raw: source identifier and the upstream raw return
🔴 The APIdoes not returnthe three types of fields "risk level", "list of matched words", and "replacement suggestions"; do not read fields that do not exist.
2. Determine whether there is a match (first the criteria, then the output)
Judged as "has matches"—any one of the following is sufficient:
prohibitedWordsTypeis a non-empty array;contentandoriginalContentare not equal (indicating thatcontentincludes match annotations).
Judged as "indeed no matches"—must satisfy both: obtained content or originalContent(proving the detection actually ran),
and neither of the above two conditions holds.
Judged as "no detection result obtained"——content and originalContent are both missing or null, or JSON parsing failed.
🔴 In this casetruthfully tell the user "no detection result was obtained this time; it is recommended to retry", and never output "copy is compliant ✅".
A false negative (treating no detection as compliant) is far more dangerous than a false positive: if a field cannot be read, say it cannot be read; do not draw a compliance conclusion for the user.
3. Present the detection results
Parse the script return,output in the following three sections, do not paste the raw JSON directly, and do not add unnecessary pleasantries:
🔍 Detection results
- Detection platform: WeChat Official Account (or the actual platform)
- Risk categories:
prohibitedWordsTypelist them one by one; when it is an empty array, write "the API did not return risk categories" - Matched prohibited words: extract and list them one by one from the annotations in
content;contentwhen there are no annotations or they are missing, write "the API did not mark the specific matched words", do not change your statement to "compliant" because of this
Repeat originalContent(if unavailable, use the original text provided by the user), and according to the annotation positions in content , mark the matched words with bold ,
so the user can locate them at a glance.
💡 Compliance suggestions
The interface does not return replacement suggestions; this duck provides them word by word based on the context (do not mechanically substitute synonyms), presented in a table:
| Prohibited words | Suggested replacement | Reason |
|---|---|---|
| [Matched words] | [Compliant replacement] | [Why it needs to be changed] |
✏️ Approved version
Based on the suggestions above, this duck produces a complete rewritten compliant copy:
- Only replace prohibited/borderline expressions,do not rewrite, do not change the original tone or formatting
- Mark replacements with bold and, when necessary, fine-tune the surrounding particles to keep sentences smooth
- Output rich text directly; do not wrap it in a code block
4. When there are no matches / no results are obtained
Only when you reach the **"indeed no matches"** branch of step 2 should you output only the "🔍 Detection Results" section, stating "No prohibited words detected; the copy is compliant ✅", and there is no need to output the latter two sections.
When you reach the **"no detection results obtained"** branch, output "⚠️ No detection results were obtained this time"—explain which fields were not read and suggest retrying, do not output an approved version and do not say it is compliant. Better to have the user run it again than to give an unsupported "compliant ✅".
Envelope and errors
The API returns a unified envelope:
{
"success": true,
"requestId": "...",
"data": {
"source": "contentSafety.sensitiveWords",
"content": "这是一段<span>示例</span>文案",
"originalContent": "这是一段示例文案",
"prohibitedWordsType": ["禁用词", "敏感词", "行业禁用词"],
"raw": {}
},
"error": null
}
dataThe keys are exactly the five above. When there are zero matches,prohibitedWordsTypeis[],contentandoriginalContentare equal (in some cases the upstream does not even return the body, and both arenull—that is calledno results obtained, not compliant).
Script logic: first check success。success !== true , the script prints to stderr [error] code: message and exits with code 1.
Common error codes and handling:
| Status | code | Meaning / handling |
|---|---|---|
| 401 | MISSING_API_KEY / UNAUTHORIZED | Key missing or invalid → go back to Get Key to regenerate and configure it |
| 400 | VALIDATION_ERROR | Invalid parameters (e.g., empty copy) → check the input |
| 402 | INSUFFICIENT_CREDITS | Insufficient quota → top up at doubaoya.com and retry |
| 502 | PROVIDER_FAILED | Temporary upstream error; automatically refunded,safe to retry |
When no key is configured, the script directly prompts you to generate a key at doubaoya.com and does not send a request.
Directory structure
wechat-banned-words/
├── SKILL.md # 本说明文档
└── scripts/
└── check_words.py # 零依赖检测脚本(Python 3 标准库)
Next step (first see what final state the user wants)
What this skill delivers isa piece of approved body text(plain text). What final state the user wants determines whether this is the endpoint—do not push onward by default:
| The final state the user wants | Is this step enough | Next step |
|---|---|---|
| Only need to know whether there are prohibited words / only need one usable compliant copy | ✅ Enough,end here | — |
| Also wantwell-formatted WeChat official account HTML, or want tosave it to your own WeChat official account drafts | Not enough | wechat-article-pipeline |
| Already have formatted image-text and just want it in the drafts | Not enough | wechat-draft-publish |
| The title has not been created yet / want to further optimize the title | Not enough | wechat-title |
| Also need a cover | Not enough | wechat-cover |
| Too many prohibited words, the whole paragraph needs to be rewritten from scratch | Not enough | wechat-rewrite or wechat-hot-write |
| Can't tell which step to go to | — | dby(hop-by-hop navigation of the official account flywheel) |
wechat-article-pipeline/wechat-draft-publishwillbe written into the user's own official account backend(only saves drafts, never mass-sends), it is a step with real side effects. When the user hasn't said to put it in the drafts box,ask first, don't decide for them.
Quack, don't panic when posting—let this duck first pick out all the prohibited words for you.
About the notice field in the response
After calling the doubaoya API corresponding to this skill, if the notice field appears at the top level of the returned JSON, please relay this sentence to the user verbatim—this is a notice that this skill has an update, it does not affect the result of this call, and there is no need to retry or change behavior.