Systems Library / AI Model Setup / How to Create AI Output Style Transfer
AI Model Setup advanced

How to Create AI Output Style Transfer

Train AI to write in your exact brand voice using style examples.

Jay Banlasan

Jay Banlasan

The AI Systems Guy

Style transfer is the process of taking content and rewriting it in a specific voice. The use case I run into most often: a business has years of great content written by a specific person, then that person leaves, and every new piece of content sounds different. Style transfer solves that. You feed the AI examples of your best existing content and it learns to replicate the patterns, vocabulary, rhythm, and tone.

I have used this to replicate founder voices for ghostwritten LinkedIn posts, match a brand's newsletter tone across different writers, and maintain a consultant's teaching style across automated content. The output is not perfect on the first pass, but it is close enough that editing takes minutes instead of hours.

What You Need Before Starting

Step 1: Build a Style Analysis Function

Before you can transfer a style, you need to understand it. Use the model to analyze your writing samples and extract explicit rules.

import openai

client = openai.OpenAI(api_key="YOUR_API_KEY")

def analyze_writing_style(samples: list[str]) -> str:
    combined_samples = "\n\n---\n\n".join(samples)

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": """You are a writing style analyst. Analyze the provided writing samples and extract a precise style guide.
Cover: sentence length patterns, vocabulary level, use of questions, use of numbers/data, structural patterns, what topics are addressed directly vs avoided, tone markers, what this writer NEVER does, and 5-10 characteristic phrases or constructions."""
            },
            {
                "role": "user",
                "content": f"Analyze the writing style in these samples:\n\n{combined_samples}"
            }
        ],
        temperature=0.3
    )

    return response.choices[0].message.content

# Run once and save the analysis
WRITING_SAMPLES = [
    """Most agencies will tell you results take 90 days. That's true for some things. 
For paid ads, you should see data in 7 days and a signal in 30. If you don't have 
a signal in 30 days, the problem isn't time. It's the offer or the audience.""",

    """Stop optimizing your ads. I know that sounds backwards. But if you're changing 
variables every 3 days, you're not learning anything. Pick one thing to test. 
Run it long enough to get statistical significance. Then make one change. That's it.""",

    """The number I care about is cost per booked call, not cost per lead. 
Leads are noise. Calls are signal. If you're celebrating a $4 lead but it takes 
50 leads to book a call, your real cost is $200. Run the full funnel math."""
]

style_analysis = analyze_writing_style(WRITING_SAMPLES)
print(style_analysis)

Step 2: Build the Style System Prompt

Convert the analysis into a system prompt that drives style-consistent output.

def build_style_prompt(style_analysis: str, few_shot_examples: list[str]) -> str:
    examples_text = "\n\n".join([f"Example {i+1}:\n{ex}" for i, ex in enumerate(few_shot_examples)])

    return f"""You are a writing engine that produces content in a specific author's voice.

STYLE GUIDE (follow precisely):
{style_analysis}

WRITING EXAMPLES (study these carefully):
{examples_text}

RULES:
- Match the rhythm and sentence patterns exactly
- Use similar vocabulary level and directness
- Never use hedge words: "might", "could potentially", "it's possible"
- Never use filler openers: "Great", "Certainly", "Of course"
- Never use passive voice where active is possible
- Match the structural pattern of the examples above
- Write as if you ARE this person, not as if you're imitating them"""

STYLE_SYSTEM_PROMPT = build_style_prompt(style_analysis, WRITING_SAMPLES)

Step 3: The Style Transfer Function

Take input content and rewrite it in the target voice.

def style_transfer(
    content: str,
    style_prompt: str,
    content_type: str = "general",
    preserve_facts: bool = True
) -> str:
    preservation_note = """
IMPORTANT: Preserve all specific numbers, names, dates, and factual claims exactly. 
Only change the style and structure, not the substance.""" if preserve_facts else ""

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": style_prompt + preservation_note},
            {
                "role": "user",
                "content": f"Rewrite the following {content_type} in this exact voice. Keep all the facts but match the style perfectly:\n\n{content}"
            }
        ],
        temperature=0.5
    )

    return response.choices[0].message.content

