雨立(Ametatsu) - Tech Blog

技術に関して様々なものを試して共有!

プロンプト管理を劇的に効率化する方法【Prompty】

はじめに

LLMでの実験を多く行っていると、プロンプトの管理に悩まされることが多かったです。テキストファイルで管理しようとすると、ファイルの呼び出しや複数メッセージでのパーサーを独自実装する必要があったり、コードで管理すると、コード全体が増えて見づらくなることがありました。

また、LangChainを本番で実装しようとすると、ライブラリ自体が大きく、古さがあるため取り扱いづらさもありました。個人の場合はGroqを利用しているため、独自実装した方が早いのでは…と考えることもありました。

そんな中、最近話題になっているPromptyを試してみたところ、バージョン管理を含めて管理が楽そうだと感じました。そのため、この記事では個人の備忘録として記録しておくことにしました。

結論

Promptyのprepareを利用して、メッセージを抽出すると、独自実装でも便利に使用できました。

prompty.prepare(prompt, inputs)

実装方法

あくまで動かせればOKとして、出力が變動しないよう、top_ptemplatureは0にして渡すようにしました。

---
name: デモプロンプト
description: とりあえず確かめてみるためのプロンプト設定
authors:
  - ametatsu
model:
  api: chat
  parameters:
    top_p: 0
    templature: 0
sample:
  userName: 田中太郎
  question: 明日のお昼ご飯を考えてください。
---
system:こちらはデモで作成しているプロンプトです。ユーザーの姓名は {{userName}} になります。ユーザーの質問に、チョコレートのように可愛く答えてください。

user:
{{question}}

assistant:
私がお答えします!!

また、userNamequestionなど、外部から挿入可能な状態を作り、実際に試してみました。

プロンプトの設定ファイルをexample.promptyとして、下記のように実装しました。

import os
import prompty
from rich import print
from prompty import Prompty
from typing import Any, Dict

from groq import Groq
from groq.types.chat import ChatCompletion

class GroqLLM:
    def __init__(self, model_name: str):
        self.model_name = model_name
        super().__init__()

    def _client(self):
        return Groq(
            api_key=os.environ.get("GROQ_API_KEY"),
        )
    
    def chat(self, prompt: Prompty, inputs: Dict[str, Any]={}) -> ChatCompletion:
        messages = prompty.prepare(prompt, inputs)
        response = self._client().chat.completions.create(
            model=self.model_name,
            messages=messages,
            top_p=prompt.model.parameters.get('top_p'),
            temperature=prompt.model.parameters.get('temperature')
        )
        return response

llm = GroqLLM('mixtral-8x7b-32768')
prompt = prompty.load("./example.prompty")
inputs = {'userName': '雨立', 'question': '美味しいスイーツを考えてください。'}
response = llm.chat(prompt, inputs)

print(prompt)
print('=' * 80)
print(prompty.prepare(prompt, inputs))
print('=' * 80)
print(response)

この実装を動かした際の出力例は、以下のようになります。

まとめ

これは欲しかった機能でした。 やはり、コード外にプロンプトがあると、メインストリームの処理が見やすくなり、ロジックに集中できる為大変便利でした。

VSCodeプラグインを利用すると、カラー付けやバリデーション機能を使えるため、ミスが減るのではないかと思います。

今後も、この機能を試しながら、様々な開発で利用していきたいと思います。

References

Prompty

【LLM】CQoTの挑戦:Toulmin Modelで精度を高める新しいプロンプト手法

はじめに

Chain-of-Thought(CoT)を利用してプロンプトを書いていた時に、推論計画が正しく計画されずに意図しない結果が返ってくることはないでしょうか?

この記事では、論文「Critical-Questions-of-Thought: Steering LLM reasoning with Argumentative Querying」(CQoT)を深掘りし、CoT の概念に、主張と論証の構造を分析・構築するための枠組みである Toulmin Model(トゥールミンモデル) を適用した新しい手法やその実務的な応用可能性について解説します。

また、実際に動かしてみた感想もまとめます。

※ 各所でプロンプトの翻訳には、Google 翻訳を利用しています。


結論

論文では、Standard(User Promptのみ)やCoTと比べると、精度向上が見られたことが記載されています。

  • Reasoning(論理推論タスク) での平均改善
    • vs Standard: +4.74%
    • vs CoT: +4.48%
  • Math(数学タスク)での平均改善
    • vs Standard +5.71%
    • vs CoT: +5.09%

