现在很多人都知道 Trae 这种本地化 AI 软件,也多多少少听过技能之类的概念。但一看到这些词,第一反应就是这是专业的存在,需要去找现成的、别人做好的拿来用,默认这些东西自己造不了。
其实不是。
市场上确实有专门的 Skill 网站,比如我试过从这些网站下载过一个叫 humanizer-zh 的技能。但用下来发现,别人造的东西总差点意思,想改还改不了,因为那是别人写的,思路框架都是限定的,要调整还不如重写。
重写?是的,你可以重写。
今天我要介绍的,是一个很简单的 Skill。它能自动检查 AI 输出的内容,把你认为的 AI用语改掉,只保留人话。没错,这就是上面的humanizer-zh的自定义版。相信你如果跟练,应该就学会了自建一个skill了。
如果你读过我之前的博客,你大概能猜到,我之前也试图用过其他的方法去解决这个问题。先是往项目的 memory 里写过禁用词清单,后来又往 rules 文件里写了一版。但 memory 不是每次开对话都能被完整加载,rules 虽然每次都读,但它本质上还是纯文本约束,AI 可能略读漏看,也可能看到了但执行的时候打折扣。
最后发现还得建一个 Skill,全篇扫,自动改,循环到全文通过才交付。
所以,今天我们就以这个自定义去除AI味的场景为例子,造一个Skill。相信你学完这个方法,结合你自己的识别清单,不仅能够建一个属于你的去除AI味的Skill,还能根据需要自定义其他场景的Skill,让Skill对你而言,不再是只能等别人造好,自己却发挥不了效果或者用着难受的高大上的存在。
Skill 到底是什么
Skill 翻译过来就是技能。在 Trae 里,Skill 本质上就是一个文件夹,里面放两样东西。
一个是 SKILL.md,告诉 AI 这个技能是干什么的,什么时候该用它,用的时候按什么步骤走。另一个是脚本或工具,真正干活的东西。
Rules 和 Skill 有个关键区别。Rules 是项目的全局约束,每次开新对话都会自动加载进来,内容是所有对话都必须遵守的规矩。Skill 是场景化的功能包,只有当 AI 认为当前任务匹配 Skill 的描述时才会触发,内容是一套可执行的操作流程。
打个比方,rules 就像公司的员工手册,每个人上班第一天就会拿到,里面写着不许迟到、不许泄密。Skill 就像一个办公设备的操作指南,只有当你要用那台设备的时候才会拿出来看,里面写着开机、设置、关机的具体步骤。
Memory 跟 Skill 也不一样。Memory 是 Trae 自动记的背景笔记,可能记不全、可能记歪了、也不是每次都能被完整加载。Skill 是你主动写的、结构化的、每次匹配到场景都会被完整读一遍的执行方案。
所以从约束 AI 的效果来看,rules 是基础,脚本是硬手段,Skill 则是把这两者组织成可自动触发的完整单元。
手把手造第一个 Skill
我们要造的这个 Skill,就是在内容交付前扫一遍,把你自己认定的 AI 套话词和格式问题揪出来。整个 skill 一共四个文件,全部贴出来,你可以直接抄。
第一步:建文件夹
在项目根目录的.trae/skills/下面建这样的目录结构。
.trae/skills/ai-tone-check/ │ ├── SKILL.md ← Skill 的说明书,写给 AI 看的 │ └── scripts/ │ ├── check_prohibited.py ← 核心检查脚本,扫文件、匹配问题、输出报告 │ └── add_prohibited.py ← 辅助脚本,往 rules 里加新禁用词就这么简单,Skill 不需要注册、不需要上传、不需要配置,只要放在.trae/skills/下面,Trae 就自动能识别到。
另外还有一个东西,skill 本身不包含,但它依赖项目里的 rules 文件。脚本每次运行时,会自动去读.trae/rules/写作规则.md里的禁用词段落,把最新词表提取出来。所以你需要确保 rules 文件里有一段这样的内容:
## 禁用词 - AI 套话禁用词:综上所述、不难发现、值得注意的是、值得一提的是、可以看出、由此可见 - 其他禁用词:(写你自己的,比如产品名、竞品名等)词表不硬编码在脚本里,改 rules 文件就同步了,脚本不用动。这是整个 skill 设计的一个小技巧。
第二步:写 SKILL.md
SKILL.md 是给 AI 看的说明书。Trae 每次开新对话,会把这个文件连同 rules 一起塞给 AI。它的结构分两部分:YAML 头部(告诉 Trae 这是谁、干什么的)和 Markdown 正文(告诉 AI 具体怎么执行)。
--- name: ai-tone-check description: "内容交付前的 AI 套话和格式硬性检查。写完内容准备交付前必跑。" supported_os: - windows --- # ai-tone-check 内容交付前的 AI 套话和格式硬性检查,以及禁用词词库的扩展管理。 ## 功能一:交付前检查 触发时机:写完内容、准备交付前。 执行步骤: 第一步,运行检查脚本: ``` python ".trae/skills/ai-tone-check/scripts/check_prohibited.py" "<目标文件路径>" ``` 第二步,AI 逐条判断输出: - 通过 → 无问题,进入第三步 - 有问题 → 逐条判断: - 真问题 → 修改,改完重跑脚本,直到通过 - 误报(代码里的英文直引号、反引号里的命令、用户明确要求保留的词)→ 跳过但**必须在对话里向用户说明哪条是误报、为什么跳过** 第三步,交付。脚本判定通过(或误报已确认)→ 正式交付。 ## 功能二:新增禁用词 用户口述了一个新的禁用词时(比如「以后不许再用XX这个词」),不要手动改 rules 文件,直接跑辅助脚本: ``` python ".trae/skills/ai-tone-check/scripts/add_prohibited.py" <类别> <新词> ``` 类别有两个:`ai-talk`(AI 套话)和 `custom`(自定义)。脚本会自动把新词插进 rules 文件对应的顿号序列里,保持原有格式不变。头部的 description 最关键。AI 靠这段文字判断什么时候该调用这个 Skill,写得越清楚,触发得越准。
第三步:放脚本
两个 Python 脚本,直接放进scripts/子文件夹里就行。
check_prohibited.py(核心检查脚本)
""" 内容禁用词与格式检查脚本 用法:python check_prohibited.py <文件路径> 禁用词清单不硬编码,每次运行时从 .trae/rules/写作规则.md 的 ## 禁用词 段落自动提取,保持和规则文件一致。 扫描项目: 1. 禁用词(来自规则文件) 2. 破折号(—— 和 —) 3. 英文直双引号 "…" 4. 英文直单引号 '…' """importsysimportrefrompathlibimportPath# ———————————————— 禁用词从规则文件自动提取 ————————————————defextract_prohibited_words(rules_path:Path)->list:"""从规则文件 ## 禁用词 段解析所有禁用词。"""text=rules_path.read_text(encoding="utf-8")lines=text.splitlines()words=[]in_section=Falseforlineinlines:stripped=line.strip()ifstripped.startswith("## 禁用词"):in_section=Truecontinueifin_sectionandstripped.startswith("## "):breakifnotin_sectionornotstripped.startswith("- "):continuecontent=stripped[2:]if":"notincontentand":"notincontent:continuecolon=content.find(":")if":"incontentelsecontent.find(":")rest=content[colon+1:]if"("inrest:rest=rest.split("(")[0]if"("inrest:rest=rest.split("(")[0]forwinre.split(r"[、,,]",rest):w=w.strip()ifw:words.append(w)returnwords# ———————————————— 硬性格式检查(不依赖规则文件)——————————————PATTERNS=[("中文破折号——",re.compile(r"——")),("半角破折号\u2014",re.compile(r"\u2014")),("半角短破折号\u2013",re.compile(r"\u2013")),("英文直双引号",re.compile(r'"[^"]*"')),("英文直单引号",re.compile(r"'[^']*'")),]# ———————————————— 主流程 ————————————————defcheck_file(file_path:Path,prohibited_words:list):ifnotfile_path.exists():print(f"[ERROR] 文件不存在:{file_path}")sys.exit(1)text=file_path.read_text(encoding="utf-8")lines=text.splitlines()issues=[]foridx,lineinenumerate(lines,start=1):forwordinprohibited_words:ifwordinline:issues.append(("禁用词",idx,word))forlabel,patterninPATTERNS:foridx,lineinenumerate(lines,start=1):forminpattern.finditer(line):issues.append((label,idx,m.group(0)))ifnotissues:print(f"[PASS]{file_path.name}—— 未发现禁用词或格式问题")returnprint(f"[WARN]{file_path.name}—— 共发现{len(issues)}处可疑项:\n")by_cat={}forcat,lineno,detailinissues:by_cat.setdefault(cat,[]).append((lineno,detail))forcat,hitsinby_cat.items():print(f" 【{cat}】")forlineno,detailinhits:print(f" 第{lineno}行:{detail}")print()if__name__=="__main__":script_dir=Path(__file__).resolve().parent rules_path=script_dir.parents[2]/"rules"/"写作规则.md"iflen(sys.argv)<2:print("用法:python check_prohibited.py <文件路径>")print(f"规则文件:{rules_path}")sys.exit(1)prohibited_words=extract_prohibited_words(rules_path)print(f"(已从规则文件提取{len(prohibited_words)}个禁用词:{'、'.join(prohibited_words)})\n")target=Path(sys.argv[1]).resolve()check_file(target,prohibited_words)add_prohibited.py(辅助加词脚本)
""" 添加新禁用词的辅助脚本 用法:python add_prohibited.py <类别> <新词> 类别(两个选项): ai-talk —— AI 套话类 custom —— 自定义禁用词类 工作内容: 1. 把新禁用词加到 .trae/rules/写作规则.md 对应的那一行 2. 打印确认信息 示例: python add_prohibited.py ai-talk 离谱 python add_prohibited.py custom 竞品名 """importsysimportrefrompathlibimportPath SCRIPT_DIR=Path(__file__).resolve().parent RULES_PATH=SCRIPT_DIR.parents[2]/"rules"/"写作规则.md"CATEGORY_MAP={"ai-talk":"AI 套话","custom":"其他",}deffind_line_index(text:str,keyword:str)->int:"""在规则文件里找到对应类别的那一行。"""lines=text.splitlines()fori,lineinenumerate(lines):stripped=line.strip()ifstripped.startswith("- ")andkeyword+"禁用词"instripped:returnireturn-1defadd_word_to_rules(word:str,category:str)->bool:"""把新词加到规则文件对应那一行,保持顿号分隔。"""text=RULES_PATH.read_text(encoding="utf-8")keyword=CATEGORY_MAP[category]idx=find_line_index(text,keyword)ifidx<0:print(f"[ERROR] 规则文件里找不到「{keyword}禁用词」那一行")returnFalselines=text.splitlines()line=lines[idx]colon=line.find(":")ifcolon<0:colon=line.find(":")ifcolon<0:print(f"[ERROR] 那一行找不到冒号")returnFalsebefore=line[:colon+1]rest=line[colon+1:]if"("inrest:paren_pos=rest.find("(")middle=rest[:paren_pos].rstrip()after=rest[paren_pos:]elif"("inrest:paren_pos=rest.find("(")middle=rest[:paren_pos].rstrip()after=rest[paren_pos:]else:middle=rest.rstrip()after=""existing=set(re.split(r"[、,,]",middle))ifwordinexisting:print(f"[SKIP] 规则文件里已经有「{word}」了")returnFalsenew_middle=middle.rstrip("、,,")+"、"+word new_line=before+new_middle+after lines[idx]=new_line RULES_PATH.write_text("\n".join(lines)+"\n",encoding="utf-8")print(f"[OK] 已添加到规则文件「{keyword}禁用词」:{word}")returnTrueif__name__=="__main__":iflen(sys.argv)!=3:print(__doc__)sys.exit(1)category=sys.argv[1]word=sys.argv[2]ifcategorynotinCATEGORY_MAP:print(f"[ERROR] 类别无效,可选:ai-talk / custom")sys.exit(1)add_word_to_rules(word,category)两个脚本加起来,整个去 AI 味的工作流就通了。
第四步:测试和迭代
写完这三样东西,自定义的技能就可以用起来了
第一,写进 Rules。你可以把「文档写完必须跑某个检查」这种触发条件直接写进项目的 rules 文件里。这样 AI 每次开对话都能看到,主动就会去加载对应的 Skill 正文然后执行。比如我自己的项目里,rules 文件末尾有一段交付前的硬性要求,我明确写了「写完必须跑检查脚本」,AI 每次看到就知道什么时候要动。
第二,你主动触发。写完东西之后,直接在对话框里打个斜杠加 Skill 的名字,比如/ai-tone-check,AI 立刻就知道你要干什么,直接加载对应正文开始执行。
第三,自然语言触发。你不说 Skill 名字,就说一句普通的话,比如"帮我看看这篇有没有问题"。AI 接收后,会自己扫一遍可用 Skill 的 description,匹配上了就主动加载正文开始跑。这条能不能触发取决于 description 写得清不清,所以有时候灵有时候不灵。
最后
所以,不要把Skill看成一般人无法上手的东西。你完全可以自己造一个适用于自己的。如果你觉得有难度,不要忘了你可以直接把这篇文章的链接发给你的AI,让它直接替你动手哟。