論文概要(Abstract)
TextGradは、LLMを基盤とした任意の計算グラフに対して「テキストによる自動微分」を実装するフレームワークである。PyTorchがテンソルと自動微分を提供するのと同様に、TextGradはパイプライン内のあらゆる成果物(コード、テキスト、分子構造等)を表現するVariable抽象を提供し、LLMフィードバックによる自動微分をサポートする。ラベルデータ不要のゼロショット最適化を数行のコードで実現し、LeetCode Hardで2.85倍改善(20%→57%)、Big-Bench HardでDSPy比+6.1%を達成した。
この記事は Zenn記事: LLMプロンプト管理CI/CD:Langfuse×LaunchDarklyでA/Bテストと安全ロールアウト の深掘りです。
情報源
- arXiv ID: 2406.07496
- URL: https://arxiv.org/abs/2406.07496
- 著者: Mert Yuksekgonul, Federico Bianchi, Joseph Boen, Sheng Liu, Zhi Huang, Carlos Guestrin, James Zou(Stanford University)
- 発表年: 2024
- 分野: cs.LG, cs.AI, cs.CL
背景と動機(Background & Motivation)
LLMパイプラインは複数のLLM呼び出し・ツール実行・外部API呼び出しで構成されるが、パイプライン全体の最適化は困難である。個別のプロンプトを手動チューニングしても、パイプライン全体の品質向上に直結しないことが多い。
ニューラルネットワークの最適化では、自動微分(Autograd)とバックプロパゲーションが決定的に重要だった。TextGradは、この成功パターンをテキスト空間に拡張する。数値勾配の代わりに「テキスト勾配」(自然言語のフィードバック)を使い、LLMパイプラインの任意の変数を最適化する。
Zenn記事のプロンプト管理CI/CDアーキテクチャにおいて、TextGradはLangfuseの本番フィードバックデータをテキスト損失として活用し、継続的プロンプト自動改善を実現する技術として位置づけられる。DSPyがラベルデータを必要とするのに対し、TextGradはゼロショットで動作するため、データアノテーションのコストがない。
主要な貢献(Key Contributions)
- テキスト自動微分の統一フレームワーク: 数値勾配の代わりに自然言語フィードバック(テキスト勾配)を用いた、LLMパイプラインへのバックプロパゲーション実装
- PyTorch準拠API: Variable、Function、Loss、TextualGradientDescentというPyTorchと同名のAPIにより、低い学習コスト
- ゼロショット・タスク非依存の最適化: ラベルデータを必要とせず、コード生成・創薬・放射線治療計画など多ドメインに適用可能
- 任意計算グラフのサポート: LLM呼び出し・コード実行・外部ツール呼び出しなど任意のPython関数を計算グラフのノードとして扱える
技術的詳細(Technical Details)
テキスト勾配の数学的基盤
ニューラルネットワークの自動微分では、スカラー損失 $L$ に対してパラメータ $\theta$ の勾配 $\frac{\partial L}{\partial \theta}$ を計算する。TextGradはこれをテキスト空間に拡張する。
関数 $f: \mathcal{X} \rightarrow \mathcal{Y}$($\mathcal{X}, \mathcal{Y}$ はテキスト空間)に対して、テキスト勾配 $\nabla_x L$ を以下のように定義:
\[\nabla_x L = \text{LLM}\left(\text{GradientPrompt}(x, f, y, \nabla_y L)\right)\]ここで、
- $x \in \mathcal{X}$: 入力変数(テキスト)
- $y = f(x) \in \mathcal{Y}$: 出力変数
- $\nabla_y L$: 下流からのテキスト勾配(フィードバック)
- $\text{GradientPrompt}$: テキスト勾配を計算するためのプロンプト関数
テキスト勾配計算のプロンプト:
1
2
3
4
5
6
7
8
9
Given the following context:
- The variable x: {x.value}
Role: {x.role_description}
- The function f applied to x: {f.description}
- The output y = f(x): {y.value}
- The downstream feedback (gradient at y): {∇y L}
Provide specific feedback on how x should be improved
to reduce the loss.
graph LR
subgraph 順伝播["順伝播 Forward Pass"]
V1["Variable x\n最適化対象のプロンプト\nrequires_grad=True"] -->|"Function f"| V2["Variable y\nLLM出力テキスト"]
V2 -->|"Loss Function"| L["Loss Variable\n品質評価フィードバック"]
end
subgraph 逆伝播["逆伝播 Backward Pass"]
L2["Loss.backward()"] -->|"GradientPrompt"| G2["テキスト勾配 ∇y L\n出力改善のフィードバック"]
G2 -->|"GradientPrompt"| G1["テキスト勾配 ∇x L\n入力改善のフィードバック"]
end
subgraph 更新["パラメータ更新 Step"]
TGD["TextualGradientDescent\nooptimizer.step()"] -->|"LLMで改善"| V1_new["Variable x 更新版\n改善されたプロンプト"]
end
L --> L2
G1 --> TGD
Variable/Function/Loss API
TextGradの3つの基本構成要素:
Variable(変数)
1
2
3
4
5
6
7
8
class Variable:
def __init__(self, value: str, role_description: str,
requires_grad: bool = False):
self.value = value # テキストコンテンツ
self.role_description = role_description # パイプラインでの役割
self.gradients = [] # 蓄積されたテキスト勾配
self.predecessors = [] # グラフ走査用の前ノード
self.requires_grad = requires_grad
Variableは文字列シリアライズ可能な任意のコンテンツを保持できる:
- Pythonコード
- プロンプト文字列
- 分子のSMILES表記
- Base64エンコードされた画像
Function(関数)
任意のPython関数をVariablesからVariablesへのマッピングとして定義。組み込みFunction:
BlackboxLLM: LLM APIコールのラッパーMultimodalLLMCall: マルチモーダル入力対応- カスタム関数: コード実行・外部API等
Loss(損失)
Functionの合成として定義。数値スカラーではなくテキストで品質を表現する。
バックプロパゲーションアルゴリズム
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def text_backpropagation(computation_graph, loss_variable):
"""TextGradのバックプロパゲーション
Args:
computation_graph: 順伝播で構築された計算グラフ
loss_variable: 損失Variable
Returns:
各requires_grad Variableのテキスト勾配
"""
# 損失の初期勾配を設定
loss_variable.gradient = "Improve this output"
# 逆トポロジカル順で各Functionを処理
for function in reverse_topological_order(computation_graph):
for input_var in function.inputs:
if input_var.requires_grad:
# テキスト勾配計算プロンプトを構築
gradient_prompt = build_gradient_prompt(
variable=input_var,
function=function,
output=function.output,
downstream_gradient=function.output.gradient
)
# LLMがテキスト勾配を生成
text_gradient = llm.generate(gradient_prompt)
input_var.gradients.append(text_gradient)
TextualGradientDescent(TGD)オプティマイザ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class TextualGradientDescent:
"""テキスト勾配降下法
PyTorchのSGDに対応するテキスト空間のオプティマイザ
"""
def __init__(self, parameters: list, engine: str = "gpt-4o"):
self.parameters = parameters
self.engine = engine
def zero_grad(self):
"""勾配をクリア"""
for param in self.parameters:
param.gradients = []
def step(self):
"""テキスト勾配に基づいてVariableを更新"""
for param in self.parameters:
# 全テキスト勾配を集約
aggregated = self._aggregate_gradients(param)
# LLMに改善を依頼
new_value = self.engine.generate(
f"""Variable: {param.value}
Feedback (text gradients):
{aggregated}
Provide an improved version of the variable."""
)
param.value = new_value
完全な使用例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import textgrad as tg
# バックエンドLLMを設定
tg.set_backward_engine("gpt-4o", override=True)
# 最適化するプロンプトを変数として定義
system_prompt = tg.Variable(
"あなたは技術記事の要約エキスパートです。",
role_description="最適化対象のシステムプロンプト",
requires_grad=True
)
# 損失関数を定義(LLMによる品質評価)
evaluator = tg.BlackboxLLM(
engine="gpt-4o",
system_prompt="""要約の品質を評価してください。
基準: 1.技術用語の保持 2.簡潔さ 3.数値データの維持
改善点を具体的に指摘してください。"""
)
# オプティマイザ設定
optimizer = tg.TextualGradientDescent(
parameters=[system_prompt],
engine="gpt-4o"
)
# 最適化ループ(5回反復)
for iteration in range(5):
optimizer.zero_grad()
# Forward pass: プロンプトで要約を生成
summary = llm_call(system_prompt, article_text)
# Loss: 品質評価
loss = evaluator(summary)
# Backward pass: テキスト勾配を計算
loss.backward()
# Update: プロンプトを改善
optimizer.step()
print(f"Iteration {iteration}: {system_prompt.value[:100]}...")
実装のポイント(Implementation)
インストールと基本設定
1
pip install textgrad
1
2
import textgrad as tg
tg.set_backward_engine("gpt-4o", override=True)
role_descriptionの重要性
TextGradの最適化品質はrole_descriptionの質に強く依存する:
| role_description | LeetCode Hard Pass Rate |
|---|---|
| 指定なし | 44% |
| “The code to optimize” | 50% |
| “Python code that solves the given LeetCode problem” | 57% |
13ポイントの差。role_descriptionはテキスト勾配の計算に使われるため、LLMが何を改善すべきか理解するための重要な情報源である。
コスト計算
$n$ 回反復、パイプラインに $k$ 個のLLM呼び出しがある場合:
\[\text{Total LLM calls} = O\left(n \cdot \frac{k(k+1)}{2}\right)\]| パイプライン構成 | k | 5回反復のLLM呼び出し数 |
|---|---|---|
| 単一プロンプト | 1 | 5 |
| 2段パイプライン | 2 | 15 |
| 3段RAG | 3 | 30 |
Langfuse統合パターン
Langfuseの本番スコアデータをTextGradの損失として活用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Langfuseから低スコアのトレースを取得
low_score_traces = langfuse.get_traces(
filters={"score": {"$lt": 0.7}},
limit=10
)
# TextGradの損失としてフィードバックを注入
for trace in low_score_traces:
user_feedback = trace.scores[0].comment
loss = tg.Variable(
user_feedback,
role_description="ユーザーからの品質フィードバック"
)
loss.backward()
optimizer.step()
# → ユーザーフィードバックに基づいてプロンプトが自動改善
実験結果(Results)
LeetCode Hard(コード最適化)
| 手法 | Pass Rate | 改善倍率 |
|---|---|---|
| GPT-4o ゼロショット | 20% | — |
| TextGrad (1回反復) | 35% | 1.75x |
| TextGrad (3回反復) | 48% | 2.40x |
| TextGrad (5回反復) | 57% | 2.85x |
5回の反復で、ゼロショットの2.85倍の改善。各反復で実行結果のフィードバックをテキスト勾配として使用している。
GPQA Diamond(大学院レベル問題)
| 手法 | Accuracy |
|---|---|
| GPT-4o ゼロショット | 51% |
| TextGrad (3回反復) | 55% |
プロンプト最適化(Big-Bench Hard)
| 手法 | Accuracy |
|---|---|
| DSPy | 83.0% |
| ProTeGi | 84.2% |
| TextGrad | 89.1% |
DSPy比で+6.1ポイント。TextGradはラベルデータなしでこの結果を達成している点が重要。
DSPyとの比較
| 比較軸 | TextGrad | DSPy |
|---|---|---|
| 最適化信号 | テキストフィードバック | ラベル付きデータ |
| データ要件 | ゼロショット | ラベル付きデータ必須 |
| 対象範囲 | 任意のテキスト変数 | プロンプト・デモ |
| 計算グラフ | 任意の構造 | 構造化LLMモジュール |
| BBH精度 | 89.1% | 83.0% |
実運用への応用(Practical Applications)
Zenn記事アーキテクチャとの統合
Zenn記事の3層防御にTextGradを統合する:
- Layer 0(TextGrad): Langfuseの本番フィードバックからプロンプトを継続的自動改善
- Layer 1(Promptfoo CI/CD): TextGrad最適化後のプロンプトを品質ゲートで検証
- Layer 2(LaunchDarkly段階ロールアウト): 検証済みプロンプトを段階的にデプロイ
- Layer 3(Langfuse本番監視): 新プロンプトのメトリクスをTextGradにフィードバック
DSPyとの使い分け
- ラベルデータがある場合: DSPy(BootstrapFewShot)で最適化。より安定した結果
- ラベルデータがない/少ない場合: TextGrad。ゼロショットで動作
- 両方を併用: DSPyで初期最適化 → TextGradで本番フィードバックベースの継続改善
関連研究(Related Work)
- DSPy (Khattab et al., 2023): 宣言的LLMパイプラインのコンパイル。ラベルデータが必要な点でTextGradと相補的
- OPRO (Yang et al., 2023): メタプロンプトベースの最適化。TextGradは計算グラフ全体を対象とするため適用範囲が広い
- ProTeGi (Pryzant et al., 2023): TextGradの先行手法。テキスト勾配のアイデアを提案したが、計算グラフへの一般化はTextGradが初
まとめと今後の展望
TextGradは「ニューラルネットワークの自動微分」をテキスト空間に拡張した重要な研究である。PyTorch準拠のAPIにより導入障壁が低く、ゼロショットで動作するためデータ収集コストがない。
Zenn記事のLangfuse×LaunchDarkly基盤と組み合わせることで、「本番フィードバック→テキスト勾配→プロンプト改善→品質ゲート→段階ロールアウト」の完全自動ループが実現する。特にLangfuseに蓄積された低スコアトレースをTextGradの損失関数として直接活用できる点が、既存のDSPy/OPROにはない独自の強みである。
参考文献
- arXiv: https://arxiv.org/abs/2406.07496
- Code: https://github.com/zou-group/textgrad(MIT License)
- Related Zenn article: https://zenn.dev/0h_n0/articles/9fc2f8c4a420e4