課題としては以下の点が挙げられています。

  • モデルサイズに応じたスケーリングの調査
    • CQoTの効果を発揮できるか、または一定のモデルサイズ以上でなければ効果が発揮されないかを検討
  • プロンプト技術との相互作用の研究
    • CQoTが他のプロンプト技術(例: Zero-shotやFew-shotプロンプト)とどのように相互作用し、パフォーマンスを向上させるかを探ること

実際に触ってみた感想としては以下が挙げられます。

  • 小規模モデルでは、精度が低くなる可能性あり
  • 実行時間が長くなるため、リアルタイム処理には不向き
  • 複数回の実行になるため、Tokenの利用数が増加しコストが増える

論文の概要

従来手法(CoT)では、どのように問題を解いていくかの推論計画に対して自己検証しないため、推論計画が間違っている場合には、出力が間違ってしまうことがありました。

そのため、推論計画に対して検証として、Toulmin Model(トゥールミンモデル)を利用したパフォーマンス向上を目指しているのが、CQoT の概要です。

Toulmin Model(トゥールミンモデル)について

Toulmin Model は、イギリスの哲学者 Stephen Toulmin が提示した、主張と論証の構造を分析・構築するための枠組みです。

論理的な議論を組み立てる際に押さえるべき 6 つの要素を提示しており、文章やスピーチなどで使われることが多いです。

全体の構成としては、以下のような構成になっています。

それぞれの要素の詳細は以下の通りです。

要素(en) 要素(ja) 詳細
Claim 主張 議論の結論
Data 根拠 主張を支える事実や情報
Warrant 論拠 根拠から主張を導く論理的つながり
Backing 裏付け 論拠をさらにサポートする追加の根拠や理論
Qualifier 限定条件 主張がどの程度妥当か、条件や程度を示す言葉
Rebuttal 反論 例外や批判への応答

例えば、「商品を選ぶ理由」に関して議論を組み立てる場合は、以下のように考えられます。

要素(en) 要素(ja)
Claim 主張 この商品はコストパフォーマンスが高いので買うべきだ
Data 根拠 同価格帯の他製品よりも機能が豊富である、ユーザーレビューの評価が高い
Warrant 論拠 コストパフォーマンスが高い製品は購入する価値がある
Backing 裏付け コストパフォーマンス評価の業界標準指標や、専門家の見解、学術的研究
Qualifier 限定条件 ほとんどの場合、この商品はコストパフォーマンスが高いと言える
Rebuttal 反論 ただし、使用者によっては必要のない機能が多く感じるかもしれない

提案手法の具体例と仕組み

以下の 3 ステップが全体のフローになります。

  1. Get Plan(計画の取得)
  2. Assess Plan(推論ステップ評価)
  3. Actual Output(実際の出力)

ステップ 2 で、Toulmin Model をベースとした 8 個の質問を LLM に投げて、Yes/No で回答させ、Yes の数に応じて最終的な出力である Actual Output(実際の出力)に進めるかどうかを決定します。

本研究では、以下の 3 パターンの場合、Actual Output に進めるようになっています。

  1. 7 個以上 Yes
  2. 4 回以上 Assess Plan をループしている場合、5 つ以上 Yes
  3. 10 回 Assess Plan をループしている場合

Get Plan(計画の取得)

System Instruction を翻訳すると、以下のようなプロンプトになります。

あなたは批判的で分析的な推論者です。ユーザープロンプトを注意深く読んでください。
返信する代わりに、ステップバイステップの推論を提供し、さまざまなステップを明確に分けて概要を示します。
各ステップは非常に具体的でわかりやすく、一連の前提が論理的に真実の結論につながる必要があります。
最終的な答えは提供しないでください(ただし、推論の過程で「最終的な答えは提供しない」と書き留めないでください)。

これによって、CoT で実現していた推論計画を前提条件として LLM に提供します。

また、そのままユーザの入力を利用した場合、質問に対する結果が出てしまいます。 そのため、 User Prompt の末尾に以下の文章を加えることで、推論計画を提供することが可能になります。

最終的な答えを提供するのではなく、推論計画を説明するだけにしてください。

Assess Plan(推論ステップ評価)

Reasoning Steps には、Get Plan で提案された推論計画を入力し、System Instruction では Critical Questions(批判的質問)を問います。

Critical Questions(批判的質問)の 8 個の質問は以下です。

