Summarization cookbook#
基本的な要約を超えてaidb.summarize_text_aggregate()
を使用するための現実世界のレシピ。各レシピは、データに適応できる自己完結型のパターンです。
すべての例では、テーブルを含むsalesデータセットを使用します
レシピ#
1.カテゴリ別にまとめます#
使用する場合 任意のディメンション製品、顧客、地域、期間ごとにグループ化されたテキストの概要が必要です。
SELECT p.product_name,
count(sn.note_id) AS note_count,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(my-llm)::json
) AS summary
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
GROUP BY p.product_name
ORDER BY note_count DESC;
チャンクしない場合、各グループのすべてのテキストは単一のLLM呼び出しとして送信されます。
500ノート各平均〜250文字未満のグループの場合、チャンクなしが最速です。
大規模なグループの場合、チャンクを追加してコンテキストウィンドウのオーバーフローを回避します Recipe 7 を参照してください。
2.ディール損失分析#
使用する場合 取引が失われている理由を、実行可能な理由とともに理解したいと考えています。
SELECT p.product_name,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(
my-llm,
prompt => Analyze these sales notes and answer: Why are we losing deals?
What are the top reasons for failure?
Provide specific, actionable insights.
)::json
) AS loss_analysis
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
WHERE so.status IN (lost, closed_lost)
GROUP BY p.product_name;
LLMが失敗パターンに焦点を当てるように、失われた取引のみ
WHERE status IN ('lost', 'closed_lost')にフィルターします。プロンプトは具体的である必要があります。「top resume」は、「analyze」よりも良い結果をもたらします。
より多くの制約された出力については、 Recipe 8 を参照してください。
3.感情分析#
使用する場合 グループ化されたテキストから全体的な感情ポジティブ/ネガティブ/混合を測定したい。
- - Compare sentiment between won and lost deals
SELECT p.product_name,
so.status AS deal_outcome,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(
my-llm,
prompt => Analyze the overall sentiment of these entries. Provide:
1) Overall sentiment (positive/negative/neutral/mixed),
2) Key positive themes,
3) Key negative themes,
4) Notable concerns if any.
)::json
) AS sentiment
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
WHERE so.status IN (won, lost)
GROUP BY p.product_name, so.status
ORDER BY p.product_name, so.status;
獲得した感情と失われた感情を並べて比較すると、成功した取引の違いが明らかになります。
プロンプトの番号付きフォーマットは、構造化された出力をガイドしますが、 LLMの遵守は規模で異なります 既知の制限 を参照してください。
4.重要なインサイトの抽出#
使用する場合 多くのエントリにわたって最も重要な所見の簡潔なリストが必要です。
SELECT p.product_name,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(
my-llm,
prompt => Extract the top 5 key insights from these entries as a numbered list.
Focus on actionable findings, patterns, and important trends.
)::json
) AS insights
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
GROUP BY p.product_name
ORDER BY count(sn.note_id) DESC
LIMIT 5;
プロンプトの「トップN」と「番号付きリスト」は、出力長をコンストレインします。
中程度のグループサイズ50〜500ノートで最適に動作します。非常に大規模なグループでは、LLMが特定の洞察ではなく一般的な概要を生成する場合があります。
5.問題の分類#
使用する場合 問題またはテーマを事前定義されたバケットに分類する必要があります。
SELECT p.product_name,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(
my-llm,
prompt => Categorize these sales notes into the following categories:
Pricing Concern, Technical Issue, Competitive Loss,
Timing/Budget, Feature Gap, Relationship/Trust, Integration Challenge.
For each category provide the count and percentage of entries that match,
plus a brief description.
)::json
) AS categories
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
WHERE so.status IN (lost, closed_lost)
GROUP BY p.product_name;
プロンプトでカテゴリを明示的に定義します。LLMに選択させないでください。
カウント/パーセンテージを要求して、定量的な出力を取得します。
LLMは、要求されたフォーマットに完全に従わない場合があります。より信頼性の高いフォーマットするには、ラッパーファンクション Recipe 8 を使用します。
6.エグゼクティブサマリー リデュース戦略#
使用する場合 詳細な説明ではなく、短く圧縮された概要が必要です。ダッシュボードとレポートに適しています。
SELECT p.product_name,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(
my-llm,
aidb.chunk_text_config(100, 100, 10, words),
NULL,
reduce,
3 -- reduction_factor: each chunk targets desired_length/3 words
)::json
) AS executive_summary
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
GROUP BY p.product_name;
仕組みテキストは100ワードのチャンクに分割されます。各チャンクはdesired_length / reduction_factor
ワード100/3 ≈
チャンクごとに33ワードをターゲットに要約されます。最終結果がdesired_length
内に収まるまで、要約は結合、再チャンク、および再要約されます。出力は約400〜900文字高圧縮です。
reduction_factor=3は、各チャンクをそのサイズの約33%に圧縮します。rf=2〜50%まで。Reduceでは常にワードベースのチャンクを使用します。charベースは失敗する場合があります 既知の制限 を参照してください。
最小安全なチャンクサイズ50ワード。
7.詳細な概要追加戦略#
使用する場合 テキストのすべての部分の詳細を保持する包括的なカバレッジが必要です。
SELECT p.product_name,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(
my-llm,
aidb.chunk_text_config(200, 200, 20, words),
NULL,
append
)::json
) AS detailed_summary
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
GROUP BY p.product_name;
仕組みテキストは、20のワードがオーバーラップする200ワードのチャンクに分割されます。各チャンクは独立して要約されます。概要が連結されます。出力は約5,000〜15,000文字冗長、詳細です。
大きなチャンク200ワード = 少ないLLM呼び出し = 高速ですが、粒度が低くなります。
小さなチャンク50ワード=より多くのLLM呼び出し=遅くなりますが、より詳細です。
20ワードのオーバーラップにより、チャンク境界でコンテキストが失われません。
8.再利用可能な構成ラッパーファンクション#
使用する場合 制約された出力形式での一貫した反復可能な分析が必要です。
事前構築された構成を返すSQLファンクションを作成し、summarize_text_aggregate()
に直接渡します。このパターンは、JSONを返すファンクション内のプロンプトロジックをラップするため、長いプロンプト文字列を繰り返すことなくGROUP BY
でクリーンに使用できます。
焦点を絞った分析
CREATE OR REPLACE FUNCTION analyze_top_reasons_config(
p_question TEXT,
p_n INTEGER DEFAULT 3,
p_model TEXT DEFAULT my-llm
) RETURNS JSON AS $$
SELECT aidb.summarize_text_config(
p_model,
prompt => format(
Answer this question about the text: %s
Give exactly %s reasons, each as a single sentence.
No preamble, no extra commentary.,
p_question, p_n
)
)::json;
$$ LANGUAGE sql;
- - Usage: pass the config function directly to the aggregate
SELECT product_name,
aidb.summarize_text_aggregate(
note_text,
analyze_top_reasons_config(Why are customers churning?, 3)
) AS churn_reasons
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
WHERE so.status = lost
GROUP BY product_name;
構造化された感情
CREATE OR REPLACE FUNCTION analyze_sentiment_config(
p_model TEXT DEFAULT my-llm
) RETURNS JSON AS $$
SELECT aidb.summarize_text_config(
p_model,
prompt => Analyze sentiment. Respond ONLY in this format:
OVERALL: [positive/negative/neutral/mixed]
SCORE: [1-10]
TOP_POSITIVE: [one sentence]
TOP_NEGATIVE: [one sentence]
TREND: [improving/declining/stable]
)::json;
$$ LANGUAGE sql;
- - Usage
SELECT product_name,
aidb.summarize_text_aggregate(note_text, analyze_sentiment_config()) AS sentiment
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
GROUP BY product_name;
問題カテゴライザー
CREATE OR REPLACE FUNCTION categorize_issues_config(
p_categories TEXT[] DEFAULT ARRAY[
pricing, technical, competition,
timing, budget, feature_gap, other],
p_model TEXT DEFAULT my-llm
) RETURNS JSON AS $$
SELECT aidb.summarize_text_config(
p_model,
prompt => format(
Categorize the issues in this text into ONLY these categories: %s.
For each category, give a count and one representative example.
Format: CATEGORY: count - "example quote",
array_to_string(p_categories, , )
)
)::json;
$$ LANGUAGE sql;
- - Usage
SELECT product_name,
aidb.summarize_text_aggregate(note_text, categorize_issues_config()) AS categories
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
WHERE so.status IN (lost, closed_lost)
GROUP BY product_name;
構成ラッパー機能は、一貫したインターフェイスを提供し、クエリを変更せずにプロンプトを調整できます。
戻り値の型は
summarize_text_aggregateのパラメーター型と一致するように、JSONBではなくJSONである必要があります。「プリアンブルなし」、「のみ」、「このフォーマットで応答」は効果的なステアリングフレーズです。
9.要約前のテキストのスクラブ#
使用する場合 テキストデータには、LLMトークンを無駄にするフィラーワード、挨拶、署名、またはボイラープレートが含まれています。
クリーンアップファンクションを作成し、集計の前に適用します。
CREATE OR REPLACE FUNCTION clean_note_text(p_text TEXT)
RETURNS TEXT AS $$
BEGIN
RETURN regexp_replace(
regexp_replace(
regexp_replace(
regexp_replace(p_text,
-- Remove email signatures
E(Best regards|Sincerely|Thanks|Cheers),?\s *\n.* $,
, gi),
-- Remove greeting lines
E^(Hi|Hello|Dear|Hey)\s+[A-Za-z]+,?\s*\n,
, gi),
-- Remove filler phrases
E\b(basically|essentially|actually|literally|you know|I mean|sort of|kind of)\b,
, gi),
-- Collapse whitespace
E\s+, , g);
END;
$$ LANGUAGE plpgsql IMMUTABLE;
- - Use it:
SELECT product_name,
aidb.summarize_text_aggregate(
clean_note_text(note_text),
aidb.summarize_text_config(my-llm)::json
) AS summary
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
GROUP BY product_name;
IMMUTABLEPostgreSQLは、繰り返しの呼び出しを最適化します。データの正規表現パターンたとえば、内部専門用語、標準ヘッダーなどをカスタマイズします。
一般的な削減 トークンが15〜30%減少します。これは、チャンクが減り、処理が高速になることを意味します。
レシピの組み合わせ 完全な最適化スタック#
最大の速度と品質を得るには、スクラビング+フィルタリング+構成ラッパーを組み合わせます。
SELECT p.product_name,
aidb.summarize_text_aggregate(
clean_note_text(sn.note_text),
analyze_top_reasons_config(Why are we losing these deals?, 3)
) AS loss_reasons
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
WHERE so.status IN (lost, closed_lost)
GROUP BY p.product_name;
このパイプライン 関連する行 失われた取引にフィルタリングします。
clean_note_text()
を介して行ごとにフィラーテキストをストリップしますトークンが15〜30%減少します。クリーン化されたすべてのテキストを蓄積し、LLMに送信します。構成ラッパーを介して制約されたプロンプトを適用して、短く焦点を当てた出力を実現します。
パフォーマンスベンチマーク#
テストスイートからのすべてのデータは、AIDB
6.1.0でOllamaを介してgranite4:1b
を使用して、salesデータセット18,503ノート、平均266文字/ノートに対して実行されます。
チャンクなしのパフォーマンス#
チャンキングがない場合、グループ内のすべてのテキストは単一のLLM呼び出しとして送信されます。高速ですが、モデルのコンテキストウィンドウによって制限されます。
Group size |
Duration |
Result size |
Notes |
|---|---|---|---|
77–84 notes |
21–28s |
1,100–2,500 chars |
安全な範囲、完全なデータ処理 |
100 notes |
28s |
2,200 chars |
約26Kの文字入力、十分なコンテキスト内 |
2,915 notes |
98s |
3,200 chars |
131Kトークンを超えるコンテキストウィンドウ以下を参照してください |
3,248 notes |
91s |
1,700 chars |
部分的なデータのみが処理されました |
チャンク戦略の比較 50ノート#
Config |
Est. LLM calls |
Duration |
ms/call |
Result chars |
Status |
|---|---|---|---|---|---|
No chunking |
1 |
12s |
11,947 |
1,443 |
PASS |
words/50/append |
32 |
152s |
4,759 |
12,159 |
PASS |
words/100/append |
16 |
77s |
4,832 |
7,021 |
PASS |
words/200/append |
8 |
48s |
6,044 |
4,781 |
PASS |
words/50/reduce rf=2 |
63 |
217s |
3,452 |
545 |
PASS |
words/100/reduce rf=2 |
31 |
102s |
3,280 |
475 |
PASS |
words/200/reduce rf=2 |
15 |
51s |
3,367 |
767 |
PASS |
words/50/reduce rf=3 |
50 |
170s |
3,396 |
410 |
PASS |
words/100/reduce rf=3 |
25 |
72s |
2,897 |
867 |
PASS |
words/200/reduce rf=3 |
12 |
44s |
3,661 |
535 |
PASS |
chars/200/append |
40 |
246s |
6,154 |
16,355 |
PASS |
chars/400/append |
20 |
137s |
6,835 |
11,793 |
PASS |
chars/800/append |
10 |
66s |
6,584 |
5,897 |
PASS |
chars/200/reduce rf=2 |
81 |
971s |
11,988 |
— |
FAIL |
chars/400/reduce rf=2 |
41 |
368s |
8,967 |
441 |
PASS |
chars/800/reduce rf=2 |
21 |
144s |
6,842 |
850 |
PASS |
戦略効率の概要#
Strategy |
Avg LLM calls |
Avg total time |
Avg ms/call |
Avg output |
|---|---|---|---|---|
No chunking |
1 |
12s |
11,947 |
1,443 chars |
Append |
21 |
121s |
5,868 |
9,668 chars |
Reduce rf=2 |
34 |
176s |
5,182 |
616 chars |
Reduce rf=3 |
29 |
95s |
3,318 |
604 chars |
予想される規模#
Scenario |
100 notes |
500 notes |
1,000 notes |
|---|---|---|---|
No chunking |
28s (1 call) |
~60s (1 call) |
~100s (1 call, partial data*) |
words/100/append |
~77s (16 calls) |
~385s (80 calls) |
~770s (160 calls) |
words/100/reduce rf=3 |
~72s (25 calls) |
~180s (62 calls) |
~240s (125 calls) |
1,000以上のノートでのノーチャンクは、コンテキストウィンドウを超える場合があります。完全を期すためにチャンクを使用します。
推奨される構成#
Use case |
Config |
Why |
|---|---|---|
Fastest possible (< 500 notes) |
No chunking |
Single LLM call, stays within context window |
Fast + detailed |
words/200/append |
Fewest calls for append, good detail |
Balanced detail |
words/100/append |
More granular, moderate speed |
Executive summary |
words/100/reduce rf=3 |
Compressed output, efficient reduce |
Maximum compression |
words/50/reduce rf=3 |
Smallest output, most LLM calls |
一般的なガイダンス
チャンクなしで開始します。グループが〜500ノートを超える場合にのみチャンクを追加します。常に、文字ベースよりもワードベースのチャンクを優先します。
Reduceの場合、rf=2 よりもrf=3
を優先します。同様の出力品質で呼び出しの合計数が少なくなります。
既知の制限#
コンテキストウィンドウのオーバーフローサイレント#
入力テキストの合計がモデルのコンテキストウィンドウを超えると、テキストはサイレントに切り捨てられます。エラーは発生しません。LLMは、適合するものを処理し、残りを無視します。
例えば granite4:1b
には、131,072トークンのコンテキストウィンドウがあります。
3,248ノート806K文字、〜201Kトークンのグループは、これを53%超えます。結果は有効な概要ですが、データの一部にすぎません。
安全な制限チャンキングなし
Avg note size |
Safe note count |
Total tokens |
|---|---|---|
250 chars |
~500 notes |
~33K tokens |
500 chars |
~250 notes |
~33K tokens |
1,000 chars |
~125 notes |
~33K tokens |
回避策これらの制限を超える可能性のあるグループにはチャンクを使用します。チャンク化は、コンテキストウィンドウのサイズに関係なく、すべてのデータを処理します。
- - Safe for large groups:
aidb.summarize_text_config(
my-llm,
aidb.chunk_text_config(200, 200, 20, words),
NULL,
append
)::json
小さなチャンクでの障害の削減#
reduce
ストラテジーでは、各要約ラウンドが入力よりも短い出力を生成する必要があります。チャンクサイズが小さい場合、LLMは入力よりも長い概要を生成することが多く、次の原因が発生します。
Text summarization failed to reduce size. The summarized output is larger than or equal to the input.
Consider adjusting the chunking parameters.
障害しきい値
Chunk type |
Size |
Result |
|---|---|---|
chars/200/reduce |
200 chars |
Always fails |
chars/400/reduce |
400 chars |
Unreliable |
chars/800/reduce |
800 chars |
Reliable |
words/50/reduce |
50 words |
Reliable |
words/100/reduce |
100 words |
Reliable |
words/200/reduce |
200 words |
Reliable |
回避策 reduce
でワードベースのチャンクを使用しますテストされたすべてのサイズで100の合格率。
charベースを使用する必要がある場合は、 >= 800 charsを使用します。
温度、max_tokens、およびシステムプロンプトの制御#
デフォルトでは、LLMはプロバイダーのデフォルト温度通常0.7〜1.0を使用します。これにより、実行ごとに出力に大きな分散が発生する可能性があります。
inference_config
を使用して、コールごとにこれらのパラメーターを制御します。
- - Control temperature and max_tokens for more consistent, bounded output
SELECT p.product_name,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(
my-llm,
NULL,
Summarize the key points,
NULL,
NULL,
aidb.inference_config(
temperature => 0.3,
max_tokens => 500
)
)::json
) AS summary
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
GROUP BY p.product_name;
- - Override the system prompt for non-summarization tasks
SELECT aidb.summarize_text(
input => Customer complained about slow response times and billing errors.,
options => aidb.summarize_text_config(
my-llm,
NULL,
NULL,
NULL,
NULL,
aidb.inference_config(
system_prompt => You are a customer support analyst. Classify issues by severity.,
temperature => 0.2
)
)::json
);
!!!ヒント 最良の結果を得るには、inference_config
と制約されたプロンプトを組み合わせます。たとえば、 max_tokens => 200
を設定し、 'Give exactly 3 bullet points, max 1 sentence each.'
のようなプロンプトを使用します。トークン制限はハードキャップとして機能し、プロンプトがフォーマットをガイドします。
エラーなしの空の結果#
場合によっては、LLMは処理後に空の文字列を返す場合があります。
AIDBは、これをエラーなしの成功結果として保存します。アプリケーションコードでlength(result)
を常に確認してください。
SELECT product_name,
CASE
WHEN length(summary) = 0 THEN ERROR: Empty result
WHEN length(summary) < 50 THEN WARNING: Suspiciously short
ELSE summary
END AS validated_summary
FROM (
SELECT p.product_name,
aidb.summarize_text_aggregate(
sn.note_text,
aidb.summarize_text_config(
my-llm,
prompt => Your prompt here
)::json
) AS summary
FROM sales_notes sn
JOIN sales_orders so ON sn.order_id = so.order_id
JOIN products p ON so.product_id = p.product_id
GROUP BY p.product_name
) sub;
プロンプトアドヒアランスは大規模に低下する#
大量のテキスト1,000以上のノートがある場合、LLMはカスタムプロンプトに関係なく、要約動作がデフォルトになる傾向があります。構造化された出力要求番号付きリスト、カテゴリフォーマット、センチメントラベルは、フォローの信頼性が低くなります。
回避策 LIMIT
以上の積極的なフィルタを使用して入力ボリュームを減らします。 append
でチャンクを使用するため、各チャンクはより小さな入力に対して独立してプロンプトを受信します。非常に明示的なフォーマット制約を指定したラッパーファンクションを使用します。要約する前にスクラブテキスト
Recipe 9 を使用してノイズを削減します。
最適化パターン#
1.テキストのスクラブ#
要約する前に、つなぎ言葉、挨拶、および定型文を削除します。 Recipe 9 を参照してください。影響 トークンが15〜30%削減され、それに比例して処理が高速化します。
2.コンストレインされたプロンプト#
特定の短いフォーマットの出力を生成するようにLLMに指示すると、トークン生成時間各呼び出しの最も遅い部分が削減されます。
- - Slow (open-ended, LLM may generate 2,000+ chars):
Summarize these notes.
- - Fast (constrained, LLM generates ~200 chars):
Give exactly 3 bullet points, max 1 sentence each.
3.抽出プレフィルタリング#
非常に大規模なデータセットの場合、LLMに送信する前にSQLを使用して最も関連性の高いテキストにプレフィルタリングします。
- - Only send notes containing relevant keywords
SELECT aidb.summarize_text_aggregate(
note_text,
aidb.summarize_text_config(
my-llm,
prompt => What are the pricing concerns?
)::json
)
FROM sales_notes
WHERE note_text ILIKE %price%
OR note_text ILIKE %cost%
OR note_text ILIKE %budget%
OR note_text ILIKE %expensive%
GROUP BY product_id;
インパクト 処理する行が大幅に減少。 LLMは関連するテキストのみを認識するため、速度と品質の両方が向上します。
4.適切な戦略の選択#
┌─ < 500 notes ──────> No chunking (fastest)
│
Group size ──┤
│ ┌─ Need detail ──> words/200/append
└─ >= 500 notes ──┤
└─ Need brief ───> words/100/reduce rf=3
5.パフォーマンスコストモデル#
支配的なコストはLLM呼び出し数です。入力サイズに応じて、各呼び出しには3〜12秒かかります。
Step |
Time |
|---|---|
Network overhead |
~50ms |
Prompt processing |
~1–3s |
Token generation |
~2–8s |
Response parsing |
~10ms |
Total per call |
~3–12s |
プロンプト処理は入力長に応じてスケーリングしますが、トークン生成は出力長に応じてスケーリングします。サマリーは入力量に関係なくほぼ固定サイズであるため、コンテキストウィンドウの制限までは、多数の小さな呼び出しよりも、より少なくて大きな呼び出しの方が効率的です。これが、ノーチャンクが小規模なグループに非常に効果的な理由です。呼び出しごとのテキストを最大化し、呼び出し数を最小限に抑えます。集計は処理前にすべての行を累計しているため、テキストは自然にバッチ化されます。