DSPy入門メモ:モジュールを使ってみる編
誰もがすなるDSPyといふものを,自分もしてみむとてするなり
ということでDSPyに入門してみる.
公式のGetStarted を進めながら気づいたことや感じたことをメモする.
シンプルな応答をさせる
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def main():
print("Hello from dspy-training!")
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY)
dspy.configure(lm=lm)
print(lm("Say this is a test!", temperature=0.7)) # => ['This is a test!']
print(lm(messages=[{"role": "user", "content": "Say this is a test!"}])) # => ['This is a test!']
if __name__ == "__main__":
main()
GetStartedのサンプルをもとに,APIキーをenvから読み取り,応答をprintで出力するように変更した.
使用するモデルプロバイダによって使用するクラスを変えなくて良いので,(version0.3時点の)LangChainよりは呼び出しが楽な印象.
(LangChainも最近はモデル名のプレフィックスとしてプロバイダを書けば良いだけになった,とか.最近の変更を追えていないのでいつか確認したい.)
モジュール
DSPyはdspy.Predict,dspy.ChainOfThought,dspy.ReActなどのモジュールを提供している.
例えば,GetStartedでは
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY)
dspy.configure(lm=lm)
math = dspy.ChainOfThought("question -> answer: float")
print(math(question="2つのサイコロを振った時,2つの出目の合計が2になる確率は?"))
if __name__ == "__main__":
main()
のようなサンプルがある(questionは日本語に変えた).
ちなみにこの出力は以下のようになる.
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_00_module_math.py
Prediction(
reasoning='2つのサイコロを振った場合、出目の合計が2になるのは、サイコロの両方が1の時だけです。サイコロはそれぞれ1から6の目が出るため、全ての出目の組み合わせは6 × 6 = 36通りです。その中で合計が2になるのは1通り((1, 1))です。したがって、確率は1/36となります。',
answer=0.027777777777777776
)
ここではdspy.ChainOfThoughtを使っているが,その引数として"question -> answer: float"を渡している.これはsignatureというもので,このようにDSPyではI/Oを自然言語で定義することができる.
つまり,(あまりいい例が思いつかなかったが)
math = dspy.ChainOfThought("question -> answer: str")
とすれば出力を文字列にすることができ,実際この場合の出力は以下のようになった.
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_00_module_math.py
Prediction(
reasoning='2つのサイコロを振った場合、出目の合計が2になるのは、両方のサイコロが1の時だけです。サイコロはそれぞれ1から6の目が出るため、出目の組み合わせは6×6=36通りあります。その中で合計が2になるのは1通り((1, 1))です。したがって、確率は1/36となります。',
answer='1/36'
)
GetStartedに例があったのはdspy.Predict,dspy.ChainOfThought,dspy.ReActだったが,少し寄り道してドキュメントを深堀りしてみると,他にもいくつかモジュールがあるので見てみる.
dspy.BestOfN
参照:dspy.BestOfN
モジュールとreward_fn,thresholdを与えると,そのモジュールをN回実行し,reward_fnの戻り値がthresholdを超えた時の出力,もしくはN回の実行の中で最も良い予測となったときの出力を返す.
公式ドキュメントのサンプルを改造して,以下のようにしてみた.
from dotenv import load_dotenv
import dspy
import os
import random
load_dotenv()
def reward_fn(args, pred):
reward = random.random()
print(pred.answer, ":", reward)
return reward
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY, cache=False)
dspy.configure(lm=lm)
qa = dspy.ChainOfThought("question -> answer")
best_of_3 = dspy.BestOfN(module=qa, N=3, reward_fn=reward_fn, threshold=1.0)
result = best_of_3(question="今日の天気は?").answer
print(result)
if __name__ == "__main__":
main()
(このサンプルの場合質問は最早何でも良いのだが,)reward_fnでは乱数を発生させて,回答と乱数を出力するようにしてみた.
これを実行すると
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_01_module_bestofn.py
最新の天気情報を知りたい場合は、天気予報のウェブサイトやアプリを確認してください。 : 0.2967000220220095
今のところ、具体的な天気情報は提供できませんが、天気予報をチェックすることをお勧めします。 : 0.8757707437090874
今日の天気を確認するには、最新の天気予報サービスを使用してください。 : 0.20301100004867612
今のところ、具体的な天気情報は提供できませんが、天気予報をチェックすることをお勧めします。
のように,評価が最も高い(乱数だが)結果が最終的に出力されることがわかる.
dspy.MultiChainComparison
複数のChainOfThoughtを比較して一番尤もらしい回答を生成する,
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY)
dspy.configure(lm=lm)
# 質問
question="日本の歴史上,最も偉大な人物は?"
# ChainOfThoughtで推論を複数(3つ)生成する
math = dspy.ChainOfThought("question -> answer: str")
completions = math(question=question, config=dict(n=3)).completions
for reasoning_logic in completions.reasoning:
print("-------------------------------------------")
print(reasoning_logic)
# MultiChainComparisonで生成した推論を比較
compare = dspy.MultiChainComparison("question -> answer: str", M=3)
final_result = compare(completions, question=question)
print("===================================")
print(final_result)
if __name__ == "__main__":
main()
このようにすると,以下のような出力がされる.
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_02_module_multi_chain_comparison.py
-------------------------------------------
日本の歴史には多くの偉大な人物が存在しますが、誰が最も偉大であるかは、視点や評価基準によって異なることがあります。例えば、聖徳太子は日本における仏教の普及や政治制度の改革に大きな影響を与えたため、多くの人々に尊敬されています。また、徳川家康は江戸時代を築いた人物であり、長期にわたる平和な時代をもたらしました。また、明治維新の中心人物である西郷隆盛や、近代化を推進した明治天皇も評価されています。したがって、最も偉大な人物を一人に絞るのは難しいですが、影響力や功績に基づいて選ぶことができます。
-------------------------------------------
日本の歴史上、最も偉大な人物を選ぶことは非常に難しいですが、一般的には聖徳太子や織田信長、徳川家康などが挙げられます。聖徳太子は仏教の普及や政治制度の改革に尽力し、近代日本の基礎を築いたとされています。織田信長は戦国時代に日本を統一するための重要な役割を果たし、徳川家康は江戸時代を開いたことで、長期にわたる平和な時代をもたらしました。どの人物が最も偉大かは見方によって異なりますが、時代や影響力を考慮すると、聖徳太子が特に重要な存在とされることが多いです。
-------------------------------------------
日本の歴史には多くの偉大な人物が存在しますが、その中でも特に影響力が大きかったのは織田信長、豊臣秀吉、徳川家康などの戦国時代の武将や、明治維新の立役者である坂本龍馬や西郷隆盛などです。また、文化や思想の面では、松尾芭蕉や福沢諭吉といった人物も重要です。最も偉大な人物を一人に絞るのは難しいですが、時代や分野によって評価は異なるため、総合的に見ると徳川家康が日本の歴史に大きな影響を与えたと言えるでしょう。
===================================
Prediction(
rationale='日本の歴史には多くの偉大な人物がいますが、誰が最も偉大かは評価基準や視点によって異なります。聖徳太子、徳川家康、織田信長などが候補として挙げられ、それぞれが日本の歴史において重要な役割を果たしました。特に聖徳太子は仏教の普及や政治制度の改革に尽力し、徳川家康は平和な時代を築いたことから、一般的には彼らが特に偉大な人物と見なされることが多いです。',
answer='聖徳太子や徳川家康が日本の歴史上最も偉大な人物の候補とされていますが、評価は視点によって異なります。'
)
この「一番尤もらしい」をどのように判断しているのかが気になるところだが,それもLLMに任せているようである.
dspy.inspect_history(n=1)
で最新1回のLLMの呼び出しを出力できるのだが,以下のようになっていた.
[2025-11-08T03:11:13.884331]
System message:
Your input fields are:
1. `question` (str):
2. `reasoning_attempt_1` (str): ${reasoning attempt}
3. `reasoning_attempt_2` (str): ${reasoning attempt}
4. `reasoning_attempt_3` (str): ${reasoning attempt}
Your output fields are:
1. `rationale` (str): ${corrected reasoning}
2. `answer` (str):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## reasoning_attempt_1 ## ]]
{reasoning_attempt_1}
[[ ## reasoning_attempt_2 ## ]]
{reasoning_attempt_2}
[[ ## reasoning_attempt_3 ## ]]
{reasoning_attempt_3}
[[ ## rationale ## ]]
{rationale}
[[ ## answer ## ]]
{answer}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
User message:
[[ ## question ## ]]
日本の歴史上,最も偉大な人物は?
[[ ## reasoning_attempt_1 ## ]]
«I'm trying to 日本の歴史には多くの偉大な人物が存在しますが、誰が最も偉大であるかは、視点や評価基準によって異なることがあります。例えば、聖徳太子は日本における仏教の普及や政治制度の改革に大きな影響を与えたため、多くの人々に尊敬されています。また、徳川家康は江戸時代を築いた人物であり、長期にわたる平和な時代をもたらしました。また、明治維新の中心人物である西郷隆盛や、近代化を推進した明治天皇も評価されています。したがって、最も偉大な人物を一人に絞るのは難しいですが、影響力や功績に基づいて選ぶことができます。 I'm not sure but my prediction is 日本の歴史上最も偉大な人物は一概には決められませんが、聖徳太子や徳川家康、西郷隆盛などが候補として挙げられます。»
[[ ## reasoning_attempt_2 ## ]]
«I'm trying to 日本の歴史上、最も偉大な人物を選ぶことは非常に難しいですが、一般的には聖徳太子や織田信長、徳川家康などが挙げられます。聖徳太子は仏教の普及や政治制度の改革に尽力し、近代日本の基礎を築いたとされています。織田信長は戦国時代に日本を統一するための重要な役割を果たし、徳川家康は江戸時代を開いたことで、長期にわたる平和な時代をもたらしました。どの人物が最も偉大かは見方によって異なりますが、時代や影響力を考慮すると、聖徳太子が特に重要な存在とされることが多いです。 I'm not sure but my prediction is 聖徳太子が日本の歴史上最も偉大な人物の一人と一般的に考えられています。»
[[ ## reasoning_attempt_3 ## ]]
«I'm trying to 日本の歴史には多くの偉大な人物が存在しますが、その中でも特に影響力が大きかったのは織田信長、豊臣秀吉、徳川家康などの戦国時代の武将や、明治維新の立役者である坂本龍馬や西郷隆盛などです。また、文化や思想の面では、松尾芭蕉や福沢諭吉といった人物も重要です。最も偉大な人物を一人に絞るのは難しいですが、時代や分野によって評価は異なるため、総合的に見ると徳川家康が日本の歴史に大きな影響を与えたと言えるでしょう。 I'm not sure but my prediction is 徳川家康»
Respond with the corresponding output fields, starting with the field `[[ ## rationale ## ]]`, then `[[ ## answer ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## rationale ## ]]
日本の歴史には多くの偉大な人物がいますが、誰が最も偉大かは評価基準や視点によって異なります。聖徳太子、徳川家康、織田信長などが候補として挙げられ、それぞれが日本の歴史において重要な役割を果たしました。特に聖徳太子は仏教の普及や政治制度の改革に尽力し、徳川家康は平和な時代を築いたことから、一般的には彼らが特に偉大な人物と見なされることが多いです。
[[ ## answer ## ]]
聖徳太子や徳川家康が日本の歴史上最も偉大な人物の候補とされていますが、評価は視点によって異なります。
[[ ## completed ## ]]
このように,質問,推論をLLMに渡し,LLMにその推論を比較させることで最適な回答を生成しているようだ.
dspy.Parallel
文字通り,パラレル実行できる
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY)
dspy.configure(lm=lm)
math = dspy.ChainOfThought("question -> answer: float")
questions = [
"2つのサイコロを振った時,2つの出目の合計が1になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が2になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が3になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が4になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が5になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が6になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が7になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が8になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が9になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が10になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が11になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が12になる確率は?",
"2つのサイコロを振った時,2つの出目の合計が13になる確率は?"
]
exec_list = [(math, {"question": question}) for question in questions]
exec_parallel = dspy.Parallel()
results = exec_parallel(exec_list)
prob_sum=0.0
for question, result in zip(questions, results):
print(f"\nQuestion: {question}")
print(f"Answer: {result.answer}")
prob_sum += result.answer
print(f"確率の合計値:{prob_sum}")
if __name__ == "__main__":
main()
実行結果:
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_03_module_parallel.py
Processed 13 / 13 examples: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 13/13 [00:05<00:00, 2.36it/s]
Question: 2つのサイコロを振った時,2つの出目の合計が1になる確率は?
Answer: 0.0
Question: 2つのサイコロを振った時,2つの出目の合計が2になる確率は?
Answer: 0.027777777777777776
Question: 2つのサイコロを振った時,2つの出目の合計が3になる確率は?
Answer: 0.05555555555555555
Question: 2つのサイコロを振った時,2つの出目の合計が4になる確率は?
Answer: 0.08333333333333333
Question: 2つのサイコロを振った時,2つの出目の合計が5になる確率は?
Answer: 0.1111111111111111
Question: 2つのサイコロを振った時,2つの出目の合計が6になる確率は?
Answer: 0.1388888888888889
Question: 2つのサイコロを振った時,2つの出目の合計が7になる確率は?
Answer: 0.16666666666666666
Question: 2つのサイコロを振った時,2つの出目の合計が8になる確率は?
Answer: 0.1388888888888889
Question: 2つのサイコロを振った時,2つの出目の合計が9になる確率は?
Answer: 0.1111111111111111
Question: 2つのサイコロを振った時,2つの出目の合計が10になる確率は?
Answer: 0.08333333333333333
Question: 2つのサイコロを振った時,2つの出目の合計が11になる確率は?
Answer: 0.05555555555555555
Question: 2つのサイコロを振った時,2つの出目の合計が12になる確率は?
Answer: 0.027777777777777776
Question: 2つのサイコロを振った時,2つの出目の合計が13になる確率は?
Answer: 0.0
確率の合計値:1.0000000000000002
dspy.Predict
最もシンプルなLLMの呼び出し.
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY, cache=False)
dspy.configure(lm=lm)
math = dspy.Predict("question -> answer: float")
print(math(question="2つのサイコロを振った時,2つの出目の合計が2になる確率は?"))
dspy.inspect_history(n=1)
if __name__ == "__main__":
main()
出力は以下の通りで,CoTと違い推論ロジックはプロンプトに含まれないことがわかる.
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_04_predict.py
Prediction(
answer=0.027777777777777776
)
[2025-11-08T03:26:50.254382]
System message:
Your input fields are:
1. `question` (str):
Your output fields are:
1. `answer` (float):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## answer ## ]]
{answer} # note: the value you produce must be a single float value
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
User message:
[[ ## question ## ]]
2つのサイコロを振った時,2つの出目の合計が2になる確率は?
Respond with the corresponding output fields, starting with the field `[[ ## answer ## ]]` (must be formatted as a valid Python float), and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## answer ## ]]
0.027777777777777776
[[ ## completed ## ]]
dspy.ChainOfThought
冒頭にCoTは試しているのだが,Predictとの比較のために改めて.
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY, cache=False)
dspy.configure(lm=lm)
math = dspy.ChainOfThought("question -> answer: float")
print(math(question="2つのサイコロを振った時,2つの出目の合計が2になる確率は?"))
dspy.inspect_history(n=1)
if __name__ == "__main__":
main()
以下のように,reasoningが含まれることがわかる.
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_00_module_math.py
Prediction(
reasoning='2つのサイコロを振った場合、出目の合計が2になるのは、サイコロの両方が1の時だけです。サイコロはそれぞれ1から6の目が出るため、全ての出目の組み合わせは6 × 6 = 36通りです。その中で合計が2になるのは1通り((1, 1))です。したがって、確率は1/36となります。',
answer=0.027777777777777776
)
[2025-11-08T03:29:32.699857]
System message:
Your input fields are:
1. `question` (str):
Your output fields are:
1. `reasoning` (str):
2. `answer` (float):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## reasoning ## ]]
{reasoning}
[[ ## answer ## ]]
{answer} # note: the value you produce must be a single float value
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
User message:
[[ ## question ## ]]
2つのサイコロを振った時,2つの出目の合計が2になる確率は?
Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## answer ## ]]` (must be formatted as a valid Python float), and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## reasoning ## ]]
2つのサイコロを振った場合、出目の合計が2になるのは、サイコロの両方が1の時だけです。サイコロはそれぞれ1から6の目が出るため、全ての出目の組み合わせは6 × 6 = 36通りです。その中で合計が2になるのは1通り((1, 1))です。したがって、確率は1/36となります。
[[ ## answer ## ]]
0.027777777777777776
[[ ## completed ## ]]
dspy.ProgramOfThought
Pythonプログラムを実行するモジュールで,Denoのインストールが必要とのこと.
"features": {
"ghcr.io/va-h/devcontainers-features/uv:1": {},
"ghcr.io/devcontainers-community/features/deno:1": {}
}
開発環境はDevContainerにしているので,上記のようにdenoのfeatureを追加して,以下のコードを動かしてみる.
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY, cache=False)
dspy.configure(lm=lm)
math = dspy.ProgramOfThought("question -> answer: float")
print(math(question="2つのサイコロを振った時,2つの出目の合計が2になる確率は?"))
dspy.inspect_history(n=2)
if __name__ == "__main__":
main()
すると,出力は以下のようになる.
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_05_program_of_thought.py
Prediction(
reasoning='2つのサイコロを振った場合、出目の合計が2になるのは、(1, 1)の組み合わせだけです。この組み合わせは1通りで、サイコロの全ての出目の組み合わせは6×6=36通りです。したがって、出目の合計が2になる確率は1/36となり、計算すると約0.0278になります。',
answer=0.027777777777777776
)
[2025-11-08T03:37:35.716788]
System message:
Your input fields are:
1. `question` (str):
Your output fields are:
1. `reasoning` (str):
2. `generated_code` (str): python code that answers the question
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## reasoning ## ]]
{reasoning}
[[ ## generated_code ## ]]
{generated_code}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
You will be given `question` and you will respond with `generated_code`.
Generating executable Python code that programmatically computes the correct `generated_code`.
After you're done with the computation and think you have the answer, make sure to provide your answer by calling the preloaded function `final_answer()`.
You should structure your answer in a dict object, like {"field_a": answer_a, ...}, evaluates to the correct value mapping for `answer`.
User message:
[[ ## question ## ]]
2つのサイコロを振った時,2つの出目の合計が2になる確率は?
Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## generated_code ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## reasoning ## ]]
2つのサイコロを振った場合、出目の合計が2になるのは、両方のサイコロが1の時だけです。サイコロはそれぞれ1から6の目が出るため、全ての出目の組み合わせは6×6=36通りです。その中で合計が2になるのは1通り((1, 1))です。したがって、確率は1/36となります。
[[ ## generated_code ## ]]
total_outcomes = 6 * 6 # 6 faces on each die
favorable_outcomes = 1 # only (1, 1) gives a sum of 2
probability = favorable_outcomes / total_outcomes
probability
[[ ## completed ## ]]
[2025-11-08T03:37:39.988670]
System message:
Your input fields are:
1. `question` (str):
2. `final_generated_code` (str): python code that answers the question
3. `code_output` (str): output of previously-generated python code
Your output fields are:
1. `reasoning` (str):
2. `answer` (float):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## final_generated_code ## ]]
{final_generated_code}
[[ ## code_output ## ]]
{code_output}
[[ ## reasoning ## ]]
{reasoning}
[[ ## answer ## ]]
{answer} # note: the value you produce must be a single float value
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the final code `question`, `final_generated_code`, `code_output`, provide the final `answer`.
User message:
[[ ## question ## ]]
2つのサイコロを振った時,2つの出目の合計が2になる確率は?
[[ ## final_generated_code ## ]]
total_outcomes = 6 * 6 # 6 faces on each die
favorable_outcomes = 1 # only (1, 1) gives a sum of 2
probability = favorable_outcomes / total_outcomes
probability
[[ ## code_output ## ]]
0.027777777777777776
Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## answer ## ]]` (must be formatted as a valid Python float), and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## reasoning ## ]]
2つのサイコロを振った場合、出目の合計が2になるのは、(1, 1)の組み合わせだけです。この組み合わせは1通りで、サイコロの全ての出目の組み合わせは6×6=36通りです。したがって、出目の合計が2になる確率は1/36となり、計算すると約0.0278になります。
[[ ## answer ## ]]
0.027777777777777776
[[ ## completed ## ]]
まず,1回目のLLM呼び出しで質問に対してそれを解くためのコードが生成されていることがわかる.
更に,2回目のLLM呼び出しで質問・1回目の呼び出しで生成したコードとその出力結果が渡され,それを元に回答が生成されている.
……1回目の呼び出しでもう回答を出すコードは生成されているので,2回目の呼び出しは要らないのでは?とも思うが,signatureで渡している形式に合わせるためにこのようにしているのだと思う.
実際,
math = dspy.ProgramOfThought("question -> probability: float")
のように変更すると,2回目のLLM呼び出しのプロンプトで,これまでanswerだったところがprobabilityとなっていることが確認できる.
dspy.ReAct
いわゆるfunction callingできるモジュール.
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def get_weather(city: str) -> str:
print("get_weather called")
return f"現在の{city}の天気は雨です."
def get_sightseeing_spots(city: str) -> list:
print("get_sightseeing_spots called")
return [f"{city}博物館", f"{city}公園"]
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY, cache=False)
dspy.configure(lm=lm)
react = dspy.ReAct("question -> answer: str", tools=[get_weather, get_sightseeing_spots])
print(react(question="東京の観光スポットは?"))
dspy.inspect_history(n=1)
if __name__ == "__main__":
main()
以下のように出力される.
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_06_react.py
get_sightseeing_spots called
Prediction(
trajectory={'thought_0': '東京の観光スポットについての情報を集める必要があります。次に、観光スポットを取得するためのツールを使用します。', 'tool_name_0': 'get_sightseeing_spots', 'tool_args_0': {'city': '東京'}, 'observation_0': ['東京博物館', '東京公園'], 'thought_1': '東京の観光スポットとして「東京博物館」と「東京公園」が得られました。これらの情報をもとに、観光スポットについての詳細をまとめることができます。タスクを完了します。', 'tool_name_1': 'finish', 'tool_args_1': {}, 'observation_1': 'Completed.'},
reasoning='東京の観光スポットとして「東京博物館」と「東京公園」が挙げられます。これらは東京を訪れる観光客にとって人気のある場所であり、文化や自然を楽しむことができます。',
answer='東京の観光スポットには「東京博物館」と「東京公園」があります。'
)
[2025-11-08T03:47:59.166542]
System message:
Your input fields are:
1. `question` (str):
2. `trajectory` (str):
Your output fields are:
1. `reasoning` (str):
2. `answer` (str):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## trajectory ## ]]
{trajectory}
[[ ## reasoning ## ]]
{reasoning}
[[ ## answer ## ]]
{answer}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
User message:
[[ ## question ## ]]
東京の観光スポットは?
[[ ## trajectory ## ]]
[[ ## thought_0 ## ]]
東京の観光スポットについての情報を集める必要があります。次に、観光スポットを取得するためのツールを使用します。
[[ ## tool_name_0 ## ]]
get_sightseeing_spots
[[ ## tool_args_0 ## ]]
{"city": "東京"}
[[ ## observation_0 ## ]]
[1] «東京博物館»
[2] «東京公園»
[[ ## thought_1 ## ]]
東京の観光スポットとして「東京博物館」と「東京公園」が得られました。これらの情報をもとに、観光スポットについての詳細をまとめることができます。タスクを完了します。
[[ ## tool_name_1 ## ]]
finish
[[ ## tool_args_1 ## ]]
{}
[[ ## observation_1 ## ]]
Completed.
Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## answer ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## reasoning ## ]]
東京の観光スポットとして「東京博物館」と「東京公園」が挙げられます。これらは東京を訪れる観光客にとって人気のある場所であり、文化や自然を楽しむことができます。
[[ ## answer ## ]]
東京の観光スポットには「東京博物館」と「東京公園」があります。
[[ ## completed ## ]]
両方のツールを使いたくなるような,以下の質問をしてみる.
print(react(question="東京の今の天気は?また,天気を踏まえて今日訪れるべき東京の観光スポットは?"))
dspy.inspect_history(n=5)
すると,以下のようにツールを使いながら段階的に回答を導いていることが分かる.
長いので折りたたみ
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_06_react.py
get_weather called
get_sightseeing_spots called
Prediction(
trajectory={'thought_0': 'まず、東京の現在の天気を確認する必要があります。その後、天気に基づいて観光スポットを提案します。', 'tool_name_0': 'get_weather', 'tool_args_0': {'city': '東京'}, 'observation_0': '現在の東京の天気は雨です.', 'thought_1': '現在、東京は雨が降っています。この天気を考慮して、屋内の観光スポットを提案する必要があります。', 'tool_name_1': 'get_sightseeing_spots', 'tool_args_1': {'city': '東京'}, 'observation_1': ['東京博物館', '東京公園'], 'thought_2': '現在の天気が雨であるため、屋内の観光スポットとして「東京博物館」を訪れることをお勧めします。「東京公園」は屋外のため、雨の日には適していません。これで情報は揃ったので、タスクを完了します。', 'tool_name_2': 'finish', 'tool_args_2': {}, 'observation_2': 'Completed.'},
reasoning='現在の東京の天気は雨です。このため、屋外の観光スポットは適していません。屋内で楽しめる観光スポットとして「東京博物館」を訪れることをお勧めします。',
answer='現在の東京の天気は雨ですので、屋内の観光スポット「東京博物館」を訪れることをお勧めします。'
)
[2025-11-08T03:49:30.899299]
System message:
Your input fields are:
1. `question` (str):
2. `trajectory` (str):
Your output fields are:
1. `next_thought` (str):
2. `next_tool_name` (Literal['get_weather', 'get_sightseeing_spots', 'finish']):
3. `next_tool_args` (dict[str, Any]):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## trajectory ## ]]
{trajectory}
[[ ## next_thought ## ]]
{next_thought}
[[ ## next_tool_name ## ]]
{next_tool_name} # note: the value you produce must exactly match (no extra characters) one of: get_weather; get_sightseeing_spots; finish
[[ ## next_tool_args ## ]]
{next_tool_args} # note: the value you produce must adhere to the JSON schema: {"type": "object", "additionalProperties": true}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
You are an Agent. In each episode, you will be given the fields `question` as input. And you can see your past trajectory so far.
Your goal is to use one or more of the supplied tools to collect any necessary information for producing `answer`.
To do this, you will interleave next_thought, next_tool_name, and next_tool_args in each turn, and also when finishing the task.
After each tool call, you receive a resulting observation, which gets appended to your trajectory.
When writing next_thought, you may reason about the current situation and plan for future steps.
When selecting the next_tool_name and its next_tool_args, the tool must be one of:
(1) get_weather. It takes arguments {'city': {'type': 'string'}}.
(2) get_sightseeing_spots. It takes arguments {'city': {'type': 'string'}}.
(3) finish, whose description is <desc>Marks the task as complete. That is, signals that all information for producing the outputs, i.e. `answer`, are now available to be extracted.</desc>. It takes arguments {}.
When providing `next_tool_args`, the value inside the field must be in JSON format
User message:
[[ ## question ## ]]
東京の今の天気は?また,天気を踏まえて今日訪れるべき東京の観光スポットは?
[[ ## trajectory ## ]]
Respond with the corresponding output fields, starting with the field `[[ ## next_thought ## ]]`, then `[[ ## next_tool_name ## ]]` (must be formatted as a valid Python Literal['get_weather', 'get_sightseeing_spots', 'finish']), then `[[ ## next_tool_args ## ]]` (must be formatted as a valid Python dict[str, Any]), and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## next_thought ## ]]
まず、東京の現在の天気を確認する必要があります。その後、天気に基づいて観光スポットを提案します。
[[ ## next_tool_name ## ]]
get_weather
[[ ## next_tool_args ## ]]
{"city": "東京"}
[[ ## completed ## ]]
[2025-11-08T03:49:32.578322]
System message:
Your input fields are:
1. `question` (str):
2. `trajectory` (str):
Your output fields are:
1. `next_thought` (str):
2. `next_tool_name` (Literal['get_weather', 'get_sightseeing_spots', 'finish']):
3. `next_tool_args` (dict[str, Any]):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## trajectory ## ]]
{trajectory}
[[ ## next_thought ## ]]
{next_thought}
[[ ## next_tool_name ## ]]
{next_tool_name} # note: the value you produce must exactly match (no extra characters) one of: get_weather; get_sightseeing_spots; finish
[[ ## next_tool_args ## ]]
{next_tool_args} # note: the value you produce must adhere to the JSON schema: {"type": "object", "additionalProperties": true}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
You are an Agent. In each episode, you will be given the fields `question` as input. And you can see your past trajectory so far.
Your goal is to use one or more of the supplied tools to collect any necessary information for producing `answer`.
To do this, you will interleave next_thought, next_tool_name, and next_tool_args in each turn, and also when finishing the task.
After each tool call, you receive a resulting observation, which gets appended to your trajectory.
When writing next_thought, you may reason about the current situation and plan for future steps.
When selecting the next_tool_name and its next_tool_args, the tool must be one of:
(1) get_weather. It takes arguments {'city': {'type': 'string'}}.
(2) get_sightseeing_spots. It takes arguments {'city': {'type': 'string'}}.
(3) finish, whose description is <desc>Marks the task as complete. That is, signals that all information for producing the outputs, i.e. `answer`, are now available to be extracted.</desc>. It takes arguments {}.
When providing `next_tool_args`, the value inside the field must be in JSON format
User message:
[[ ## question ## ]]
東京の今の天気は?また,天気を踏まえて今日訪れるべき東京の観光スポットは?
[[ ## trajectory ## ]]
[[ ## thought_0 ## ]]
まず、東京の現在の天気を確認する必要があります。その後、天気に基づいて観光スポットを提案します。
[[ ## tool_name_0 ## ]]
get_weather
[[ ## tool_args_0 ## ]]
{"city": "東京"}
[[ ## observation_0 ## ]]
現在の東京の天気は雨です.
Respond with the corresponding output fields, starting with the field `[[ ## next_thought ## ]]`, then `[[ ## next_tool_name ## ]]` (must be formatted as a valid Python Literal['get_weather', 'get_sightseeing_spots', 'finish']), then `[[ ## next_tool_args ## ]]` (must be formatted as a valid Python dict[str, Any]), and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## next_thought ## ]]
現在、東京は雨が降っています。この天気を考慮して、屋内の観光スポットを提案する必要があります。
[[ ## next_tool_name ## ]]
get_sightseeing_spots
[[ ## next_tool_args ## ]]
{"city": "東京"}
[[ ## completed ## ]]
[2025-11-08T03:49:34.968379]
System message:
Your input fields are:
1. `question` (str):
2. `trajectory` (str):
Your output fields are:
1. `next_thought` (str):
2. `next_tool_name` (Literal['get_weather', 'get_sightseeing_spots', 'finish']):
3. `next_tool_args` (dict[str, Any]):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## trajectory ## ]]
{trajectory}
[[ ## next_thought ## ]]
{next_thought}
[[ ## next_tool_name ## ]]
{next_tool_name} # note: the value you produce must exactly match (no extra characters) one of: get_weather; get_sightseeing_spots; finish
[[ ## next_tool_args ## ]]
{next_tool_args} # note: the value you produce must adhere to the JSON schema: {"type": "object", "additionalProperties": true}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
You are an Agent. In each episode, you will be given the fields `question` as input. And you can see your past trajectory so far.
Your goal is to use one or more of the supplied tools to collect any necessary information for producing `answer`.
To do this, you will interleave next_thought, next_tool_name, and next_tool_args in each turn, and also when finishing the task.
After each tool call, you receive a resulting observation, which gets appended to your trajectory.
When writing next_thought, you may reason about the current situation and plan for future steps.
When selecting the next_tool_name and its next_tool_args, the tool must be one of:
(1) get_weather. It takes arguments {'city': {'type': 'string'}}.
(2) get_sightseeing_spots. It takes arguments {'city': {'type': 'string'}}.
(3) finish, whose description is <desc>Marks the task as complete. That is, signals that all information for producing the outputs, i.e. `answer`, are now available to be extracted.</desc>. It takes arguments {}.
When providing `next_tool_args`, the value inside the field must be in JSON format
User message:
[[ ## question ## ]]
東京の今の天気は?また,天気を踏まえて今日訪れるべき東京の観光スポットは?
[[ ## trajectory ## ]]
[[ ## thought_0 ## ]]
まず、東京の現在の天気を確認する必要があります。その後、天気に基づいて観光スポットを提案します。
[[ ## tool_name_0 ## ]]
get_weather
[[ ## tool_args_0 ## ]]
{"city": "東京"}
[[ ## observation_0 ## ]]
現在の東京の天気は雨です.
[[ ## thought_1 ## ]]
現在、東京は雨が降っています。この天気を考慮して、屋内の観光スポットを提案する必要があります。
[[ ## tool_name_1 ## ]]
get_sightseeing_spots
[[ ## tool_args_1 ## ]]
{"city": "東京"}
[[ ## observation_1 ## ]]
[1] «東京博物館»
[2] «東京公園»
Respond with the corresponding output fields, starting with the field `[[ ## next_thought ## ]]`, then `[[ ## next_tool_name ## ]]` (must be formatted as a valid Python Literal['get_weather', 'get_sightseeing_spots', 'finish']), then `[[ ## next_tool_args ## ]]` (must be formatted as a valid Python dict[str, Any]), and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## next_thought ## ]]
現在の天気が雨であるため、屋内の観光スポットとして「東京博物館」を訪れることをお勧めします。「東京公園」は屋外のため、雨の日には適していません。これで情報は揃ったので、タスクを完了します。
[[ ## next_tool_name ## ]]
finish
[[ ## next_tool_args ## ]]
{}
[[ ## completed ## ]]
[2025-11-08T03:49:37.147362]
System message:
Your input fields are:
1. `question` (str):
2. `trajectory` (str):
Your output fields are:
1. `reasoning` (str):
2. `answer` (str):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## trajectory ## ]]
{trajectory}
[[ ## reasoning ## ]]
{reasoning}
[[ ## answer ## ]]
{answer}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
User message:
[[ ## question ## ]]
東京の今の天気は?また,天気を踏まえて今日訪れるべき東京の観光スポットは?
[[ ## trajectory ## ]]
[[ ## thought_0 ## ]]
まず、東京の現在の天気を確認する必要があります。その後、天気に基づいて観光スポットを提案します。
[[ ## tool_name_0 ## ]]
get_weather
[[ ## tool_args_0 ## ]]
{"city": "東京"}
[[ ## observation_0 ## ]]
現在の東京の天気は雨です.
[[ ## thought_1 ## ]]
現在、東京は雨が降っています。この天気を考慮して、屋内の観光スポットを提案する必要があります。
[[ ## tool_name_1 ## ]]
get_sightseeing_spots
[[ ## tool_args_1 ## ]]
{"city": "東京"}
[[ ## observation_1 ## ]]
[1] «東京博物館»
[2] «東京公園»
[[ ## thought_2 ## ]]
現在の天気が雨であるため、屋内の観光スポットとして「東京博物館」を訪れることをお勧めします。「東京公園」は屋外のため、雨の日には適していません。これで情報は揃ったので、タスクを完了します。
[[ ## tool_name_2 ## ]]
finish
[[ ## tool_args_2 ## ]]
{}
[[ ## observation_2 ## ]]
Completed.
Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## answer ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## reasoning ## ]]
現在の東京の天気は雨です。このため、屋外の観光スポットは適していません。屋内で楽しめる観光スポットとして「東京博物館」を訪れることをお勧めします。
[[ ## answer ## ]]
現在の東京の天気は雨ですので、屋内の観光スポット「東京博物館」を訪れることをお勧めします。
[[ ## completed ## ]]
dspy.CodeAct
色々試したがPoT + ReActのようなもの……らしい.
from dotenv import load_dotenv
import dspy
import os
load_dotenv()
def get_calorie_by_food_name(food_name: str) -> float:
return 100.0
def get_necessary_calorie_each_day_by_age(age: int) -> float:
return 2000.0
def main():
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
lm = dspy.LM("openai/gpt-4o-mini", api_key=OPENAI_API_KEY, cache=False)
dspy.configure(lm=lm)
codeact = dspy.CodeAct("question -> answer: float", tools=[get_calorie_by_food_name, get_necessary_calorie_each_day_by_age])
print(codeact(question="30歳です.ハンバーグを食べたら1日に必要なカロリーの何%を摂取したことになりますか?"))
dspy.inspect_history(n=15)
if __name__ == "__main__":
main()
「料理名を入れたらカロリーを返すツール」「年齢を入れたら必要なカロリー量を返すツール」を渡し,それらから「ハンバーグは1日に必要なカロリーの何%なのか」を計算するようにしてみた.
ツールを使うところはReAct,計算するところはPoTを使っていることになるはず.
長いので折りたたみ
vscode ➜ /workspaces/dspy-training (main) $ uv run 01_07_codeact.py
Prediction(
trajectory={'generated_code_0': '# Define the age\nage = 30\n\n# Get the necessary calorie intake for the day based on age\nnecessary_calories = get_necessary_calorie_each_day_by_age({\'age\': age})\n\n# Define the food item\nfood_name = "ハンバーグ"\n\n# Get the calorie content of the food item\nfood_calories = get_calorie_by_food_name({\'food_name\': food_name})\n\n# Calculate the percentage of daily calorie intake from the food\npercentage_calories = (food_calories / necessary_calories) * 100\n\n# Print the result\nprint(f"ハンバーグを食べたら1日に必要なカロリーの{percentage_calories:.2f}%を摂取したことになります。")', 'code_output_0': '"\\u30cf\\u30f3\\u30d0\\u30fc\\u30b0\\u3092\\u98df\\u3079\\u305f\\u30891\\u65e5\\u306b\\u5fc5\\u8981\\u306a\\u30ab\\u30ed\\u30ea\\u30fc\\u306e5.00%\\u3092\\u6442\\u53d6\\u3057\\u305f\\u3053\\u3068\\u306b\\u306a\\u308a\\u307e\\u3059\\u3002\\n"'},
reasoning='30歳の人が1日に必要なカロリーを摂取するためには、年齢や性別、活動レベルに基づいて計算される必要カロリーがあります。ハンバーグのカロリーを調べ、そのカロリーが1日に必要なカロリーの何%に相当するかを計算しました。結果として、ハンバーグを食べた場合、1日に必要なカロリーの5.00%を摂取したことになります。',
answer=5.0
)
[2025-11-08T03:56:27.071880]
System message:
Your input fields are:
1. `question` (str):
2. `trajectory` (str):
Your output fields are:
1. `generated_code` (str): Python code that when executed, produces output relevant to answering the question
2. `finished` (bool): a boolean flag to determine if the process is done
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## trajectory ## ]]
{trajectory}
[[ ## generated_code ## ]]
{generated_code}
[[ ## finished ## ]]
{finished} # note: the value you produce must be True or False
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
You are an intelligent agent. For each episode, you will receive the fields `question` as input.
Your goal is to generate executable Python code that collects any necessary information for producing `answer`.
For each iteration, you will generate a code snippet that either solves the task or progresses towards the solution.
Ensure any output you wish to extract from the code is printed to the console. The code should be enclosed in a fenced code block.
When all information for producing the outputs (`answer`) are available to be extracted, mark `finished=True` besides the final Python code.
You have access to the Python Standard Library and the following functions:
(1) get_calorie_by_food_name. It takes arguments {'food_name': {'type': 'string'}}.
(2) get_necessary_calorie_each_day_by_age. It takes arguments {'age': {'type': 'integer'}}.
User message:
[[ ## question ## ]]
30歳です.ハンバーグを食べたら1日に必要なカロリーの何%を摂取したことになりますか?
[[ ## trajectory ## ]]
{}
Respond with the corresponding output fields, starting with the field `[[ ## generated_code ## ]]`, then `[[ ## finished ## ]]` (must be formatted as a valid Python bool), and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## generated_code ## ]]
python
# Define the age
age = 30
# Get the necessary calorie intake for the day based on age
necessary_calories = get_necessary_calorie_each_day_by_age({'age': age})
# Define the food item
food_name = "ハンバーグ"
# Get the calorie content of the food item
food_calories = get_calorie_by_food_name({'food_name': food_name})
# Calculate the percentage of daily calorie intake from the food
percentage_calories = (food_calories / necessary_calories) * 100
# Print the result
print(f"ハンバーグを食べたら1日に必要なカロリーの{percentage_calories:.2f}%を摂取したことになります。")
[[ ## finished ## ]]
True
[[ ## completed ## ]]
[2025-11-08T03:56:30.210015]
System message:
Your input fields are:
1. `question` (str):
2. `trajectory` (str):
Your output fields are:
1. `reasoning` (str):
2. `answer` (float):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## question ## ]]
{question}
[[ ## trajectory ## ]]
{trajectory}
[[ ## reasoning ## ]]
{reasoning}
[[ ## answer ## ]]
{answer} # note: the value you produce must be a single float value
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `question`, produce the fields `answer`.
User message:
[[ ## question ## ]]
30歳です.ハンバーグを食べたら1日に必要なカロリーの何%を摂取したことになりますか?
[[ ## trajectory ## ]]
[[ ## generated_code_0 ## ]]
# Define the age
age = 30
# Get the necessary calorie intake for the day based on age
necessary_calories = get_necessary_calorie_each_day_by_age({'age': age})
# Define the food item
food_name = "ハンバーグ"
# Get the calorie content of the food item
food_calories = get_calorie_by_food_name({'food_name': food_name})
# Calculate the percentage of daily calorie intake from the food
percentage_calories = (food_calories / necessary_calories) * 100
# Print the result
print(f"ハンバーグを食べたら1日に必要なカロリーの{percentage_calories:.2f}%を摂取したことになります。")
[[ ## code_output_0 ## ]]
"\u30cf\u30f3\u30d0\u30fc\u30b0\u3092\u98df\u3079\u305f\u30891\u65e5\u306b\u5fc5\u8981\u306a\u30ab\u30ed\u30ea\u30fc\u306e5.00%\u3092\u6442\u53d6\u3057\u305f\u3053\u3068\u306b\u306a\u308a\u307e\u3059\u3002\n"
Respond with the corresponding output fields, starting with the field `[[ ## reasoning ## ]]`, then `[[ ## answer ## ]]` (must be formatted as a valid Python float), and then ending with the marker for `[[ ## completed ## ]]`.
Response:
[[ ## reasoning ## ]]
30歳の人が1日に必要なカロリーを摂取するためには、年齢や性別、活動レベルに基づいて計算される必要カロリーがあります。ハンバーグのカロリーを調べ、そのカロリーが1日に必要なカロリーの何%に相当するかを計算しました。結果として、ハンバーグを食べた場合、1日に必要なカロリーの5.00%を摂取したことになります。
[[ ## answer ## ]]
5.0
[[ ## completed ## ]]
dspy.Refine
BestOfNに近いのだが,試行を繰り返しながら,前回までの実行のフィードバックを踏まえて文字通りrefineしていくというものらしい.
……のだが,reward_fnがthresholdに達していない時,
Refine: Attempt failed with rollout id 1: 'str' object has no attribute 'get'
Refine: Attempt failed with rollout id 2: 'str' object has no attribute 'get'
という出力がされてしまい,うまく動いていない雰囲気.
ただ,例えばreward_fnに
def reward_fn(args, pred):
if pred.answer[-1] == "。":
return 1.0
else:
return 0.0
を指定して「ベルギーの首都は?」と聞くと,モデルは「ブリュッセル」と返答し,これは末尾の1文字が「。」ではないのでrewardは0.0となる.
この時,LLMとのやり取りでは
[[ ## advice ## ]]
For the `predict` module, it has made a mistake in the past by not ensuring that the answer ends with the appropriate punctuation mark, which is crucial for the reward function to return a positive value. In the future, when generating the `answer`, the module should check if the answer ends with the correct punctuation (in this case, "。") before finalizing it. If it does not, the module should append the necessary punctuation to ensure compliance with the reward function's requirements.
このようなアドバイスが生成されており,本来このアドバイスが次のLLM呼出時に含まれることで,例えば「ベルギーの首都はブリュッセルです。」のような形式で返答が来ることが期待されるものだと思われる.
おわりに
本当はプロンプト最適化とかまでやりたかったのだがmoduleでお腹いっぱいなので一旦ここまで.
Discussion