defluff

io.github.ahmedak由 GitHub 帳號 ahmedak 發布v0.1.2更新於 Oct 24, 2026

Deterministic prose linter for LLM text — flags AI slop, filler, buzzwords, hedges, clichés.

已驗證STDIO僅桌面知識與記憶開發者工具

概覽

AI 產生的概覽

一個在本機執行的確定性文字檢查器,讓助理為文字打出 AI 套話、空話與陳腔濫調的分數,並管理用來比對的詞語清單。

可以做什麼
為一段文字計算空話密度,回傳被標記的詞語及其類別、權重與位置,並附上詞彙表版本雜湊與短文字的低信度標記。將自訂詞語加入使用者層級或專案層級的詞彙表疊加層,或封鎖例如領域術語的詞語。完全離線運作,是確定性比對器,不使用模型也不需要 API 金鑰,因此每次執行結果一致。
適用情境
適合讓助理在回傳草稿、更新日誌或文件前先自行檢查其中可刪除的空話,也適合用可固定、可重現的套話分數在 CI 中把關生成內容。由於只比對已知詞語清單,對辨識新出現的流行語幫助有限。
執行需求
透過 uvx 從含 mcp 擴充的 defluff 套件以本機程序方式執行,或安裝該套件後執行 defluff-mcp 進入點。需要 Python;未說明需要 API 金鑰、模型下載或網路存取。
安裝前請注意
slop_add 與 slop_ignore 工具會寫入磁碟上的詞彙表疊加層,位置為整台機器範圍或專案的 .defluff 目錄,變更會持續保存並可能隨儲存庫提交。此分數只是觸發提示,並非品質衡量,預設門檻被說明為暫定值;被標記的片段應人工複核,因為領域術語可能是正當內容。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 defluff,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

defluff

The deterministic slop check for AI-generated prose. Point it at a changelog, a doc, or an agent's own output and get back the filler phrases to cut — plus a CI exit code and a pinnable score, identical on every run. No model, no API key.

[CI] [PyPI] [Python 3.10+] [License: MIT] [Last commit]

[Fluffy text gets flagged: defluff lints a sentence full of buzzwords and clichés, returning the slop spans and an over-threshold score.]

Every flagged span carries no information, so cutting them loses nothing. Clean text, same tool, passes straight through:

[Clean text passes: defluff lints a plain, concrete sentence and returns a 0% slop score.]

What makes defluff worth installing over a one-off grep is the engine around the list: bring your own phrases, per-project overlays, and an MCP server your agents pick up with no wiring.


Install

bash
pip install defluff

Or on macOS/Linux via Homebrew:

bash
brew install ahmedak/defluff/defluff

That's it. No model download. No API key. Runs anywhere Python does.


Quick start

bash
# Lint a file — exit 1 on slop, 0 when cleandefluff lint essay.md
# Pipe textcat draft.md | defluff lint
# Get a bare score for scripts (0.0 – 1.0)defluff score essay.md
# Machine-readable JSON for downstream toolingdefluff lint essay.md --json

MCP server

Exposes three tools so any MCP-aware agent can self-check prose without bespoke wiring — including its own draft, before returning it.

Zero-install via uvx (recommended) — pulls the package and the mcp extra on first run:

json
{  "mcpServers": {    "defluff": {      "command": "uvx",      "args": ["--from", "defluff[mcp]", "defluff-mcp"]    }  }}

Or install it and run the entry point directly:

bash
pip install "defluff[mcp]"defluff-mcp
json
{  "mcpServers": {    "defluff": { "command": "defluff-mcp" }  }}

Published to the MCP Registry as io.github.ahmedak/defluff (see server.json).

mcp-name: io.github.ahmedak/defluff

ToolArgsReturns
slop_detecttext: strslop_score, spans (text, category, weight, offsets), categories, lexicon_version
slop_addpattern: str, category: str, scope: "user"|"project"adds a phrase to the lexicon overlay
slop_ignorepattern: str, scope: "user"|"project"suppresses a phrase (e.g. domain jargon)

