
Ai Rewriting Text
- 3 installs
- 11 repo stars
- Updated June 28, 2026
- lebsral/dspy-programming-not-prompting-lms-skills
Builds a DSPy rewriter that transforms text to a different tone, reading level, or audience, evaluated by a dual-judge metric scoring fidelity and style match.
About
Guides building a DSPy rewriter that takes source text plus a target style and audience and returns rewritten text preserving factual claims. A developer uses it for tone transformation, simplifying jargon, or matching brand voice.
- Rewriter signature preserves factual claims while changing tone and audience
- Dual-judge evaluation separately scores meaning preservation and style match
Ai Rewriting Text by the numbers
- 3 all-time installs (skills.sh)
- Ranked #1,661 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/lebsral/dspy-programming-not-prompting-lms-skills --skill ai-rewriting-textAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 11 |
| Last updated | June 28, 2026 |
| Repository | lebsral/dspy-programming-not-prompting-lms-skills ↗ |
What it does
Builds a DSPy rewriter that transforms text to a different tone, reading level, or audience, evaluated by a dual-judge metric scoring fidelity and style match.
Files
ai-rewriting-text
Rewrite existing text to match a different tone, style, reading level, or audience using DSPy. The core pattern is - source text + target style/tone/audience → rewritten text. Evaluation uses a dual-judge metric that separately scores meaning preservation (fidelity) and style match.
Step 1 - Understand the rewriting task
Before writing code, clarify:
- What text? — source content (paragraph, article, legal clause, doc page)
- What target tone/style? — casual, formal, friendly, authoritative, playful
- What audience? — developers, executives, children, general public
- Reading level target? — grade level or Flesch-Kincaid score
- How much creative liberty? — strict paraphrase vs. free rewrite
- Preserve structure? — keep headings, bullet points, paragraph breaks
Step 2 - Build basic rewriter
import dspy
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
class RewriteText(dspy.Signature):
"""Rewrite the source text to match the specified tone and audience.
Preserve all factual claims and key information from the original.
Do not add new claims or information not present in the source.
Output length should be similar to the input length unless instructed otherwise."""
source_text: str = dspy.InputField(desc="The original text to rewrite")
target_tone: str = dspy.InputField(desc="Desired tone - e.g. casual, formal, friendly, authoritative")
target_audience: str = dspy.InputField(desc="Who the rewritten text is for - e.g. developers, executives, general public")
rewritten_text: str = dspy.OutputField(desc="The rewritten text matching the specified tone and audience")
rewriter = dspy.Predict(RewriteText)
result = rewriter(
source_text="The system leverages a multi-tiered caching architecture to minimize latency.",
target_tone="casual",
target_audience="general public",
)
print(result.rewritten_text)
# → "The app stores frequently used data nearby so it loads faster for you."Step 3 - Pass style examples
Provide 2-3 examples of the target style to anchor the model's output.
class RewriteWithExamples(dspy.Signature):
"""Rewrite the source text to match the tone and style shown in the examples.
Preserve all factual claims. Do not add new information.
Output length should be similar to input length unless instructed otherwise."""
source_text: str = dspy.InputField(desc="The original text to rewrite")
target_tone: str = dspy.InputField(desc="Desired tone description")
target_audience: str = dspy.InputField(desc="Target audience")
style_examples: str = dspy.InputField(desc="2-3 example passages written in the target style, separated by ---")
rewritten_text: str = dspy.OutputField(desc="Rewritten text matching the tone and style of the examples")
rewriter = dspy.Predict(RewriteWithExamples)
style_examples = """
Our dashboard gives you a clear picture of what's happening right now.
---
Setting up takes about two minutes. No credit card, no fuss.
---
We built this for teams who'd rather ship than configure.
"""
result = rewriter(
source_text="The analytics module provides real-time visibility into system telemetry.",
target_tone="casual, direct, startup-friendly",
target_audience="small business owners",
style_examples=style_examples,
)Step 4 - Reading level adjustment
class RewriteToReadingLevel(dspy.Signature):
"""Rewrite the source text to match the specified reading level.
Use vocabulary and sentence structures appropriate for the grade level.
Preserve all key information. Do not add new claims.
Output length should be similar to input length unless instructed otherwise."""
source_text: str = dspy.InputField(desc="The original text to rewrite")
target_reading_level: str = dspy.InputField(desc="Target reading level - e.g. '5th grade', '8th grade', 'college'")
rewritten_text: str = dspy.OutputField(desc="Rewritten text at the target reading level")
rewriter = dspy.Predict(RewriteToReadingLevel)
result = rewriter(
source_text=(
"Photosynthesis is the biochemical process by which chlorophyll-containing organisms "
"convert radiant energy from solar radiation into chemical energy stored as glucose."
),
target_reading_level="5th grade",
)To measure reading level programmatically, install textstat:
import textstat
score = textstat.flesch_kincaid_grade(result.rewritten_text)
print(f"Flesch-Kincaid grade level: {score:.1f}")Step 5 - Dual-judge fidelity and style evaluation
One judge scores meaning preservation (fidelity), another scores style match.
class FidelityJudge(dspy.Signature):
"""Score how well the rewritten text preserves the meaning and key facts of the original.
Score 0-1 where 1 means all key information is preserved with no additions or omissions."""
source_text: str = dspy.InputField()
rewritten_text: str = dspy.InputField()
fidelity_score: float = dspy.OutputField(desc="Float 0-1")
reasoning: str = dspy.OutputField(desc="Brief explanation of score")
class StyleJudge(dspy.Signature):
"""Score how well the rewritten text matches the target tone and audience.
Score 0-1 where 1 means the tone and style are a perfect match."""
rewritten_text: str = dspy.InputField()
target_tone: str = dspy.InputField()
target_audience: str = dspy.InputField()
style_score: float = dspy.OutputField(desc="Float 0-1")
reasoning: str = dspy.OutputField(desc="Brief explanation of score")
fidelity_judge = dspy.Predict(FidelityJudge)
style_judge = dspy.Predict(StyleJudge)
def rewrite_metric(example, prediction, trace=None):
fidelity = fidelity_judge(
source_text=example.source_text,
rewritten_text=prediction.rewritten_text,
)
style = style_judge(
rewritten_text=prediction.rewritten_text,
target_tone=example.target_tone,
target_audience=example.target_audience,
)
return float(fidelity.fidelity_score) * float(style.style_score)Step 6 - When to rewrite vs regenerate
| Content length | Approach | Reason |
|---|---|---|
| < 200 words | Regenerate from scratch | Short enough that full regeneration is fast and clean |
| > 200 words | Rewrite preserving structure | Prevents losing details buried in long content |
| Highly technical | Rewrite with explicit preserve-facts instruction | Regeneration risks dropping precision |
| Structured (lists, tables) | Rewrite paragraph-by-paragraph | Keeps formatting intact |
| Marketing copy | Either — prefer regeneration | Creative latitude usually wanted |
Step 7 - Brand voice matching
Pass brand guidelines and example content as inputs so the model can anchor to a specific voice.
class BrandVoiceRewriter(dspy.Signature):
"""Rewrite the source text to match the brand voice described in the guidelines
and demonstrated in the brand examples. Preserve all factual information.
Do not add new claims. Match the vocabulary, sentence rhythm, and personality
shown in the examples."""
source_text: str = dspy.InputField(desc="Text to rewrite")
brand_guidelines: str = dspy.InputField(desc="Brand voice description - tone, personality, dos and donts")
brand_examples: str = dspy.InputField(desc="2-3 example passages written in the brand voice, separated by ---")
rewritten_text: str = dspy.OutputField(desc="Text rewritten in the brand voice")
rewriter = dspy.Predict(BrandVoiceRewriter)Step 8 - Evaluate and optimize
import dspy
from dspy.teleprompt import BootstrapFewShot
trainset = [
dspy.Example(
source_text="Authentication uses OAuth 2.0 with PKCE flow.",
target_tone="casual, friendly",
target_audience="non-technical users",
rewritten_text="Signing in is secure — we use industry-standard login protection behind the scenes.",
).with_inputs("source_text", "target_tone", "target_audience"),
# add more examples
]
optimizer = BootstrapFewShot(metric=rewrite_metric, max_bootstrapped_demos=3)
optimized_rewriter = optimizer.compile(dspy.Predict(RewriteText), trainset=trainset)When NOT to use AI rewriting
- Legal or regulatory text — tone changes can alter legal meaning; requires human review
- Already well-written content — if the original is clear and appropriate, rewriting adds risk
- Simple terminology swaps — use find-and-replace; AI adds unnecessary variability
- Translation between languages — use
/ai-translating-contentinstead (different task) - Content with precise numerical claims — AI can silently alter figures during rewriting
Key patterns
| Goal | Approach |
|---|---|
| Tone change | target_tone input + style examples |
| Reading level | target_reading_level + textstat measurement |
| Brand voice | Brand guidelines + example passages as inputs |
| Long content | Process paragraph-by-paragraph |
| High-fidelity | Dual-judge metric + explicit preserve-facts instruction |
| Optimization | BootstrapFewShot with composite fidelity * style metric |
Gotchas
- Claude regenerates instead of rewriting, losing specific facts — always run a fidelity judge that compares key claims between source and output; add "preserve all factual claims" to the signature docstring.
- Tone changes are inconsistent across paragraphs in long text — split content at paragraph boundaries and rewrite each chunk with the same tone instruction, then reassemble.
- Claude adds new information not in the original — put "Do not add new claims or information not present in the source" in the signature docstring; the fidelity judge will catch violations.
- Simplified text becomes much shorter, dropping important details — include "Output length should be similar to input length unless instructed otherwise" in the signature docstring.
- Style examples pulled from the wrong domain cause register mismatch — examples must match both the tone AND the domain (e.g. use software product copy as examples when rewriting software product copy, not general prose).
Cross-references
Install any skill: npx skills add lebsral/DSPy-Programming-not-prompting-LMs-skills --skill <name>/dspy-refine— iterative refinement with feedback; use when first rewrite fails fidelity or style checks and you want automatic retry with correction signals/dspy-modules— composing multi-step DSPy modules; use when chaining extraction + rewriting + validation/ai-improving-accuracy— systematic accuracy improvement techniques that apply to rewriting pipelines/ai-checking-outputs— output validation patterns; use to enforce fidelity constraints on rewritten text/ai-generating-data— generate synthetic (source, rewritten) training pairs to build a labeled trainset for optimization- Install `/ai-do` if you do not have it — it routes any AI problem to the right skill and is the fastest way to work:
npx skills add lebsral/DSPy-Programming-not-prompting-LMs-skills --skill ai-do
Additional resources
See examples.md for worked examples - technical-to-plain-English, tone adapter, and reading level adjuster.
last_audit:
date: 2026-05-04
score: 0/0
versions:
dspy: 3.2.0
[
{
"id": "technical-to-plain-english",
"description": "Rewrite technical API documentation to plain English for a non-technical audience",
"inputs": {
"source_text": "To authenticate API requests, include your Bearer token in the Authorization header. Tokens expire after 3600 seconds and must be refreshed using the /oauth/token endpoint with your client_id and client_secret.",
"target_tone": "friendly, non-technical",
"target_audience": "small business owner with no coding experience"
},
"evaluation": {
"method": "dual-judge",
"fidelity_threshold": 0.8,
"style_threshold": 0.75,
"composite_threshold": 0.65,
"checks": [
"rewritten_text does not contain 'Bearer token', 'Authorization header', 'endpoint', or 'client_id'",
"rewritten_text mentions that the access key/password expires",
"rewritten_text mentions needing to get a new key/password after expiry",
"rewritten_text length is within 50% of source_text length"
]
}
},
{
"id": "formal-to-casual-with-fidelity",
"description": "Convert a formal business report excerpt to a casual blog post tone while preserving all figures",
"inputs": {
"source_text": "Revenue for the fiscal quarter increased by 23% year-over-year, reaching $4.2M. This growth was primarily attributable to expansion within the mid-market segment and a 15% improvement in net revenue retention.",
"target_tone": "casual, conversational, founder blog post",
"target_audience": "startup founders and operators"
},
"evaluation": {
"method": "dual-judge",
"fidelity_threshold": 0.9,
"style_threshold": 0.75,
"composite_threshold": 0.7,
"checks": [
"rewritten_text contains '23%' or 'twenty-three percent'",
"rewritten_text contains '$4.2M' or '4.2 million'",
"rewritten_text contains '15%' or 'fifteen percent'",
"rewritten_text does not use 'fiscal quarter', 'year-over-year', or 'attributable'",
"rewritten_text uses at least one contraction or informal phrase"
]
}
},
{
"id": "reading-level-reduction",
"description": "Reduce a college-level science passage to 8th grade reading level while preserving the core concept",
"inputs": {
"source_text": "The mitochondria, often referred to as the powerhouse of the cell, are double-membrane-bound organelles responsible for the production of adenosine triphosphate (ATP) through oxidative phosphorylation, a process integral to cellular respiration and energy metabolism.",
"target_reading_level": "8th grade"
},
"evaluation": {
"method": "reading-level-check",
"target_flesch_kincaid_grade": 8.0,
"tolerance": 2.0,
"fidelity_threshold": 0.8,
"checks": [
"rewritten_text mentions mitochondria",
"rewritten_text conveys that mitochondria produce energy",
"rewritten_text does not contain 'oxidative phosphorylation' without explanation",
"rewritten_text does not contain 'double-membrane-bound organelles' without simplification",
"rewritten_text length is within 50% of source_text length"
]
}
}
]
ai-rewriting-text - Examples
Example 1 - Technical-to-plain-English converter
Convert developer documentation into user-friendly help articles.
import dspy
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
class TechToPlainEnglish(dspy.Signature):
"""Rewrite technical documentation as a friendly help article for non-technical users.
Replace all jargon with plain language equivalents.
Preserve every feature and instruction from the original.
Do not add new information. Keep output length similar to input length."""
technical_text: str = dspy.InputField(desc="Developer-facing documentation or technical description")
user_persona: str = dspy.InputField(desc="Who the help article is for - e.g. 'small business owner', 'first-time user'")
plain_english_text: str = dspy.OutputField(desc="User-friendly help article version of the documentation")
converter = dspy.Predict(TechToPlainEnglish)
technical_docs = [
{
"text": (
"To authenticate API requests, include your Bearer token in the Authorization header. "
"Tokens expire after 3600 seconds and must be refreshed using the /oauth/token endpoint "
"with your client_id and client_secret."
),
"persona": "small business owner with no coding experience",
},
{
"text": (
"Enable two-factor authentication (2FA) by navigating to Account Settings > Security. "
"The system supports TOTP-based authenticator apps and SMS fallback. "
"Recovery codes are generated on initial 2FA setup."
),
"persona": "general user setting up their account",
},
]
for doc in technical_docs:
result = converter(
technical_text=doc["text"],
user_persona=doc["persona"],
)
print(f"Original:\n{doc['text']}\n")
print(f"Plain English:\n{result.plain_english_text}\n")
print("---")What to expect:
- "Bearer token in the Authorization header" → "a password that proves who you are"
- "TOTP-based authenticator apps" → "an app on your phone that shows a code"
- Output length stays proportional to input
---
Example 2 - Tone adapter (formal report to casual blog post)
Transform a formal quarterly business report excerpt into a casual, readable blog post.
import dspy
from dspy.teleprompt import BootstrapFewShot
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
class ToneAdapter(dspy.Signature):
"""Rewrite the source text in the target tone while preserving all facts and figures.
Match the energy and vocabulary of the style examples provided.
Do not add new claims or omit any data points from the original.
Output length should be similar to input length."""
source_text: str = dspy.InputField(desc="Formal text to rewrite")
target_tone: str = dspy.InputField(desc="Description of target tone")
style_examples: str = dspy.InputField(desc="2-3 example passages in the target style, separated by ---")
rewritten_text: str = dspy.OutputField(desc="Rewritten text in the target tone")
# Style examples of casual, conversational blog writing
casual_style_examples = """
Q3 was a good one. Revenue climbed 18% and we finally cracked the enterprise market we'd been eyeing.
---
Here's the short version - we shipped faster, customers stuck around longer, and the team grew without breaking anything.
---
The numbers tell a clear story this quarter. Churn dropped. Signups jumped. The product finally clicked for users.
"""
formal_passages = [
(
"Revenue for the fiscal quarter increased by 23% year-over-year, reaching $4.2M. "
"This growth was primarily attributable to expansion within the mid-market segment "
"and a 15% improvement in net revenue retention."
),
(
"Customer acquisition cost (CAC) decreased by 12% relative to the prior period, "
"driven by optimization of paid acquisition channels and increased organic referral volume. "
"Payback period improved from 14 months to 11 months."
),
]
adapter = dspy.Predict(ToneAdapter)
for passage in formal_passages:
result = adapter(
source_text=passage,
target_tone="casual, conversational, founder blog post",
style_examples=casual_style_examples,
)
print(f"Formal:\n{passage}\n")
print(f"Casual:\n{result.rewritten_text}\n")
print("---")Adding a fidelity check
class FidelityJudge(dspy.Signature):
"""Score how well the rewritten text preserves all numbers, percentages, and factual claims
from the original. Score 0-1 where 1 means every data point is present and accurate."""
source_text: str = dspy.InputField()
rewritten_text: str = dspy.InputField()
fidelity_score: float = dspy.OutputField(desc="Float 0-1")
missing_or_changed: str = dspy.OutputField(desc="List any missing or altered facts, or 'none'")
judge = dspy.Predict(FidelityJudge)
for passage in formal_passages:
result = adapter(
source_text=passage,
target_tone="casual, conversational, founder blog post",
style_examples=casual_style_examples,
)
check = judge(source_text=passage, rewritten_text=result.rewritten_text)
print(f"Fidelity score - {check.fidelity_score}")
print(f"Issues - {check.missing_or_changed}\n")---
Example 3 - Reading level adjuster with measurement
Adjust a college-level passage to 8th grade reading level and verify with textstat.
import dspy
# pip install textstat
import textstat
lm = dspy.LM("openai/gpt-4o-mini") # or "anthropic/claude-sonnet-4-5-20250929", etc.
dspy.configure(lm=lm)
class ReadingLevelAdjuster(dspy.Signature):
"""Rewrite the source text to match the target reading level.
Use vocabulary, sentence length, and structure appropriate for the grade level.
Preserve all key information and concepts from the original.
Do not add new information. Keep output length similar to input length."""
source_text: str = dspy.InputField(desc="Original text at its current reading level")
target_reading_level: str = dspy.InputField(desc="Target grade level - e.g. '8th grade', '5th grade', 'college'")
rewritten_text: str = dspy.OutputField(desc="Text rewritten at the target reading level")
adjuster = dspy.Predict(ReadingLevelAdjuster)
college_level_passages = [
(
"The mitochondria, often referred to as the powerhouse of the cell, are double-membrane-bound "
"organelles responsible for the production of adenosine triphosphate (ATP) through oxidative "
"phosphorylation, a process integral to cellular respiration and energy metabolism."
),
(
"Macroeconomic policy instruments encompass fiscal and monetary mechanisms deployed by governmental "
"and central banking authorities to regulate aggregate demand, mitigate inflationary pressures, "
"and stabilize employment levels across economic cycles."
),
]
target_level = "8th grade"
for passage in college_level_passages:
original_grade = textstat.flesch_kincaid_grade(passage)
result = adjuster(
source_text=passage,
target_reading_level=target_level,
)
rewritten_grade = textstat.flesch_kincaid_grade(result.rewritten_text)
print(f"Original (grade {original_grade:.1f}):\n{passage}\n")
print(f"Rewritten (grade {rewritten_grade:.1f}):\n{result.rewritten_text}\n")
print("---")Iterative adjustment with retry
If the first rewrite misses the target grade level, retry with explicit feedback.
def adjust_to_grade(source_text: str, target_grade: float, max_attempts: int = 3) -> str:
"""Rewrite text to target grade level, retrying if the measured level is off."""
current_text = source_text
current_instruction = f"{target_grade:.0f}th grade"
for attempt in range(max_attempts):
result = adjuster(
source_text=source_text, # always rewrite from original
target_reading_level=current_instruction,
)
measured = textstat.flesch_kincaid_grade(result.rewritten_text)
if abs(measured - target_grade) <= 1.5:
print(f"Hit target on attempt {attempt + 1} (measured grade {measured:.1f})")
return result.rewritten_text
# Give corrective feedback for the next attempt
if measured > target_grade:
current_instruction = f"{target_grade:.0f}th grade - use shorter sentences and simpler words (current text is grade {measured:.1f}, too complex)"
else:
current_instruction = f"{target_grade:.0f}th grade - you can use slightly more sophisticated vocabulary (current text is grade {measured:.1f}, too simple)"
return result.rewritten_text # return best attempt
adjusted = adjust_to_grade(college_level_passages[0], target_grade=8.0)
print(adjusted)