Unslop for custom agents
Clarity Voicing (CV) measures the repetitive defaults in an agent's user-facing responses, turns the findings into an agent-specific voice profile, and can apply that profile to every final response through Tau.
Use CV when a custom agent is doing the right work but its final responses still sound generic, repetitive, over-structured, or unlike the person it is supposed to represent. It is especially useful when:
- the agent already has real transcripts that expose habits synthetic prompts miss;
- the same openings, summaries, handbacks, disclaimers, formatting, or jargon recur across otherwise unrelated turns;
- the agent has standing instructions or reader preferences that must be protected while the repetitive defaults are removed; or
- the voice rules must apply to every final response, not only when the model decides to load a writing skill.
Use the Tau integration when you need enforcement at delivery time. Tau
0.57.0's BeforeFinalOutput hook gives Unslop the completed, tool-free draft
before the TUI displays or persists it. The adapter runs a separate tool-free
Tau editor with the selected voice profile and returns one replacement message.
This is not a repair for incorrect reasoning, failed tool calls, missing facts, or a broken structured-output contract. The finalizer is instructed to preserve facts, decisions, refusals, evidence limits, names, dates, amounts, identifiers, URLs, code, and references. It also adds one model call to each finalized turn, so use it where consistent user-facing voice justifies that latency and cost.
Clone this fork and install Tau 0.57.0 or newer from PyPI:
git clone https://github.com/Nuosis/ClarifyVoicing.git
cd ClarifyVoicing
uv tool install --force 'tau-by-clarity>=0.57.0'
tau --versionThe optional Tau integration dependency is also recorded in
requirements-tau.txt as tau-by-clarity>=0.57.0. In a project-managed Python
environment, install it with either command:
uv add 'tau-by-clarity>=0.57.0'
# or
python3 -m pip install 'tau-by-clarity>=0.57.0'Python 3.10 or newer runs both unslop.py and the hook adapter. Claude Code is
required only when generating or analyzing a profile; applying an existing
profile uses Python and the installed Tau executable.
Start with a profile under profiles/, or generate one from the agent's own
output using the workflow below. Copy the chosen profile into a stable path
owned by the custom agent:
mkdir -p /absolute/path/to/custom-agent/.tau
cp profiles/writing.md /absolute/path/to/custom-agent/.tau/voice-profile.mdUse a profile derived for that agent and reader whenever possible. A broad writing profile can remove common defaults, but it cannot encode a particular agent's role, vocabulary, or the reader's stated preferences.
From the ClarifyVoicing repository, run:
python3 hooks/tau/install.py \
--agent-root /absolute/path/to/custom-agent \
--profile /absolute/path/to/custom-agent/.tau/voice-profile.md \
--tau-command "$(command -v tau)" \
--provider openai \
--model gpt-5.4-mini \
--thinking low--agent-root is the directory from which the custom agent runs. The installer
merges a command named unslop_before_final_output into
<agent-root>/.tau/hooks.json; it preserves unrelated hooks and replaces an
older Unslop hook when rerun.
The provider, model, and thinking flags configure the separate finalizer call, not the custom agent's main model. Choose a combination supported by the local Tau installation. The finalizer inherits Tau authentication from the calling agent, but runs without tools, extensions, skills, context-file discovery, or a session of its own. It uses a temporary home and working directory so it cannot rediscover and recursively call the custom agent's hook.
The generated hook command contains absolute paths to this repository and the profile. Rerun the installer after moving either one.
Launch Tau from the agent root as usual:
cd /absolute/path/to/custom-agent
tauExercise a response that contains facts which must survive and voice patterns the profile should remove. Confirm both of these outputs:
- The Tau TUI shows one rewritten assistant response with every fact intact.
- The new Tau session JSONL contains the same rewritten response and does not contain an emitted copy of the original draft.
Hook failures are fail-open: Tau delivers the original draft instead of
suppressing the response. Test installation in a temporary copy of the agent
before enabling it in a deployed agent. The paired baseline/hooked protocol,
artifact verifier, and latest local result are under
evals/tau-before-final-output/.
For Claude Code or Codex transcripts, harvest only the final messages a person actually saw, then analyze them with the agent's standing instructions and the reader's preferences:
python3 unslop.py \
--domain "customer-facing responses from MyAgent" \
--corpus codex \
--corpus-root /absolute/path/to/transcripts \
--directives /absolute/path/to/custom-agent/.tau/SYSTEM.md \
--preferences /absolute/path/to/reader-preferences.md \
--output ./my-agent-voiceSupported transcript adapters are codex and claude-code. For another
transcript format, add an adapter to harvest.py that emits source, project,
session, ts, and text, or use the synthetic workflow below.
--directives protects behavior the agent was explicitly told to use.
--preferences supplies positive voice requirements from the reader. Without
preferences, Unslop creates preference-request.md with an original and rewrite
for the reader to judge; the profile is not validated until that question is
resolved.
When no production corpus exists, let Unslop generate a prompt set and samples through Claude Code:
python3 -m venv .venv
source .venv/bin/activate
python3 unslop.py \
--domain "customer-facing responses from MyAgent" \
--directives /absolute/path/to/custom-agent/.tau/SYSTEM.md \
--preferences /absolute/path/to/reader-preferences.mdFor visual work, install Playwright and select the visual pipeline:
pip install playwright
playwright install chromium
python3 unslop.py --domain "SaaS landing pages" --type visual --count 20 --concurrency 3The analysis counts lexically detectable patterns across the corpus and uses a separate judged pass for problems such as conceptual repetition that regexes cannot detect. Review the evidence before installing the generated profile; frequent language can be required behavior rather than a defect.
Results land in the selected output directory, ./unslop-output/ by default:
unslop-output/
skill.md
analysis.md
judged-*.md # when the judged pass is enabled
preference-request.md # when no preferences were supplied
prompts.json # synthetic runs
samples/
screenshots/ # visual runs
before-after/ # synthetic runs unless skipped
before.md / before.html
after.md / after.html
before.png / after.png
skill.md is the generated profile. Read analysis.md and the judged reports
to confirm that its rules come from observed patterns. For corpus runs, inspect
preference-request.md to ensure the rewrite preserved every number,
identifier, caveat, and decision. Copy the accepted skill.md to the stable
agent-local profile path used by the hook installer.
python3 unslop.py --domain "marketing emails" --count 50 --concurrency 5
python3 unslop.py --domain "Python tutorials" --count 100 --concurrency 10
python3 unslop.py --domain "React landing pages" --type visual --model sonnet --effort low
python3 unslop.py --domain "agent final responses" --corpus claude-code --corpus-root ~/.claude/projects| Flag | Default | Description |
|---|---|---|
--domain |
required | Domain or agent-output class to analyze |
--type |
text |
text for writing/code/prose; visual for websites/HTML |
--count |
50 |
Samples to generate or maximum harvested messages |
--concurrency |
5 |
Parallel Claude Code calls |
--model |
Claude default | Claude model alias/name used during profile generation |
--effort |
Claude default | Claude effort level used during profile generation |
--timeout |
600 |
Seconds allowed for each Claude call |
--analysis-timeout |
1800 |
Seconds allowed for an analysis pass |
--retries |
1 |
Retries for each failed Claude call |
--output |
./unslop-output |
Generated artifact directory |
--skip-comparison |
false |
Skip the synthetic before/after comparison |
--corpus |
none | Harvest codex or claude-code final responses instead of generating samples |
--corpus-root |
adapter default | Transcript root used by the selected corpus adapter |
--judge-shards |
3 |
Shards for the judged-pattern pass; 0 disables it |
--directives |
none | Repeatable file or directory containing standing agent instructions |
--preferences |
none | Repeatable file containing the reader's positive voice requirements |
Replace the placeholders and send this instruction to the agent responsible for the custom agent repository:
Clone https://github.com/Nuosis/ClarifyVoicing.git, read README.md and skills/unslop/SKILL.md, derive or select a voice profile for AGENT_ROOT using its real final-response transcripts, standing directives, and reader preferences, install that profile with hooks/tau/install.py, verify the result through a paired Tau TUI baseline and hooked run, and return the profile, measured patterns, hook path, and verification artifacts.
The profile is portable markdown; enforcement is runtime-specific. For a custom
Tau agent, keep the profile separate from the agent's task instructions and
apply it with BeforeFinalOutput so voice policy runs on every delivered
response. Do not package an always-on voice profile as a model-invoked skill,
because the model can decide not to load it on the turns that need it.
MIT