关于 slop-chop
slop-chop 是文本清理命令行工具,可检测并移除当前配置文件定义的写作模式。确定性规则可独立运行,也可选择使用模型改写句子。
主要功能
- 01确定性文本清理规则
- 02自定义词汇和模式配置
- 03检查、修复与评分模式
- 04识别 Markdown 内容
- 05可选模型改写
适用场景
- 01发布前清理文本
- 02在 CI 中检查写作模式
- 03检查源代码注释
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
slop-chop
AI 写作会留下痕迹。
剔除废话。粘贴文本,就能得到读起来像是人写的内容。
无需安装即可试用:slop-chop.com 在浏览器中运行相同的引擎。
AI 写作有固定模式。它爱用 em dash,隔几句就塞进一个分号,偏爱 comprehensive 和 leverage 这类词,还会用“In summary”或“Giving it to you honestly”这样的开场白清清嗓子。
slop-chop 一次处理即可去除这些模式。你也可以提供自己的待删清单,让结果读起来像你写的,而不是聊天机器人写的。
为什么
手动清理这些内容很费劲。让模型“别用 em dash”大概管三句话,之后它就忘了。slop-chop 每次都会应用相同的清理规则。
工作原理
分为两轮处理,你可以单独运行其中任何一轮。
第一轮是规则处理,速度快且结果确定。它会替换字符、删除你标记的词、改写陈词滥调和词语、应用你自己的模式、统一拼写方言,并整理标点;无需模型、没有成本,而且每次运行都会得到相同的输出。它能识别 Markdown,因此围栏代码块和行内反引号片段会原样保留。
第二轮是可选的改写处理,会将文本交给模型处理规则无法处理的内容,例如重写句子,使其不再需要分号,或让写作风格更贴近你选择的语气。
安装
Homebrew:
brew install dcadolph/tap/slop-chop
使用 Go:
go install github.com/dcadolph/slop-chop@latest
或者克隆仓库并使用 Makefile:
git clone [email protected]:dcadolph/slop-chop.git
cd slop-chop
make install # build and install into $(go env GOPATH)/bin, version stamped
make uninstall # remove it again
不指定目标运行 make,即可查看完整列表(build、test、cover、lint、fmt、tidy、clean)。
它有效吗?
有三项测量,都可以在此仓库中复现。docs/BENCHMARK.md 介绍了方法和注意事项。
它不会影响人类写作。对 7 个语言生态系统中 886 份开源 README 进行评分,其中大多数来自自 2021 年以来没有提交记录的项目;只有 7 份达到文本读起来不再流畅的临界线。占比为 0.8%,而这正是 GitHub Action 默认判定失败的临界线。slop-chop check 的标准更严格:只要文件中出现任何一个有分量的特征,就会判定失败;同一批 README 中有 41.5% 至少包含一个这样的特征。
词语列表敌不过同义词词典。slop-chop attack 会替换文本以绕过规则,却不会改善文本;74 个词语特征中有 33 个能被绕过。而 90 个结构特征中只有 1 个能被绕过,这也是为什么句子结构在评分中权重加倍。这个数字反映的是攻击者的能力边界,而不是规则的能力边界:替换操作无法重构句子,因此本来也无法改变句子结构。至于这些规则面对被要求改写文本的模型时表现如何,目前尚无测量结果。
尚未进行能够验证这一点的测试。还没有人检查过评分结果是否符合人类读者的判断。evaldata/ 中提供了评分协议和测试工具。完成测试之前,我们只做有限的主张:相同输入会得到相同输出,而且规则所识别的模式每次都会被捕捉到。
用法
# Print the cleaned text to stdout. Your file is not changed.
slop-chop fix notes.md
# Clean the file in place, like gofmt -w.
slop-chop fix -w notes.md
# Pipe text through it
echo "In summary, a robust—and seamless—result." | slop-chop fix
# Flag slop without changing anything (exits non-zero if a finding carries score weight)
slop-chop check notes.md
# Check or fix several files at once
slop-chop check docs/intro.md docs/guide.md README.md
slop-chop fix -w docs/intro.md docs/guide.md
# Enforce a spelling variant: flag or fix the other dialect
slop-chop check --dialect american notes.md
slop-chop fix --dialect british notes.md
# Overlay a built-in pack, like corporate phrasing to plain English
slop-chop fix --preset plain notes.md
# Use your own profile
slop-chop fix --profile myprofile.json notes.md
# Get findings as JSON for other tools to read
slop-chop check --json notes.md
slop-chop check --json --pretty notes.md
# Deeper clean: rules first, then a model rewrite (needs ANTHROPIC_API_KEY)
slop-chop fix --rewrite notes.md
slop-chop fix --rewrite --verify notes.md
check --json 会向标准输出打印一个 {"findings": [...]} 对象。fix --json 会返回
{"cleaned": "...", "findings": [...], "remaining": [...], "score": {...}, "scoreAfter": {...}}。
每条发现都包含规则、匹配的文本、建议的替换内容,以及行号和列号。带有替换内容的发现会被改写,没有替换内容的则只会被标记。
remaining 是在清理后的文本上运行 check 所报告的内容,也就是没有规则能替你改写的部分。不带 --json 时,fix 会在标准错误输出一行,说明它改写了什么、留下了什么供你处理,以及分数的变化情况。
模式
check会标记发现的问题;如果某条发现带有分数权重,则以非零状态退出。 不带权重的清理项只会列出,不会导致失败。传入--strict可在发现任何问题时失败。在 CI 中使用此选项。添加--why后,每条发现都会附带一行通俗说明,指出触发了什么规则以及会如何处理;这些说明与 Web 应用中点击后显示的说明相同。fix会将清理后的文本写入标准输出,不会改动你的文件。传入-w可直接修改文件。score会衡量文本中所列配置文件包含的模式密度,范围为 0 到 100。 这是 lint 结果,不是对文本作者身份的判断,也无法将其转变为此类判断: 人们有意写作时也会使用相同的模式。低于 25 表示文本较为干净,25 到 54 表示混合,55 及以上表示此类特征密集;Web 应用显示的也是这些分段。score --by-paragraph会分别对每个段落评分,因此,即使一份文档的千字人工撰写内容中夹杂着两个生成的段落,也能显示它们的位置;此时--max会以分数最高的段落为准进行门控,而不是使用被稀释的全文分数。
源代码有单独的处理方式。对于 .go、.py、.ts 或任何其他代码文件,
check 和 score 只会读取注释,以及 Python 文件中的文档字符串。因此,注释中的流行词会在其实际行号和列号处被标记,而标识符、字符串和
格式对齐都不会产生任何影响。
fix 会直接拒绝改写代码文件,因为清理文本可能会破坏代码。你可以通过标准输入传递文件,将其有意作为纯文本读取;也可以添加 --as .go,将标准输入作为该类型的源文件读取并仅扫描其中的注释,编辑器正是通过此方式检查尚未保存的缓冲区。函数内部的注释也会像其他注释一样被扫描。没有注释的数据文件(如 .json 和 .csv)会被跳过,并附带说明。
分数
score 会给出一个 0 到 100 之间的单一数字,表示文本中启用的配置文件所列模式的密度。更改配置文件后,同一段文本的分数也会不同,这最清楚地说明了分数的含义:它表示符合某个指定规则集的程度。结构性特征在密度计算中的权重是普通特征的两倍,因为常见的句式结构即使经过同义词替换仍会保留,而列出的词语则不会。
引擎自带一个已标注的语料库,包含 AI、人类和技术类文本片段,位于
sanitize/testdata/;每次运行时,TestBenchmark 都会测量该语料库上的召回率、精确率和分数差距,因此任何削弱检测能力的改动都会导致构建失败,而不会悄然通过。docs/BENCHMARK.md 展示了具体数值、语料库构成、哪些内容会触发规则,以及分数声明的局限。分数是 lint 结果,不是作者身份判定。
slop-chop score notes.md # notes.md: 42 (mixed: 5 tells in 180 words)
slop-chop score --json notes.md # {"value":42,"scoringVersion":2,"tells":7,...}
slop-chop score --max 20 notes.md # exit non-zero when the score is above 20
--max 会将其用作门槛,因此分数超出门槛的文档会导致构建失败,方式与 check 相同。
每个分数都会注明生成该分数的评分版本。任何可能改变未改动文本分数的版本发布都会提升版本号;docs/SCORING.md 会列出变更内容,因此在某个版本上设置的门槛会一直保持原意,直到版本号变化。仓库中有一项测试:如果基准语料库的分数发生变化而版本号没有提升,测试就会失败。
结构性特征
替换词语可以捕捉 AI 写作的词汇特征。规则检查还会标记词表无法发现的 61 个结构性特征:it's not just X, it's Y 这种节奏及其缩略形式 isn't a perk. It's an expectation,以 let's dive in 开头,用 here's the thing 清场,用 The best part? 以片段形式揭晓内容,用 here are five ways 列举事项,连续出现的粗体标签项目符号和编号条目,装饰有表情符号的标题,以及如今模型会使用的、带空格的连字符表达——毕竟 em dash 已经成了明显特征。它还会捕捉聊天回复而非书面文章的语体:let me break this down、you might be wondering、here's where it gets interesting、happy coding!,以及生成式营销文案中的 say goodbye to X 和 Enter Foo, the tool that 句式。这些特征只会被标记,不会被改写,因为如何修改取决于整个句子,将由改写环节处理。在配置文件中使用 flagPatterns 字段添加自己的规则。
每当某种特征广为人知,下一个模型就会接受训练以避免它,写作手法也会转移到别处,因此默认配置文件追踪的是模型现在的写作方式,而不是它们在 2023 年的写作方式。
在 CI 中使用
添加一个工作流:当文件的 slop 分数超过 25 时,让拉取请求检查失败。所有发现仍会列在运行日志中,因此空白和标点整理问题会显示出来,但不会导致构建失败:
name: slop-chop
on: pull_request
jobs:
slop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dcadolph/[email protected]
with:
files: docs/intro.md docs/guide.md
# max: 25 # optional, the score line
# strict: "true" # optional, fail on any single finding
# scoring-version: "1" # optional, fail when the engine scores differently
# profile: myprofile.json # optional
# dialect: american # optional
# preset: plain # optional
设置 max 可调整阈值,设置为 max: 100 则关闭分数门槛。设置 strict: "true" 可像命令行中的 slop-chop check --strict 一样,在出现任何单条发现时就让检查失败。默认阈值很少会被普通的人类写作触及:基准测试中的 886 份 README 里,只有 0.8% 的分数达到或超过 25。
该 Action 会根据你指定的标签构建引擎,因此标签会将引擎及其评分版本固定在一起。当评分版本较高时,更新的标签可能会对同一文件给出不同的评分。将 scoring-version 设置为门槛所依据的版本;如果标签更新导致评分版本变化,作业就会失败,并提示查看 docs/SCORING.md,而不是悄悄改变你的门槛。
如果某条发现对你的仓库来说是误报,有两种处理方式。可以在 Markdown 或代码注释中用指令忽略这一行;也可以在仓库根目录的 .slop-chop.json 中通过 allow 列表设置一个在所有位置都不再标记的词,Action 会自动读取该文件:
We leverage the platform on purpose here.
// slop-chop-ignore-next-line
// A robust fallback for the legacy path.
{ "allow": ["leverage", "robust"] }
单独使用 slop-chop-ignore 只会忽略它所在的那一行。其他内容见 docs/PROFILE.md。
或者,让它修复文件,并将整理后的内容推回拉取请求分支:
name: slop-chop
on: pull_request
jobs:
slop:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.head_ref }}
- uses: dcadolph/[email protected]
with:
files: docs/intro.md docs/guide.md
mode: fix
commit: "true"
# message: Chop the slop # optional commit message
修复并提交工作流会将更改推回拉取请求分支,因此适用于来自同一仓库分支的拉取请求。来自 fork 的拉取请求会获得只读令牌,因此无论 permissions 块如何设置,推送都会失败并返回 403。对于来自 fork 的拉取请求,请使用上面的检查工作流:它不需要写入权限,也仍会在发现 slop 时让运行失败。要自动修复自己的分支,可以改用 push 触发器运行同一个修复并提交作业。
若要将发现显示为代码扫描警报(在拉取请求差异和 Security 标签页中),请将 sarif 设置为一个路径,并上传它写入的日志:
name: slop-chop
on: pull_request
jobs:
slop:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v7
- uses: dcadolph/[email protected]
with:
files: docs/intro.md docs/guide.md
sarif: slop-chop.sarif
- uses: github/codeql-action/upload-sarif@v4
if: ${{ !cancelled() }}
with:
sarif_file: slop-chop.sarif
category: slop-chop
即使分数门槛导致作业失败,上传步骤仍会运行,因此警报会显示在触发它们的拉取请求中。计入分数的发现会作为警告显示,其余则作为备注显示。它们都不是错误,因此默认情况下警报不会导致拉取请求检查失败,分数门槛仍是唯一门槛。公共仓库可免费上传代码扫描结果;私有仓库则需要 GitHub Code Security。来自 fork 的拉取请求会获得只读令牌,因此上传会失败。在命令行中,slop-chop check --sarif 会将相同日志写入标准输出,且可处理任意数量的文件。
若要用相同的阈值检查拉取请求标题和正文,请设置 pull-request: "true"。将其作为独立工作流运行,并使用 edited 类型,这样标题或描述编辑后就会重新检查,而无需重新运行 CI 的其他部分:
name: slop-chop pull request
on:
pull_request:
types: [opened, edited, synchronize, reopened]
jobs:
text:
if: github.event.pull_request.user.type != 'Bot'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dcadolph/[email protected]
with:
pull-request: "true"
标题和正文会作为同一段文本进行检查:标题位于第 1 行,正文从第 3 行开始,使用与文件相同的 max 和 strict 设置。文本来自事件,因此检查不需要令牌,也适用于来自 fork 的拉取请求。重新运行时读取的是运行开始时的文本,因此编辑后应以 edited 事件触发的运行为准。if 会跳过 Dependabot 和 Renovate,因为它们的正文包含上游发行说明;checkout 步骤则会让仓库的 .slop-chop.json 生效。添加 files 可在同一步骤中检查文件;也可以设置 pull-request-title 和 pull-request-body,在由其他触发器启动的工作流中检查其他文本。代码块和 HTML 注释会被跳过,因此拉取请求模板不会计入作者的内容。若要忽略某一行,作者可以在该行上方添加一条 GitHub 不会渲染的注释:``。
该 Action 使用 actions/setup-go@v7 安装 Go,该版本在 Node 24 上运行。自托管运行器需要使用 2.327.1 或更高版本。安装步骤还会为作业后续步骤设置 GOTOOLCHAIN=local,因此该作业后续的 go 命令会使用已安装的 Go,而不会下载新版本。
将其用作 Claude Code 插件

