humanize_and_verify
Rewrites AI-generated text to sound human and iteratively checks against AI detectors until the target score is met.
Instructions
Humanize, then iterate against detectors until a target score is met.
v0.2.0 (Bet 3): wraps :class:pipelines.IterativeHumanizer, which
implements the Cheng et al. 2025 detector-guided loop (research/04
section 1.3). Each iteration:
Locates the worst-scoring paragraph in the current text.
Generates
candidates_per_iterationstochastic paraphrase candidates of that paragraph (viaParaphrasePass.paraphrase_candidates).Scores each candidate, keeps the lowest, splices it back in.
Re-scores the whole text. If at or below
target_ai_score, returns.
This replaces the v0.1.0 loop, which re-ran the deterministic 9-pass
pipeline at ramped intensities. As documented in
docs/REVIEW_v0.1.0.md section 2.9, every pass except 9-heavy is
idempotent on its own output, so iterations 2-3 of the old loop did no
work. The new loop is meaningfully different because it depends on
stochastic candidate generation: only non-deterministic search can
converge on a lower score after a deterministic fixed point.
The function always returns a result, even if the target was not
reached; callers should check target_reached to know.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | The input prose to humanize. | |
| style | No | Name of a style preset; see :func:`list_styles`. | blog |
| max_iterations | No | Hard upper bound on improvement iterations. Must be >= 1. Iteration 0 is always the baseline humanization and does not count against this bound. | |
| target_ai_score | No | The aggregate probability_ai value below which the loop exits. Must be in [0, 1]. Cheng et al. 2025 use 0.15 as their stop value; we keep 0.3 ("comfortably human") as the default for backwards compatibility with v0.1.x callers. | |
| target_detector | No | Which detector field the loop optimizes against. ``"trusted_mean"`` (default) uses the suite's mean over detectors without documented bias caveats. ``"raw_mean"`` uses the unweighted mean across all detectors. Any other value is treated as a specific detector name (e.g. ``"roberta_openai"``). | trusted_mean |
| candidates_per_iteration | No | How many stochastic paraphrase candidates to generate per iteration. The Cheng et al. 2025 paper uses 3-5; we default to 3 as a quality / latency tradeoff. Must be >= 1. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | ||
| notes | No | ||
| iterations | Yes | ||
| final_score | Yes | The full output of :func:`score_humanity`. | |
| initial_score | Yes | The full output of :func:`score_humanity`. | |
| total_time_ms | No | Total wall-clock time including baseline humanization. | |
| target_reached | Yes | ||
| target_ai_score | Yes | ||
| target_detector | No | Which detector field the loop optimized against. One of "trusted_mean", "raw_mean", or a specific detector name. | trusted_mean |
| per_iteration_scores | No | Whole-text AI probability after iteration 0 (baseline) then after each accepted improvement. Empty if scoring was unavailable. | |
| candidates_per_iteration | No | How many stochastic paraphrase candidates were generated per iteration. 1 reduces the loop to deterministic paraphrasing. |