Common use cases

  • Agent self-correction — call slop_detect on a draft and revise the flagged phrases before returning it. Zero wiring, one session, no second model in the loop.
  • CI gate on generated content — fail the build when an AI-drafted changelog or doc ships full of "furthermore" and "robust." Deterministic + exit codes + a pinnable lexicon is what an LLM-judge gate can't give you.
  • Writing assistant feedback — highlight the exact phrases an editor would cut, instead of a vague "this sounds AI."
  • A reward component for fine-tuning — see reward loops for caveats; on its own it's gameable.

Why defluff?

Every "AI detector" tries to classify whether text was AI-generated — a hard, unreliable problem. defluff asks a different question: does this text contain removable filler? That's deterministic, and it's true whether a human or an LLM wrote "at the end of the day."

proselint is the closest prior art — deterministic, no model — but emits yes/no warnings rather than a tunable density score, and isn't built around a list you swap, overlay, or pin.

A grep over a word list gives you raw hits. defluff gives you what you'd otherwise have to build around that list:

  • An MCP server — agents self-check with zero wiring.
  • Markdown- and code-aware — strips code fences, inline code, and URLs first.
  • Whole-word matching — "foster" won't fire inside "fostering".
  • A normalized score instead of a hit count — filler density, so one threshold works on a tweet or a 5,000-word doc.
  • Overlap handling — "at the end of the day" overlapping "end of the day" counts each word once (longest-match-wins).
  • Exit codes, JSON, and char-offset spans — drop into CI, pre-commit, and editor tooling.
  • A pinnable lexicon hash — prove the ruler didn't move between runs.

Bring your own slop

bash
defluff lint draft.md --lexicon team-slop.md
# team-slop.md- circle back- low-hanging fruit- boil the oceanparadigm shift

