AI/TLDR

What Is TextGrad? Autograd for Text Prompts

You will understand how TextGrad borrows the backpropagation idea to optimize prompts, using LLM-written natural-language feedback as gradients that push each component to improve.

ADVANCED11 MIN READUPDATED 2026-06-14

In plain English

When you train a neural network, you don't tune its millions of numbers by hand. You run an input forward, measure how wrong the output was, and then backpropagate that error so every weight learns which direction to move. That feedback signal is the gradient. TextGrad asks a simple but radical question: what if the thing you're optimizing isn't a number, but a piece of text — a prompt, an instruction, a snippet of code — and the gradient is also text?

TextGrad — illustration
TextGrad — headstorm.ai

TextGrad is a framework that treats an LLM pipeline like a computation graph and optimizes the text inside it the way autograd optimizes weights. Instead of a numeric gradient, it uses a textual gradient: a natural-language critique, written by an LLM, that says what went wrong and which way to push. "Your answer missed the units in step 3" is a gradient. The framework propagates that feedback backward through the pipeline and edits each text component to be a little better.

Here is the everyday analogy. Imagine a writer handing a draft to an editor. The editor doesn't hand back a number — they hand back margin notes: "this paragraph is vague," "cut the jargon here," "you contradicted yourself." Those notes are gradients in plain English. The writer applies them and the draft improves. Now chain three writers (research → outline → prose) and let the editor's notes flow backward through all three: the prose editor's complaint becomes feedback for the outliner, whose complaint becomes feedback for the researcher. That backward flow of written critique is exactly what TextGrad automates.

Why it matters

Most people improve a prompt by hand: tweak a word, run it, eyeball the output, tweak again. This works for one prompt but falls apart the moment you have a pipeline — a retriever feeding a reasoner feeding a formatter — because a bad final answer could be any link's fault, and you have no principled way to assign blame. TextGrad solves the credit-assignment problem the same way backpropagation did for neural nets: it pushes blame backward, component by component.

  • It automates the iteration loop. Hand-tuning prompts is slow, subjective, and doesn't scale past a handful of variants. TextGrad runs the critique-and-revise loop for you, the way an optimizer runs gradient descent for you.
  • It optimizes whole pipelines, not just one prompt. Because feedback propagates backward through every text node, you can improve a multi-step system end-to-end from a single loss signal at the output — the part humans are worst at debugging.
  • The objects it optimizes are general. A "variable" in TextGrad can be a system prompt, a reasoning instruction, a chunk of code, a molecule description, or a candidate answer. Anything expressible as text can be a variable that receives gradients.
  • It is framework-agnostic and model-agnostic. TextGrad sits above your model calls. The same loop works whether the engine generating gradients is Claude, GPT, or an open model, because the gradient is just a prompt asking the model to critique.

Who should care? Anyone building beyond a single chat box: teams optimizing prompt pipelines, researchers tuning reasoning chains, and engineers who want a systematic alternative to vibes-based prompt editing. It belongs to the same wave as DSPy — both reframe "prompt engineering" as "program optimization" — but TextGrad's distinctive bet is that the optimization signal itself should be human-readable natural language.

How it works

TextGrad runs the same four-beat loop as numeric gradient descent, but every quantity is text. You define variables (the prompts/answers you want to optimize), run a forward pass to get an output, compute a loss (a text judgment of how good the output is), call .backward() to generate textual gradients, and then let an optimizer rewrite each variable using its gradient. Repeat.

The four pieces

  • Variable — a piece of text marked requires_grad. This is what changes. A system prompt, a solution draft, a code snippet.
  • Engine — the LLM that powers both the forward pass and the gradient generation. You can use one model to answer and a stronger one to critique.
  • Loss — itself produced by an LLM. Instead of a number, the loss is a natural-language evaluation: "the answer is correct but ignores the constraint about positive integers." This is the LLM-as-a-judge idea used as an objective function.
  • Textual gradient — the critique that flows backward from the loss to each variable, phrased as how this specific variable should change to reduce the loss.

