LlamaIndexモジュールガイドを試してみる: Prompts
Prompts
プロンプトはLLMに表現力を与える基本的な入力である。LlamaIndexはプロンプトを使ってインデックスを構築し、挿入を行い、クエリ中にトラバーサルを実行し、最終的な答えを合成する。
LlamaIndexはデフォルトのプロンプトテンプレートのセットを使用する。
さらに、gpt-3.5-turboのようなチャットモデル用に特別に書かれたプロンプトもある。
フレームワークの動作をさらにカスタマイズするために、ユーザーが独自のプロンプトテンプレートを提供することもできます。カスタマイズのための最良の方法は、上記のリンクからデフォルトのプロンプトをコピーし、それをベースに修正を加えることです。
スクラッチでカスタムなプロンプトテンプレートを作る場合は以下のような感じ。
from llama_index.prompts import PromptTemplate
template = (
"以下は提供されたコンテキスト情報です。\n"
"---------------------\n"
"{context_str}"
"\n---------------------\n"
"これらの情報を元に次の質問に回答してください: {query_str}\n"
)
qa_template = PromptTemplate(template)
これにテンプレート変数をアサインする
context_str = """ドウデュース(欧字名:Do Deuce、2019年5月7日 - )は、日本の競走馬。主な勝ち鞍は2021年の朝日杯フューチュリティステークス、2022年の東京優駿、2023年の有馬記念。
馬名の意味は「する+テニス用語(勝利目前の意味)」。愛称は「おドウ」。2021年のJRA賞最優秀2歳牡馬である。"""
query_str = "ドウデュースの馬名の由来は?"
prompt = qa_template.format(context_str=context_str, query_str=query_str)
実際に生成されるプロンプト。
print(prompt)
以下は提供されたコンテキスト情報です。
---------------------
ドウデュース(欧字名:Do Deuce、2019年5月7日 - )は、日本の競走馬。主な勝ち鞍は2021年の朝日杯フューチュリティステークス、2022年の東京優駿、2023年の有馬記念。
馬名の意味は「する+テニス用語(勝利目前の意味)」。愛称は「おドウ」。2021年のJRA賞最優秀2歳牡馬である。
---------------------
これらの情報を元に次の質問に回答してください: ドウデュースの馬名の由来は?
format_messagesでチャット向けプロンプトに変換もできる。
messages = qa_template. format_messages(context_str=context_str, query_str=query_str)
print(messages)
[
ChatMessage(
role=<MessageRole.USER: 'user'>,
content='以下は提供されたコンテキスト情報です。\n---------------------\nドウデュース(欧字名:Do Deuce、2019年5月7日 - )は、日本の競走馬。主な勝ち鞍は2021年の朝日杯フューチュリティステークス、2022年の東京優駿、2023年の有馬記念。\n馬名の意味は「する+テニス用語(勝利目前の意味)」[6]。愛称は「おドウ」。2021年のJRA賞最優秀2歳牡馬である。\n---------------------\nこれらの情報を元に質問に回答してください: ドウデュースの馬名の由来は?\n',
additional_kwargs={}
)
]
または最初からチャット向けに特化したプロンプトテンプレートを使うこともできる。
from llama_index.prompts import ChatPromptTemplate, ChatMessage, MessageRole
user_query_str = """
以下は提供されたコンテキスト情報です。
---------------------
{context}
---------------------
これらの情報を元に、ユーザーからの質問に回答してください: {query}
"""
message_templates = [
ChatMessage(
content="あなたは競馬専門のチャットボットです。提供されたコンテキストに基づき、ユーザーからの競馬に関する質問に答えます。",
role=MessageRole.SYSTEM
),
ChatMessage(
content=user_query_str,
role=MessageRole.USER,
),
]
chat_template = ChatPromptTemplate(message_templates=message_templates)
テンプレート変数にアサイン。
context_str = """ドウデュース(欧字名:Do Deuce、2019年5月7日 - )は、日本の競走馬。主な勝ち鞍は2021年の朝日杯フューチュリティステークス、2022年の東京優駿、2023年の有馬記念。
馬名の意味は「する+テニス用語(勝利目前の意味)」[6]。愛称は「おドウ」。2021年のJRA賞最優秀2歳牡馬である。"""
query_str = "ドウデュースの馬名の由来は?"
messages = chat_template.format_messages(context=context_str, query=query_str)
print(messages)
[
ChatMessage(
role=<MessageRole.SYSTEM: 'system'>,
content='あなたは競馬専門のチャットボットです。提供されたコンテキストに基づき、ユーザーからの競馬に関する質問に答えます。',
additional_kwargs={}
),
ChatMessage(
role=<MessageRole.USER: 'user'>,
content='\n以下は提供されたコンテキスト情報です。\n---------------------\nドウデュース(欧字名:Do Deuce、2019年5月7日 - )は、日本の競走馬。主な勝ち鞍は2021年の朝日杯フューチュリティステークス、2022年の東京優駿、2023年の有馬記念。\n馬名の意味は「する+テニス用語(勝利目前の意味)」[6]。愛称は「おドウ」。2021年のJRA賞最優秀2歳牡馬である。\n---------------------\nこれらの情報を元に、ユーザーからの質問に回答してください: ドウデュースの馬名の由来は?\n',
additional_kwargs={}
)
]
formatでこれをテキスト補完向けのプロンプトに変換することもできる。
prompt = chat_template.format(context=context_str, query=query_str)
print(prompt)
system: あなたは競馬専門のチャットボットです。提供されたコンテキストに基づき、ユーザーからの競馬に関する質問に答えます。
user:
以下は提供されたコンテキスト情報です。
---------------------
ドウデュース(欧字名:Do Deuce、2019年5月7日 - )は、日本の競走馬。主な勝ち鞍は2021年の朝日杯フューチュリティステークス、2022年の東京優駿、2023年の有馬記念。
馬名の意味は「する+テニス用語(勝利目前の意味)」[6]。愛称は「おドウ」。2021年のJRA賞最優秀2歳牡馬である。
---------------------
これらの情報を元に、ユーザーからの質問に回答してください: ドウデュースの馬名の由来は?
assistant:
高レベルモジュール (query engine、response synthsizer、retrieverなど) は、LLMの呼び出しとプロンプトテンプレートが含まれる形となっている。よってこれらのモジュールを使いつつ、カスタムなプロンプトを定義したい場合は、スクラッチでプロンプトテンプレートを作るのではなく、それらのモジュールが使用しているデフォルトのプロンプトテンプレートを更新する形となる。
デフォルトのプロンプトテンプレートは以下。
モジュールが使用しているプロンプトはコード内から参照することができるらしい。modelsのservice contextのところで作ったサンプルのRAGのコードで確認してみる。
from pathlib import Path
import requests
# Wikipediaからのデータ読み込み
wiki_titles = ["イクイノックス", "ドウデュース"]
for title in wiki_titles:
response = requests.get(
"https://ja.wikipedia.org/w/api.php",
params={
"action": "query",
"format": "json",
"titles": title,
"prop": "extracts",
# 'exintro': True,
"explaintext": True,
},
).json()
page = next(iter(response["query"]["pages"].values()))
wiki_text = page["extract"]
data_path = Path("data")
if not data_path.exists():
Path.mkdir(data_path)
with open(data_path / f"{title}.txt", "w") as fp:
fp.write(wiki_text)
from llama_index import ServiceContext, set_global_service_context
from llama_index.embeddings import OpenAIEmbedding
from llama_index import VectorStoreIndex, SimpleDirectoryReader
embed_model = OpenAIEmbedding()
service_context = ServiceContext.from_defaults(embed_model=embed_model)
set_global_service_context(service_context)
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("ダービーを勝ったのは?")
print(response)
query_engineのget_promptsメソッドでプロンプトが辞書で返ってくるので、以下のような関数で表示してやる。
from IPython.display import Markdown, display
def display_prompt_dict(prompts_dict):
for k, p in prompts_dict.items():
text_md = f"**Prompt Key**: {k}<br>" f"**Text:** <br>"
display(Markdown(text_md))
print(p.get_template())
display(Markdown("<br><br>"))
prompts_dict = query_engine.get_prompts()
display_prompt_dict(prompts_dict)
Prompt Key: response_synthesizer:text_qa_template
Text:Context information is below. --------------------- {context_str} --------------------- Given the context information and not prior knowledge, answer the query. Query: {query_str} Answer:
Prompt Key: response_synthesizer:refine_template
Text:The original query is as follows: {query_str} We have provided an existing answer: {existing_answer} We have the opportunity to refine the existing answer (only if needed) with some more context below. ------------ {context_msg} ------------ Given the new context, refine the original answer to better answer the query. If the context isn't useful, >return the original answer. Refined Answer:
どうやらquery_engineのresponse_synthesizerに2つのテンプレートが紐づいている様子。突然response_synthesizerというのが出てきたような感があるけども、ここでは深く考えない。
いずれにせよ、上記のRAGのコードではプロンプトは一切定義していないが、実際にはデフォルトのテンプレートが使用されており、これを書き換えるにはデフォルトのプロンプトがどのように使用されているのかを理解する必要がある。
上記にある通り、よく使われるテンプレートは上にも出てきた2種類
-
text_qa_template
- ベクトル検索で取得したノード(ドキュメントのチャンク)を元に(初回)回答を得るプロンプトテンプレート
-
refine_template
- クエリエンジンにはresponse_modeというパラメータがある。このモード設定により、
text_qa_template
の結果を元に、更に回答を"refine"することができる。その際に使用されるのがコチラのプロンプトテンプレート。
- クエリエンジンにはresponse_modeというパラメータがある。このモード設定により、
response_synthesizerのresponse_modeは以下にあるけど、とりあえずcompact
とrefine
だけ抑えておく。
refine
- 検索されたテキストチャンクを順番に見ていくことで、答えを作成し、洗練させる。これにより、ノード/検索されたチャンクごとに別々の LLM 呼び出しが行われます。
- 最初のチャンクはtext_qa_templateプロンプトを使用したクエリで使用されます。次に、refine_templateプロンプトを使用した別のクエリで、答えと次のチャンク(および元の質問)が使用されます。そしてすべてのチャンクが解析されるまで続けます。
- チャンクが大きすぎてウィンドウに収まらない場合(プロンプトのサイズを考慮した場合)、TokenTextSplitterを使用してチャンクが分割され(チャンク間のテキストの重なりを許容する)、(新しい)追加のチャンクは元のチャンクコレクションのチャンクとみなされます(したがって、refine_templateでもクエリが実行されます)。
- より詳細な回答を得るのに適しています。
- compact`(デフォルト)
- refine と似ていますが、チャンクをあらかじめコンパクトに(連結して)おくため、LLM の呼び出しが少なくなります。
- コンテキストウィンドウ(text_qa_templateとrefine_templateの間の最大プロンプトサイズを考慮)に収まるだけのテキスト(取得したチャンクを連結/パックしたもの)を詰め込む。テキストが長すぎて1つのプロンプトに収まらない場合は、必要な数だけ分割される(TokenTextSplitterを使用するため、テキストチャンク間の重複がある程度許容される)。
- 各テキスト部分は「チャンク」とみなされ、refineシンセサイザーに送られる。
- 要するに、refineのようなものだが、LLMの呼び出しは少ない。
とりあえず実際にやってみて違いを見てみるのが良さそう。ログを有効にしてresponse_modeを明示的に設定してみる。まずcompact
。
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
query_engine = index.as_query_engine(response_mode="compact")
response = query_engine.query("ダービーを勝ったのは?")
print(response)
ログを見てみると以下のような感じでリクエストを投げている様子。
System: You are an expert Q&A system that is trusted around the world.
Always answer the query using the provided context information, and not prior knowledge.
Some rules to follow:
1. Never directly reference the given context in your answer.
2. Avoid statements like 'Based on the context, ...' or 'The context information ...' or anything along those lines.
User: Context information is below.
---------------------
file_path: data/ドウデュース.txt
しかし出馬投票後の同月24日、調教後に左前肢跛行を発症しドバイターフへの出走を取り消した。友道は「調教後に左腕節に違和感を認め、競馬に向けて進めておりましたが、将来のある馬なのでここでは無理をせず、取り消すことを決断いたしました」と語った。
(snip)
file_path: data/ドウデュース.txt
6月10日に国際競馬統括機関連盟が発表した「ロンジンワールドベストレースホースランキング」において、ドウデュースは日本ダービーを勝利した功績を評価され、シャフリヤールやエンブレムロードと並ぶレーティング120で第15位タイに位置づけられた。
(snip)
---------------------
Given the context information and not prior knowledge, answer the query.
Query: ダービーを勝ったのは?
Answer:
text_qa_template
を使ってプロンプトが生成されているのがわかる。
では同様にresponse_modeがrefine
の場合。
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
query_engine = index.as_query_engine(response_mode="refine")
response = query_engine.query("ダービーを勝ったのは?")
print(response)
ログを見ると、compact
のときと同じリクエストが行われたあとで、もう一度以下のようなプロンプトでリクエストが行われているのがわかる。
User: You are an expert Q&A system that strictly operates in two modes when refining existing answers:
1. **Rewrite** an original answer using the new context.
2. **Repeat** the original answer if the new context isn't useful.
Never reference the original answer or context directly in your answer.
When in doubt, just repeat the original answer.New Context: file_path: data/ドウデュース.txt
6月10日に国際競馬統括機関連盟が発表した「ロンジンワールドベストレースホースランキング」において、ドウデュースは日本ダービーを勝利した功績を評価され、シャフリヤールやエンブレムロードと並ぶレーティング120で第15位タイに位置づけられた。
(snip)
Query: ダービーを勝ったのは?
Original Answer: オルフェーヴルがダービーを勝ちました。
New Answer:
1回目のtext_qa_template
を使った際の応答を含めて、refine_template
と「似た」ようなプロンプトが生成されているのがわかる(そしてよく見ると1回目の回答は間違っている・・・)
「似た」というのは、refineはrefineっぽいなんだけど、get_promptsで表示されたものと違うんだよね・・・
多分get_promptsで表示されたのはこれ。
completionとchat completionでプロンプトが少し違うんだけども、どうもget_promptsがちゃんと返してくれてないような気がする。
それは一旦置いておいて。
モジュールが使用しているプロンプトを置き換える。
from llama_index.prompts import PromptTemplate
query_engine = index.as_query_engine() # response_mode="compact"に一旦戻す
qa_prompt_tmpl_str = (
"コンテキスト情報は以下です。\n"
"---------------------\n"
"{context_str}\n"
"---------------------\n"
"与えられたコンテキスト情報を元に、事前知識を使用することなく、質問に答えてください。回答には絵文字をたくさん使って楽しくかつ丁寧に回答してください。\n"
"質問: {query_str}\n"
"回答: "
)
qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str)
query_engine.update_prompts(
{"response_synthesizer:text_qa_template": qa_prompt_tmpl}
)
response = query_engine.query("ダービーを勝ったのは?")
print(response)
ドウデュースがダービーを勝ちました🏆🐎
実際のプロンプトはこんな感じになっていた。
コンテキスト情報は以下です。
---------------------
file_path: data/ドウデュース.txt
しかし出馬投票後の同月24日、調教後に左前肢跛行を発症しドバイターフへの出走を取り消した。友道は「調教後に左腕節に違和感を認め、競馬に向けて進めておりましたが、将来のある馬なのでここでは無理をせず、取り消すことを決断いたしました」と語った。
その後夏は治療と休養にあて、秋初戦として10月29日に東京競馬場で開催される天皇賞(秋)に出走。当日の第5競走で武豊が騎乗馬に右膝を蹴られ負傷したため、急遽戸崎圭太に乗り替わりとなった。レースは中団6番手からチャンスを狙ったものの、最終直線で伸び切れず7着に終わった。
(snip)
file_path: data/ドウデュース.txt
6月10日に国際競馬統括機関連盟が発表した「ロンジンワールドベストレースホースランキング」において、ドウデュースは日本ダービーを勝利した功績を評価され、シャフリヤールやエンブレムロードと並ぶレーティング120で第15位タイに位置づけられた。
次走として凱旋門賞への出走を表明した。ドウデュースののんびりとした性格上、フランスへ赴いた場合に放牧と勘違いする可能性を踏まえ、日本ダービー直後の時点で友道は直行する予定と語っていたが、のちに松島はニエル賞を前哨戦として使ってから凱旋門賞に出走するプランもあり、その後はアメリカのブリーダーズカップ・ターフへの出走も検討していると述べた。最終的に友道は凱旋門賞に直行ではなくニエル賞を経由して凱旋門賞に出走すると発表した。いずれも後方からの競馬となったが休み明けや重馬場の影響もあり十分なパフォーマンスを発揮出来ず、それぞれ4着、19着に終わった。
(snip)
---------------------
与えられたコンテキスト情報を元に、事前知識を使用することなく、質問に答えてください。回答には絵文字をたくさん使って楽しくかつ丁寧に回答してください。
質問: ダービーを勝ったのは?
回答:
カスタムで作ったプロンプトが置き換わっているのがわかる。ただし、chat completionsの場合にはデフォルトだと設定されていたsystemプロンプトが今回は使用されていない。completionsだと1リクエストのプロンプトは1つでいいけども、chat completionsでは、userプロンプトだけカスタマイズするという訳にはいかない模様。
これLangChainでも以前触ったときに似たようなことがあって、高レベルモジュールのプロンプトのカスタマイズをする場合には、必要なプロンプトは全部カスタマイズする、ぐらいに考えておいたほうが良さそう。
ということで、chat向けプロンプトのカスタマイズはこちら。
response_modeをrefine
にしてフルでカスタマイズしてみた。
from llama_index.prompts import ChatPromptTemplate
from llama_index.llms import ChatMessage, MessageRole
# System prompt
system_prompt_content=(
"あなたは、世界中で信頼されているエキスパートQ&Aシステムです。\n"
"事前知識ではなく、常に提供されたコンテキスト情報を使って質問に答えてください。\n"
"守るべきルール:\n"
"1. 回答の中で、与えられた文脈を直接参照してはいけません。\n"
"2. 「コンテキスト情報によると・・・」や「コンテキスト情報では・・・」というような文章を回答に含めないでください\n"
"3. 回答には絵文字をたくさん使って楽しくかつ丁寧に回答してください。"
)
# Text QA Prompt
chat_text_qa_msgs = [
ChatMessage(
role=MessageRole.SYSTEM,
content=system_prompt_content,
),
ChatMessage(
role=MessageRole.USER,
content=(
"コンテキスト情報は以下です。\n"
"---------------------\n"
"{context_str}\n"
"---------------------\n"
"与えられたコンテキスト情報を元に、事前知識を使用することなく、質問に答えてください。\n"
"質問: {query_str}\n"
"回答: "
),
),
]
text_qa_template = ChatPromptTemplate(chat_text_qa_msgs)
# Refine Prompt
chat_refine_msgs = [
ChatMessage(
role=MessageRole.SYSTEM,
content=system_prompt_content,
),
ChatMessage(
role=MessageRole.USER,
content=(
"あなたは2つのモードで厳密に動作するエキスパートQ&Aシステムです。\n"
"以下に従って既存の回答を改善してください:\n"
"1. 新しいコンテキストを使って、既存の回答を**書き直す**。\n"
"2. 新しいコンテキストが有用でない場合は、既存の回答をそのまま**繰り返す**。\n"
"回答の中で、既存の回答やコンテキストを直接参照してはいけません。\n"
"迷ったら、既存の回答をそのまま繰り返してください。\n"
"新しいコンテキスト: {context_msg}\n"
"質問: {query_str}\n"
"既存の回答: {existing_answer}\n"
"新しい回答: "
),
),
]
refine_template = ChatPromptTemplate(chat_refine_msgs)
query_engine = index.as_query_engine(response_mode="refine")
query_engine.update_prompts(
{"response_synthesizer:text_qa_template": text_qa_template, "response_synthesizer:refine_template": refine_template}
)
response = query_engine.query("ダービーを勝ったのは?")
print(response)
実際に実行されたプロンプトはこうなっていた。
System: あなたは、世界中で信頼されているエキスパートQ&Aシステムです。
事前知識ではなく、常に提供されたコンテキスト情報を使って質問に答えてください。
守るべきルール:
1. 回答の中で、与えられた文脈を直接参照してはいけません。
2. 「コンテキスト情報によると・・・」や「コンテキスト情報では・・・」というような文章を回答に含めないでください
3. 回答には絵文字をたくさん使って楽しくかつ丁寧に回答してください。
User: コンテキスト情報は以下です。
---------------------
file_path: data/ドウデュース.txt
しかし出馬投票後の同月24日、調教後に左前肢跛行を発症しドバイターフへの出走を取り消した。友道は「調教後に左腕節に違和感を認め、競馬に向けて進めておりましたが、将来のある馬なのでここでは無理をせず、取り消すことを決断いたしました」と語った。
その後夏は治療と休養にあて、秋初戦として10月29日に東京競馬場で開催される天皇賞(秋)に出走。当日の第5競走で武豊が騎乗馬に右膝を蹴られ負傷したため、急遽戸崎圭太に乗り替わりとなった。レースは中団6番手からチャンスを狙ったものの、最終直線で伸び切れず7着に終わった。
(snip)
---------------------
与えられたコンテキスト情報を元に、事前知識を使用することなく、質問に答えてください。
質問: ダービーを勝ったのは?
回答:
System: あなたは、世界中で信頼されているエキスパートQ&Aシステムです。
事前知識ではなく、常に提供されたコンテキスト情報を使って質問に答えてください。
守るべきルール:
1. 回答の中で、与えられた文脈を直接参照してはいけません。
2. 「コンテキスト情報によると・・・」や「コンテキスト情報では・・・」というような文章を回答に含めないでください
3. 回答には絵文字をたくさん使って楽しくかつ丁寧に回答してください。
User: あなたは2つのモードで厳密に動作するエキスパートQ&Aシステムです。
以下に従って既存の回答を改善してください:
1. 新しいコンテキストを使って、既存の回答を**書き直す**。
2. 新しいコンテキストが有用でない場合は、既存の回答をそのまま**繰り返す**。
回答の中で、既存の回答やコンテキストを直接参照してはいけません。
迷ったら、既存の回答をそのまま繰り返してください。
新しいコンテキスト: file_path: data/ドウデュース.txt
6月10日に国際競馬統括機関連盟が発表した「ロンジンワールドベストレースホースランキング」において、ドウデュースは日本ダービーを勝利した功績を評価され、シャフリヤールやエンブレムロードと並ぶレーティング120で第15位タイに位置づけられた。
次走として凱旋門賞への出走を表明した。ドウデュースののんびりとした性格上、フランスへ赴いた場合に放牧と勘違いする可能性を踏まえ、日本ダービー直後の時点で友道は直行する予定と語っていたが、のちに松島はニエル賞を前哨戦として使ってから凱旋門賞に出走するプランもあり、その後はアメリカのブリーダーズカップ・ターフへの出走も検討していると述べた。最終的に友道は凱旋門賞に直行ではなくニエル賞を経由して凱旋門賞に出走すると発表した。いずれも後方からの競馬となったが休み明けや重馬場の影響もあり十分なパフォーマンスを発揮出来ず、それぞれ4着、19着に終わった。(snip)
質問: ダービーを勝ったのは?
既存の回答: ダービーを勝ったのは、タイトルホルダーです!🏆
新しい回答:
最終的な回答は以下となった。
ダービーを勝ったのは、ドウデュースです!🏆
updates_promptを使わずにquery engineの初期化時に設定することもできる。多分この使い方が普通になる気がする。
query_engine = index.as_query_engine(
response_mode="refine",
text_qa_template=text_qa_template,
refine_template=refine_template,
)
高レベルモジュールではなく、もっと細かく指定することもできるみたいだけど、ここでは大きく触れない。
from llama_index import get_response_synthesizer
from llama_index.query_engine import RetrieverQueryEngine
retriever = index.as_retriever()
response_synthesizer = get_response_synthesizer(
response_mode="refine",
text_qa_template=text_qa_template,
refine_template=refine_template
)
query_engine = RetrieverQueryEngine(retriever, response_synthesizer)
response = query_engine.query("ダービーを勝ったのは?")
print(response)
OpenAIを使う限りはもはやcompletionsを使うことはないので、chat completionsに合わせたほうがよい。
あと、get_promptsがchatの場合にちゃんと正しいものを返してくれないとか、ドキュメントは全体的にcompletionsベースのもので書かれている箇所がある印象(たしか以前のデフォルトのモデルはtext-davinci-003だったはず。今はgpt-3.5-turbo)。
実際に使われているプロンプトをログで流すなりコード追いかけるなりして、元のプロンプトテンプレートの構造を確認しておくという、LangChainでもやってたようなことはLlamaIndexでもやらないといけなさそう。抽象化度合いの高いフレームワークのデメリットはこういうところかな、やっぱり。