One phrase per line (# comments and -/* markers ignored). These layer on top of the built-in defaults and report under a neutral custom category. For real categories and per-phrase weights, use a .json list (see Lexicon overlays).

Ready-made domain packs

bash
defluff lint post.md --pack marketing-growthdefluff lint post.md --pack marketing-growth,ai-llm   # stack several
PackCatchesPackCatches
corporate-linkedinoffice jargoncrypto-web3crypto hype
startup-vcpitch-deck speakpr-press-releasepress-release boilerplate
marketing-growthhype copyacademicresearch hedging
ai-llmLLM tellswellness-selfhelpinfluencer-speak
social-mediaX/Twitter engagement-bait

List them with defluff packs. High-false-positive terms (e.g. pivot, detox) ship commented-out so they're inert until you opt in. See the packs README.

Batteries-included defaults

~130 curated patterns across five weighted categories, case-insensitive, whole-word matched:

CategoryWhat it catchesExamples
ai-vocabWords disproportionately overused by LLMsdelve, tapestry, nuanced, pivotal, robust, showcase
clicheHollow idioms that add no informationat the end of the day, move the needle, circle back, game changer
hedgeEmpty qualifiersit should be noted that, needless to say, basically, essentially
corporateBuzzword inflationleverage, synergy, actionable insights, cutting-edge, scalable
transitionFiller connectives LLMs reach for by defaultfurthermore, moreover, in conclusion, first and foremost

Rhetorical patterns (beyond the list)

Some AI tells are sentence shapes, not fixed phrases — the antithesis: it's not X, it's Y, not just a list but a runtime. A regex pattern layer catches these under a rhetoric category:

ModeWhat it looks forDefaultWhy
Compound (confident)full shape: it's not X, it's Y · not just X but Yonthe second clause proves the rhetorical move — rarely a false alarm
Fragment (guessing)bare X, not Y — e.g. a signal, not a verdictoff, --pack rhetoricalso fires on plain corrections (shipped Tuesday, not Wednesday)

Each match counts as one unit toward the score regardless of length. Turning on fragment mode changes the lexicon hash.

bash
defluff lint draft.md                  # compound antithesis caught by defaultdefluff lint draft.md --pack rhetoric  # also catch the punchy "X, not Y" fragmentdefluff lint draft.md --category rhetoric   # gate CI on antithesis alone
[Same sentence run twice: the default catches the compound antithesis; --pack rhetoric also catches the short punchy fragment form.]

Python API

python
import defluff
report = defluff.detect("It is worth noting that we should leverage synergies.")print(report.slop_score)   # 0.0 – 1.0print(report.spans)        # flagged phrase locations + categories
score = defluff.score(text)       # bare floatclean = defluff.is_slop(text)     # bool at default threshold
lex = defluff.load_lexicon()                # pin for reproducible runsscore = defluff.score(text, lexicon=lex)

SlopReport fields:

FieldTypeNotes
slop_scorefloatClamped [0, 1] — use for thresholds
slop_densityfloatRaw unclamped — better gradient for reward loops
spanslist[Span]Per hit: text, category, weight, char offsets into the cleaned text (see Limitations)
categoriesdict[str, float]Per-category density
n_wordsintToken count
low_confidenceboolTrue when n_words < 20
lexicon_versionstrSHA prefix of resolved entry set

Lexicon overlays and versioning

The bundled lexicon is the baseline; layer on top without editing the package:

bash
# "synergy" is slop on this machinedefluff lexicon add "synergy" --category corporate --scope user
# "leverage" is fine in this repo (finance context) — commit this with the repodefluff lexicon rm "leverage" --scope projectgit add .defluff/ignore.json && git commit -m "allow 'leverage' in finance context"
  • User overlay (~/.config/defluff/) — machine-wide, not committed
  • Project overlay (.defluff/ at git root) — per-repo, commit it for your whole team

Writes are atomic and cross-process locked; a corrupt overlay is warned and skipped — detect() never crashes.

Every resolved lexicon (bundled + overlays + packs) carries a short content hash, printed on every run (lexicon: 2cc05ba84457) and exposed as SlopReport.lexicon_version. Pass lexicon=defluff.load_lexicon() once and every call scores against the same ruler — pinnable for CI baselines and RL rewards, and auditable since the hash changes if and only if the resolved entry set changes. Each release ships a dated lexicon with a changelog (CHANGELOG.md); ai-vocab is expected to turn over release to release, cliche/hedge/corporate/transition are near-stable.


Reading the output and setting the threshold

  1. The spans — the exact filler phrases, with category. Deterministic, reliable. Act on these.
  2. The score — slop_score = flagged words ÷ total, weighted by category (ai-vocab counts a little more, transition a little less). 0.20 ≈ "a fifth of this text is listed filler." Usually 0.0–1.0; can edge slightly above on text that's almost nothing but slop. Instead of being a quality quantfier, its just a tripwire that drives the CI exit code.

Pick a threshold based on how filler-dense the text is allowed to be:

--thresholdMeaningGood for
0.05~5% filler — strictmarketing copy, landing pages, customer-facing text
0.08 (default)~8% filler — a tripwire for triagegeneral prose, blog drafts
0.12–0.20only flag heavy paddingtechnical docs that legitimately use robust, scalable, in order to

The default 0.08 is provisional — hand-chosen, not yet calibrated on a labeled corpus (hence the [threshold provisional] tag). For a hard CI gate, set your own threshold and suppress your domain's vocabulary first (below).


Use in CI

Technical writing — API docs, ADRs, RFCs — legitimately uses words like robust, scalable, in order to. Suppress your project's domain vocabulary first:

bash
defluff lexicon rm "scalable" --scope projectdefluff lexicon rm "in order to" --scope projectgit add .defluff/ignore.json && git commit -m "defluff: allow domain vocabulary"

Then gate only on the categories you trust, not all five:

yaml
- name: Check AI-generated content for slop  # --category ai-vocab,hedge gates only on the highest-precision categories  run: cat generated_output.md | defluff lint --category ai-vocab,hedge --threshold 0.1

Exit code 1 fails the step, 0 passes.


Use with pre-commit

yaml
# .pre-commit-config.yamlrepos:  - repo: local    hooks:      - id: defluff        name: defluff slop check        entry: defluff lint        language: system        types: [markdown]

Use in reward loops (experimental)

A deterministic, non-differentiable scalar for filler density can be a small component of a reward mix — but it's gameable alone: a model optimized purely against a fixed phrase list learns to paraphrase the filler rather than remove it. Pair it with a real quality signal (human or LLM judge); we don't yet have a published training run showing it helps.

python
lex = defluff.load_lexicon()  # pin oncereward = lambda text: -defluff.detect(text, lexicon=lex).slop_density  # unclamped, better gradient
delta = defluff.compare(draft_v1, draft_v2, lexicon=lex)# {"score_a": 0.31, "score_b": 0.18, "delta": -0.13,#  "improved": [...], "regressed": [...]}  # set diff of flagged phrases, not a semantic diff

CLI reference

bash
defluff lint [FILE] [--json] [--threshold FLOAT] [--category CATS] [--lexicon PATH] [--pack NAMES] [--no-project-overlay]defluff score [FILE] [--pack NAMES] [--no-project-overlay]defluff packs          # list bundled domain packs
defluff lexicon list   [--category CATEGORY] [--scope SCOPE] [--json]defluff lexicon add    PATTERN --category CATEGORY [--scope SCOPE] [--weight FLOAT]defluff lexicon rm     PATTERN [--scope SCOPE]
  • --category — comma-separated; only spans in those categories count toward the exit-code decision (still reports all hits). Valid: ai-vocab, cliche, hedge, corporate, transition, custom, rhetoric.
  • --lexicon PATH — layers your phrases on top of the defaults. .txt/.md is one phrase per line (lands in custom); .json carries explicit categories and weights.
  • --pack NAMES — comma-separated domain packs. rhetoric is reserved for the pattern pack, enabling the opt-in X, not Y antithesis fragment.

Exit codes for defluff lint: 0 = clean · 1 = slop · 2 = bad input.


Accuracy

defluff is a deterministic matcher, not a trained classifier, so the metric that matters is precision — when it flags something, is it actually removable filler? On a 50-example hand-labeled set (eval/validation.jsonl) spanning clear slop, clean prose, and jargon-as-content traps (e.g. "the robust standard errors", "pivotal trials"), at the default threshold:

MetricScoreReading
Precision1.000 false positives — clean prose and legitimate jargon were not flagged
Recall0.65bounded by lexicon coverage

Reproduce: python eval/score.py eval/validation.jsonl

Misses are novel buzzwords the lexicon hasn't seen yet (e.g. "operationalize the ideation funnel") — the known limit of a list-based matcher, not noise. Recall on listed filler is 1.00 and will rise as the lexicon grows, but won't reach 1.00 against open-ended novel jargon without a semantic layer.

Caveats: the set is small and labeled by the author — a sanity check on precision, not an independently adjudicated benchmark.


Limitations

  • It matches a known list by design — it doesn't understand text. Novel buzzwords are missed; this is the trade for being deterministic, local, and reproducible (no model, no API key, pinnable hash). Pair with an LLM judge if you need semantic detection of novel filler.
  • Domain jargon is contextual. "leverage" in a finance document is real content. Read the flagged spans; suppress false positives with defluff lexicon rm (adds to an ignore list, doesn't delete from the bundled lexicon).
  • Span offsets are into the cleaned text. defluff strips code fences, inline code, URLs, and markdown markup before matching, so offsets won't line up with your original document — match on span.text, not raw offsets, against marked-up source.
  • custom is read-only-via-file. Phrases from a --lexicon file land in custom, but defluff lexicon add --category custom is rejected — add only takes the five curated categories.
  • English only in v0.
  • Short texts (< 20 words) get low_confidence: true — the denominator is floored at 20 so one phrase can't read as 100% slop on a two-sentence input.

Contributing

The easiest contribution is adding a missed filler phrase:

  1. Add it to src/defluff/data/lexicon-v1.json with the right category
  2. pytest — smoke tests catch boundary errors
  3. PR with one or two examples of the phrase in the wild

See CONTRIBUTING.md for code setup and guidelines.


License

MIT

來源:README.md,提交 67c0ef3

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.1.2最新Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分