What "backward" actually does

In a numeric net, .backward() applies the chain rule to multiply local derivatives. In TextGrad, .backward() runs an LLM prompt at each node that asks, in effect: "Given this downstream feedback, what is wrong with this particular input, and how should it change?" The model's answer is the gradient for that node. When a node has an upstream parent, TextGrad passes the child's critique into the parent's gradient prompt — that is the textual version of the chain rule. Feedback flows from the loss, back through the answer, back through the reasoning prompt, back to the system prompt.

The optimizer (TextGrad calls it TGD, Textual Gradient Descent) then takes each variable plus its accumulated textual gradient and asks the engine to produce a revised version that addresses the feedback. One full cycle nudges every optimizable text in the system toward a lower loss — and because everything is language, you can read the gradients and watch why each edit happened.

A worked example in code

The library's whole appeal is that it looks like PyTorch. Here is the canonical shape of an optimization: wrap an input as a Variable, run it through an LLM, score it with a loss, then backward() and step().

optimize_a_solution.pypython
import textgrad as tg

tg.set_backward_engine("a-strong-model")   # model that writes the gradients
model = tg.BlackboxLLM("a-fast-model")      # model that produces answers

# The text we want to improve. requires_grad=True = "optimize this".
solution = tg.Variable(
    "The answer is 3 because 3 squared is 6.",
    requires_grad=True,
    role_description="a candidate math solution",
)

# The loss is itself an LLM judging the solution against an instruction.
loss_fn = tg.TextLoss(
    "Evaluate whether the solution is correct and well-reasoned. "
    "Point out any arithmetic or logical error."
)

optimizer = tg.TGD(parameters=[solution])

for step in range(3):
    loss = loss_fn(solution)   # forward: get a textual critique
    loss.backward()            # backward: turn critique into a gradient
    optimizer.step()           # rewrite `solution` using its gradient
    print(solution.value)      # watch the text improve each round

Swap solution for a system prompt and feed the loss real training examples, and the same three lines become prompt optimization: the gradient now reads like "the prompt should tell the model to show intermediate steps," and after a few epochs the prompt has rewritten itself to do exactly that. This is the practical workhorse use of TextGrad — closely related to meta-prompting, where an LLM writes prompts for you, but with a structured backward-pass loop wrapped around it.

TextGrad vs DSPy vs hand-tuning

TextGrad is most often compared to DSPy, the other major "compile your prompts instead of writing them" framework. They share a goal — stop hand-editing prompts — but optimize differently. And both differ sharply from the manual loop most people still use.

AspectHand-tuningDSPyTextGrad
Optimization signalYour intuitionMetric score on examplesNatural-language critique (textual gradient)
Mental modelTrial and errorCompiler + few-shot searchBackprop / autograd for text
What it tunesOne prompt at a timePrompts + few-shot demosAny text node in a graph
Readability of the signalN/AA numberHuman-readable feedback
Needs labeled dataNoUsually yesA judge or examples, often light

The key contrast: DSPy mostly searches for good few-shot examples and instructions guided by a numeric metric, while TextGrad propagates written explanations of failure and edits the text directly. DSPy's signal is "score went from 0.71 to 0.78"; TextGrad's signal is "the answer ignored the second constraint, so the prompt should mention it explicitly." Neither is strictly better — DSPy's numeric metric is sturdier and less noisy, TextGrad's textual gradient is richer and explains itself. They are increasingly used together.

Pitfalls and practical tips

TextGrad is powerful but it inherits every quirk of using an LLM as an optimizer. The gradients are only as good as the model writing them, and the loop can wander if you're not careful.

  • Cost adds up fast. Every forward pass, every loss, and every backward step is one or more LLM calls. A three-step pipeline over a few epochs can be dozens of calls per example. Budget for it and cache aggressively.
  • Noisy gradients. Because the gradient is generated text, the same critique can vary run to run. Two fixes mirror classic training: use a stronger model as the backward engine, and optimize over a batch of examples so idiosyncratic feedback averages out.
  • The judge is your objective — and it can be gamed. If your loss prompt is vague, the optimizer will happily satisfy the letter of it while drifting from your real intent. Spend effort on the loss/judge wording; it is the thing you are actually optimizing toward.
  • Overfitting to the validation prompt. Run too many epochs against one judge and the variable starts pandering to that specific judge. Hold out test examples, exactly as you would when A/B testing prompts.
  • It can diverge. Unlike a tiny numeric step, a text rewrite can swing wildly. Keep the best version seen so far (a momentum/validation check) rather than blindly accepting the last step.