# 文章 対象項目
1 推論プロセスは明確に定義された前提から始まっていますか? Data
2 前提は証拠または受け入れられた事実に基づいていますか? Data
3 推論プロセスは前提と結論の間に論理的なつながりを使用していますか? Warrant
4 使用されている論理的つながりは有効ですか? Warrant
5 推論プロセスは誤謬や論理的エラーを避けていますか? Warrant, Backing
6 結論は前提から論理的に導かれていますか? Claim
7 推論プロセスは確立された知識や原則と一致していますか? Backing
8 推論プロセスは妥当で合理的な結論につながっていますか? Claim, Qualifier, Rebuttal

上記の 8 個の質問は、Toulmin Model を厳密に実行しているわけではなく、包括的に質問ができるように選定されています。

こちらも Get Plan と同様に、そのまま結果を出さないようにするため、 User Prompt の末尾に以下の文章を入れます。

覚えておいてください: 各質問には必ず回答する必要があります。返信は「はい」または「いいえ」のみにしてください。

Actual Output(実際の出力)

Actual Output はシンプルで、System Instruction として段階的に考えるように指示を出し、 Reasoning Steps には Get Plan で提案された推論計画を入力して、User Prompt を実行します。

System Instruction を翻訳すると以下のようになります。

あなたは熟練した、批判的で分析的な推論者です。提供された指示に従って、常に段階的に推論します。
推論ステップによって確立されたプロトコルに完全かつ厳密に従って、ユーザーのプロンプトに回答します。あなたがしなければならないことの段階は次のとおりです。
1) 推論ステップで詳細に説明されているプロトコルを注意深く読んで理解します。それから推論を始めてください。
2) あなたの推論の各中間結論は、それぞれの前提の真実性によってもたらされなければなりません。
3) 確立された各結論は、他の情報と矛盾してはなりません。各ステップの後で、前のステップを振り返り、以前に推測したことに矛盾がないか確認してください。
4) 最終的な返答には、推論のステップと確立されたそれぞれの結論が論理的に含まれていなければなりません。返答が自分の推論と一致していることを注意深く確認してください。

実際にやってみた

どのような動きをするのか感覚を掴むために、実際に動かしてみました。そのため、CoT等との比較は特に実施していません。

論文で紹介されていた Github FCast07/CQoT を利用しました。 LLM として、Groq で提供されている llama3-70b-8192 を利用しました。

プロンプトとしては、LLM がよく間違えやすい、以下の問題としました。

9.9と9.11はどちらが大きいですか?

結果としては、3/5 の正解でした。

# 結果 出力(一部) ループ回数
1 x Therefore, 9.11 is larger than 9.9. 1
2 x Therefore, 9.11 is larger than 9.9. 6
3 o Therefore, the answer is: 9.9 is larger than 9.11. 6
4 o Therefore, the answer is: 9.9 is larger than 9.11. 2
5 o Therefore, the answer is: 9.9 is larger than 9.11. 6

70B のモデルであるため、実験対象の他モデルと比べると小さいモデルであるため、CQoTを利用しても高い精度は出ないことが確認できました。

ただ、全く解けなかった問題に対して、バリューが出る可能性があると考えています。


まとめ

概要や処理のアプローチについてまとめ、精度向上が成されていることが確認できました。

個人的な感想としては、+5%の精度向上を考えるとかなり魅力的な手法だと考えます。 しかし、実務レベルで実装する場合には精度とToken利用数・実行時間を考えると、コストパフォーマンスが悪いため、プロダクトへの適用はまだまだ難しい領域かと考えます。

今回触れていないですが、top_p や temperature に影響を受けて、出力のブレが出てくると考えているので、一定的で厳密な出力を求める場合には、利用が難しい可能性もあると考えます。

Python×Richで作るリッチな出力のススメ

はじめに

Pythonを使って業務効率化をする時、どうしても味気ないコンソール表示だけだと疲れてしまうことがあります。
例えば、ExcelCSVなどの表データを扱う場合、条件に合致したものだけ色を変えて強調表示したり、見やすい形式で出力したりするととても便利ですよね。
また、ファイル整理のように進捗を可視化したい時にも、ステータスやサマリーが一目でわかると安心感が違います。

「色々調べながらカスタマイズするのはちょっと面倒…」という方にこそ、rich はオススメです。
シンプルなコードで、視覚的にリッチな出力を実現できるので、最初に少し触ってみるだけで一気に見栄えが良くなります。


richのインストール方法

まずは rich をインストールしましょう。コマンド一つでサクッと導入できます。

pip install rich

Excelファイルの操作

続きを読む