上面的循环正是关键所在:Agent 起草内容,引擎以确定性方式评判,问题反馈回来,重写后的内容会经过验证,而不是被盲目信任。slop-chop 附带 Claude Code 插件,因此助手可以替你运行工具。此仓库本身就是一个 marketplace。添加该 marketplace,然后从中安装插件:
/plugin marketplace add dcadolph/slop-chop
/plugin install slop-chop@slop-chop
slop-chop@slop-chop 采用 plugin@marketplace 的命名格式:插件名为 slop-chop,marketplace 名为 slop-chop。
插件调用的是 slop-chop 二进制文件,而非取代它,因此请先安装该二进制文件,并将其放到你的 PATH 中:
go install github.com/dcadolph/slop-chop@latest # lands in $(go env GOPATH)/bin
slop-chop --version # confirm it is on PATH
之后,插件会提供两种方式,让 Claude 使用该工具:
| 方式 | 说明 | 你要做什么 |
|---|---|---|
slop-chop skill | Claude 会在起草内容时自动使用它。 | 提供文本并要求润色。 |
/slop-chop command | 你对文件或文本调用的命令。 | 输入 /slop-chop notes.md。 |
# Let the skill decide
Clean the slop out of this before I send it: <paste your text>
# Or call the command
/slop-chop notes.md
规则检查免费。重写需要密钥,默认关闭,因此请说明何时需要更彻底的润色;如果想使用无需密钥的本地后端,也请指定后端:
Rewrite this to sound human, and use my local Ollama so it costs nothing.
docs/PLUGIN.md 是完整的插件指南,其中包括故障排除说明。
配置文件和预设
配置文件是一个 JSON 文件,其中列出了要删去的内容以及替换内容:字符、短语、单词、正则表达式、黑名单和一些开关。使用 --profile 指定配置文件,或者在代码仓库中放置 .slop-chop.json,之后从该目录下的任何目录运行时都会自动加载。如果两者都未设置,则使用内置默认配置。
预设是通过 --preset 叠加使用的精选配置包。内置配置包包括 cleaver、plain、corporate、academic、marketing、no-dashes、typography 和 procedure。cleaver 的删改力度最大:默认配置文件只会标记 leverage 和 robust 之类的流行语,而 cleaver 会将它们改写成朴素的词语;网页应用默认启用的就是它。--preset plain
会在现有配置文件的基础上,将企业化措辞改成通俗易懂的英语;其他预设则针对各自领域中常见的套话。最后两个预设用于设置破折号策略:no-dashes 会将所有冒充 em dash 的破折号改成逗号;typography 则会在有意使用排版破折号、弯引号和省略号的文本中保留它们。procedure 适用于说明内容:它会标记列表项中超过 20 个单词的句子,以及其他位置超过 25 个单词的句子;与清理规则一样,它不计入评分,因此请添加 --strict,使其触发失败。使用逗号叠加多个预设:--preset corporate,plain。
docs/PROFILE.md 是完整参考文档,涵盖每个字段、预设、拼写变体、允许列表以及行内忽略指令。
攻击它
slop-chop attack draft.md
attack 会尝试绕过你自己的规则。它会改写文本,尽可能躲过这些规则,同时不让文本写得更好,然后报告哪些特征仍然有效。
流行语可以用同义词替换来规避:74 个词汇特征中有 33 个能被绕过。90 个结构特征中只有 1 个能被绕过,因为替换词语不会重构句子,因此该项衡量的是攻击者能触及的范围,而不是某种结构的稳健程度。攻击之后,71 段文本中仍有 67 段带有特征,平均分仅从 83 降至 78。结构特征在评分中计为双倍,原因就在于此。
在你自己的配置文件上运行它,找出哪些规则会被同义词替换绕过。-w 会将规避改写保存为语料。docs/BENCHMARK.md 提供完整表格,以及有关这些结果所能证明内容的局限。
你的声音
voice 是个性化层:keep 可让你的措辞不受任何规则和预设影响,prefer 会将某个词替换为你会使用的词,而 avoid 会标记你自己的口头禅。slop-chop voice diff draft.md final.md 会读取你从草稿到最终发布版本所做的修改,并根据这些编辑内容提出条目。这是模型无法反过来提供给你的唯一声音信号。
slop-chop voice fingerprint posts/*.md 会用数字衡量鲜少有人提及的部分:句子节奏、标点习惯和语体。接着,slop-chop voice drift draft.md 会列出文本中所有不再像你风格的特征,而 --bands 2 则可将其设为团队风格的 CI 门禁。风格偏移不等于冗余内容,因此不会重写文本,也不会改变评分。docs/VOICE.md 中有完整说明。
重写环节(可选)
规则环节是确定性的,且免费。对于规则无法处理的工作,例如重构句子以免使用分号,或让文本更贴近你的风格,可以添加 --rewrite。它会先运行规则,再将结果交给模型处理。此功能需要 ANTHROPIC_API_KEY,且会产生费用,因此默认关闭。
export ANTHROPIC_API_KEY=sk-...
slop-chop fix --rewrite notes.md
slop-chop fix --rewrite --verify notes.md
在你收到回复前,系统会先进行检查。规则会再次应用于回复,系统会将回复中的代码块和关键令牌与输入进行比较,而 --verify 还会额外运行一次模型检查,标记含义上的变化。ENGINE.md 完整介绍了重写及其检查机制。
后端
重写环节默认使用 Anthropic,但添加 --provider openai 可通过 OPENAI_API_KEY 将其指向任意兼容 OpenAI 的 Chat Completions API。使用 --base-url 可将其指向本地服务,这样重写就会在你自己的机器上运行,无需密钥,也不会产生费用。
# OpenAI
OPENAI_API_KEY=sk-... slop-chop fix --rewrite --provider openai --model gpt-4o notes.md
# Local Ollama, no key, no bill
slop-chop fix --rewrite --provider openai --base-url http://localhost:11434/v1 \
--model llama3.1 notes.md
用与撰写草稿不同的供应商来重写是个好主意,因为模型不擅长发现自己的习惯用法。
将其用作 Go 库
该引擎是一个公开的包,因此 Go 程序可以在进程内处理文本,无需调用外部二进制文件,也不会向任何地方发送内容。添加方式如下:
go get github.com/dcadolph/slop-chop/sanitize
根据配置文件构建一次清理器,然后重复使用。Fix 会返回清理后的文本和发现的问题,Check 会报告问题但不修改文本,而 Score 会根据配置文件中模式出现的密度,将文本评分为 0 到 100 分。
package main
import (
"fmt"
"github.com/dcadolph/slop-chop/sanitize"
)
func main() {
s, err := sanitize.New(sanitize.DefaultProfile())
if err != nil {
panic(err)
}
clean, findings := s.Fix("In summary, the plan—which shipped—works; the results speak for themselves.")
fmt.Println(clean) // The plan, which shipped, works. The results speak for themselves.
fmt.Println(len(findings), "tells")
}
使用 sanitize.ApplyPresets 叠加内置预设,通过配置文件的 Dialect 字段强制采用拼写规范,或使用 profile.WithVoice 合并个人的 sanitize.Voice。可选的模型重写功能位于 github.com/dcadolph/slop-chop/rewrite。完整参考文档见 pkg.go.dev。
其他平台
同一引擎可在许多平台上运行。除特别注明外,均为本地运行且免费。
| 平台 | 获取方式 |
|---|---|
| Web 应用 | slop-chop.com,无需安装 |
| VS Code | 在 Visual Studio Marketplace 中搜索 slop-chop |
| Cursor、VSCodium | 在 Open VSX 中搜索 slop-chop |
| JetBrains IDE | Marketplace 插件,或使用 LSP4IJ 配合 slop-chop lsp,参见 docs/LSP.md |
| Neovim、Helix 及任何 LSP 编辑器 | slop-chop lsp,参见 docs/LSP.md |
| Obsidian | 在社区插件中搜索 slop-chop,源代码位于 obsidian/ |
| Node | npm install slop-chop-wasm |
| Go 程序 | import github.com/dcadolph/slop-chop/sanitize,参见上文 |
| Claude Desktop、Cursor 及任何 MCP 客户端 | slop-chop mcp,参见 docs/MCP.md |
| CI、Raycast、macOS、pre-commit | GitHub Action 和 integrations/ |
文档
| 文档 | 内容 |
|---|---|
| docs/PROFILE.md | 每个配置文件字段、预设、方言、允许列表和忽略项。 |
| docs/SCORING.md | 评分版本、每个版本的变更,以及如何固定门槛。 |
| docs/VOICE.md | 保留、偏好和避免的内容、如何从你的编辑中学习,以及语气指纹。 |
| docs/PLUGIN.md | Claude Code 插件:安装、技能、命令和后端。 |
| ENGINE.md | 引擎的工作原理:规则类型、执行顺序和改写。 |
状态
项目仍处于早期阶段,但核心功能已就绪。规则检查流程已构建完成并正常运行。改写流程也已构建完成,但需要 API 密钥且会产生费用,因此由 --rewrite 标志控制;免费且可预测的规则检查流程仍是默认选项。实时改写路径包含一项需要密钥的集成测试,该测试不会纳入默认构建,因此不会意外产生费用。使用 API 密钥,通过真实 API 运行该测试:
ANTHROPIC_API_KEY=sk-... go test -tags=integration ./rewrite/ -run Live -v
许可证
MIT。参见 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。