Going deeper

Once the basic loop clicks, the interesting part of TextGrad is how far the autograd analogy stretches — and where it stops being a perfect mirror of numeric optimization.

It is not just for prompts. The Nature paper applies the same machinery to problems with no obvious prompt at all: refining solutions to hard reasoning problems at inference time ("test-time" optimization of a single answer), improving code by treating the code as the variable and a test suite's output as the loss, and even tuning molecule or treatment-plan descriptions in scientific settings. The unifying idea is that any system built from composable LLM calls is a differentiable-by-text computation graph.

Instance optimization vs prompt optimization. Two modes are worth separating. In prompt optimization you fix the test examples and improve a shared prompt that generalizes — like training. In instance optimization you improve a single output for a single input, in place — like solving one hard problem really well. Same .backward() and .step(), different variable being optimized.

Where the analogy leaks. Real gradients compose cleanly via the chain rule; textual gradients are approximate, stochastic, and can contradict each other across a batch. There is no guaranteed convergence and no true learning rate — the "step size" is however much the optimizer model decides to rewrite. So treat TextGrad as a powerful heuristic search guided by rich feedback, not as a theorem-backed descent. In practice that means more validation, more batching, and more attention to your judge than a numeric optimizer would demand.

Where to go next. Pair TextGrad with a solid evaluation harness so your loss reflects real quality, version the prompts it produces (see prompt management), and compare its output head-to-head against your hand-written prompt before adopting it. The reference implementation, examples, and the Nature paper links all live in the official repository below — start from its tutorials, which mirror the autograd API you already know.

FAQ

What is a textual gradient in TextGrad?

A textual gradient is a natural-language critique, written by an LLM, that explains what is wrong with a piece of text and how it should change to reduce the loss. It plays the same role as a numeric gradient in neural-network training — it tells each component which direction to move — but it is human-readable English instead of a number.

How is TextGrad different from DSPy?

Both replace hand-tuning prompts with automated optimization, but the signal differs. DSPy mostly searches for good instructions and few-shot examples guided by a numeric metric, while TextGrad propagates written explanations of failure (textual gradients) and edits the text directly. DSPy's signal is a score; TextGrad's signal is a readable critique. The two are complementary and are sometimes combined.

Does TextGrad actually use backpropagation?

Not in the numeric sense — there are no derivatives or real chain-rule math. It borrows the structure of backpropagation: a forward pass produces an output, a loss critiques it, and that critique flows backward through each text node via LLM calls that act like the chain rule. The framework even mirrors PyTorch's API with .backward() and an optimizer step, which is why it is called 'autograd for text.'

What can you optimize with TextGrad?

Anything expressible as text: system prompts, reasoning instructions, candidate answers, code snippets, and even scientific artifacts like molecule or treatment descriptions. As long as you can write an LLM-based loss that judges quality, that text can become a variable that receives gradients and gets rewritten.

Is TextGrad expensive to run?

It can be. Every forward pass, loss evaluation, and backward step is one or more LLM calls, so optimizing a multi-step pipeline over several epochs and many examples multiplies quickly. Use a cheaper model for the forward pass, reserve a stronger model for writing gradients, batch your examples, and cache results to keep cost manageable.

Do I need labeled data to use TextGrad?

Often less than you'd think. The loss is an LLM judge, so for many tasks a well-written evaluation instruction plus a few example inputs is enough to start optimizing. You do, however, need held-out examples to check that the optimized prompt actually generalizes rather than just pleasing the judge.

Further reading