# Usage
generic_content = """
When running paid advertising campaigns, it is important to monitor your metrics 
regularly. Cost per lead can vary depending on many factors including audience 
targeting and creative quality. You should consider testing different approaches 
to optimize your results over time.
"""

transferred = style_transfer(generic_content, STYLE_SYSTEM_PROMPT, "marketing advice")
print(transferred)

Step 4: Add a Style Consistency Scorer

Score how well the transferred output actually matches the target style.

def score_style_match(
    transferred_text: str,
    style_examples: list[str],
    scorer_model: str = "gpt-4o"
) -> dict:
    examples_text = "\n\n---\n\n".join(style_examples[:3])

    response = client.chat.completions.create(
        model=scorer_model,
        messages=[
            {
                "role": "system",
                "content": "You evaluate how well a text matches a target writing style. Score 1-10 on: tone, sentence rhythm, vocabulary level, directness, and overall match. Return JSON only."
            },
            {
                "role": "user",
                "content": f"""Target style examples:
{examples_text}

---

Text to evaluate:
{transferred_text}

Return: {{"tone": N, "rhythm": N, "vocabulary": N, "directness": N, "overall": N, "notes": "..."}}"""
            }
        ],
        temperature=0,
        response_format={"type": "json_object"}
    )

    import json
    return json.loads(response.choices[0].message.content)

score = score_style_match(transferred, WRITING_SAMPLES)
print(f"Style match score: {score['overall']}/10")
print(f"Notes: {score['notes']}")

Step 5: Build a Style Library for Multiple Voices

Manage multiple brand voices in a reusable library.

import json
from pathlib import Path

STYLES_DIR = Path("./style_library")
STYLES_DIR.mkdir(exist_ok=True)

def save_style(name: str, examples: list[str], additional_rules: str = ""):
    analysis = analyze_writing_style(examples)
    style_data = {
        "name": name,
        "analysis": analysis,
        "examples": examples,
        "additional_rules": additional_rules,
        "system_prompt": build_style_prompt(analysis + "\n\n" + additional_rules, examples)
    }
    (STYLES_DIR / f"{name}.json").write_text(json.dumps(style_data, indent=2))
    print(f"Style saved: {name}")

def load_style(name: str) -> dict:
    path = STYLES_DIR / f"{name}.json"
    if not path.exists():
        raise FileNotFoundError(f"Style not found: {name}")
    return json.loads(path.read_text())

def transfer_to_named_style(content: str, style_name: str) -> str:
    style = load_style(style_name)
    return style_transfer(content, style["system_prompt"])

# Save a style once
save_style("jay-banlasan", WRITING_SAMPLES, "Never uses em dashes. Never uses 'perhaps'.")

# Use it anywhere
result = transfer_to_named_style("Your ad results might improve with some testing.", "jay-banlasan")
print(result)

Step 6: Batch Process Content for a Migration

Rewrite an entire archive of content in a new voice.

from pathlib import Path

def batch_style_transfer(
    input_folder: str,
    output_folder: str,
    style_name: str,
    file_pattern: str = "*.txt"
) -> dict:
    input_path = Path(input_folder)
    output_path = Path(output_folder)
    output_path.mkdir(parents=True, exist_ok=True)

    style = load_style(style_name)
    files = list(input_path.glob(file_pattern))
    processed = 0
    errors = []

    for file in files:
        try:
            content = file.read_text(encoding="utf-8")
            transferred = style_transfer(content, style["system_prompt"])
            output_file = output_path / file.name
            output_file.write_text(transferred, encoding="utf-8")
            processed += 1
            print(f"Processed: {file.name}")
        except Exception as e:
            errors.append({"file": file.name, "error": str(e)})

    return {"processed": processed, "errors": len(errors), "error_details": errors}

What to Build Next

Related Reading

Want this system built for your business?

Get a free assessment. We will map every system your business needs and show you the ROI.

Get Your Free Assessment

Related Systems