デコレータベースで軽量なLLMアクセス用ライブラリ「promptic」を試す
以前に試した、PDFからポッドキャスト風音声を生成する「PDF2Audio」
元をたどると「pdf-to-podcast」というプロジェクトがベースになっている。
で、このpdf-to-podcastの中でLLMとのやりとりに使用されているのが、「promptic」というライブラリ
promptic
promptic
は軽量なデコレーターベースのPythonライブラリで、litellmを使用して大規模言語モデル(LLM)とのやり取りを簡素化する。promptic
を使用すると、数行のコードでプロンプトを簡単に作成し、入力引数を処理し、LLMから構造化された出力を受け取ることができる。
機能
- デコレータベースのAPI: 関数のドキュメント説明文を使用してプロンプトを簡単に定義し、
@promptic.llm
で装飾する。- 引数インターポレーション: docstrings内の
{argument_name}
プレースホルダを使用して、関数の引数をプロンプトに自動的にインターポレーションする。- Pydanticモデルのサポート: Pydanticモデルを使用して期待される出力構造を指定すると、
promptic
がLLMの応答が定義されたスキーマに適合していることを保証する。- ストリーミングのサポート: デコレータ関数を呼び出す際に
stream=True
を設定すると、LLM の応答をリアルタイムで受信できる。- LLM とのやり取りの簡素化: OpenAPI 応答オブジェクトやその他の LLM 固有の詳細を正確に把握しておく必要がなくなった。
promptic
が複雑な部分を抽象化してくれるため、プロンプトの定義と構造化された出力の受信に集中できる。なぜprompticなのか?
promptic
はシンプルかつ機能的で堅牢に設計されており、LLMを使用する際に必要なものの90%を提供します。OpenAPIレスポンスオブジェクトの特定の形状やその他のLLM固有の詳細を覚える必要がなくなり、プロンプトの作成と構造化された出力の受信に集中することができます。読みやすく簡潔なコードベースにより、
promptic
は信頼性が高く、理解しやすい。promptic
は、裏でlitellmの力を活用し、幅広いLLMとの互換性を確保している。
非常にシンプルに書けるので少し試してみる。
Colaboratoryで。
パッケージインストール
!pip install promptic
APIキーを環境変数で設定。今回はOpenAIとAnthropicのものを設定する。
import os
from google.colab import userdata
os.environ["OPENAI_API_KEY"] = userdata.get('OPENAI_API_KEY')
os.environ["ANTHROPIC_API_KEY"] = userdata.get('ANTHROPIC_API_KEY')
prompticでは、プロンプトを関数を@llm
デコレータでラップ、プロンプトは関数のdocstringで指定、引数をテンプレートとして展開する。呼び出す際はその関数に引数を渡すだけである。
from promptic import llm
@llm
def race_winner(race, year):
"""{year}年の{race}の勝ち馬はなんという馬?"""
print(race_winner("有馬記念", 2006))
ディープインパクト
モデルを変えてみる。デフォルトはgpt-3.5-turboとやや古い。@llm
デコレータにモデルをパラメータとして渡す
from promptic import llm
@llm(model="gpt-4o-mini")
def winner(race, year):
"""{year}年の{race}の勝ち馬はなんという馬?"""
print(winner("有馬記念", 2006))
2006年の有馬記念の勝ち馬は「ディープインパクト」です。彼は日本の競馬界で非常に有名な名馬で、多くの重賞を勝利しました。
システムプロンプトを渡す。
from promptic import llm
@llm(model="gpt-4o-mini", system="あなたは大阪のおばちゃんです。大阪弁で元気に話してください。")
def winner(race, year):
"""{year}年の{race}の勝ち馬はなんという馬?"""
print(winner("有馬記念", 2006))
2006年の有馬記念の勝ち馬は「ディープインパクト」やで!めっちゃ強い馬やったな。あの年は感動したわ~!競馬ファンにはたまらんレースやったよな。あなたも競馬好きなん?
@llm
にはLiteLLMのcompletionで渡せるパラメータをそのまま渡すことができる。
from promptic import llm
@llm(model="gpt-4o-mini", temperature=0.7, system="あなたは大阪のおばちゃんです。大阪弁で元気に話してください。")
def winner(race, year):
"""{year}年の{race}の勝ち馬はなんという馬?"""
print(winner("有馬記念", 2006))
あんた、2006年の有馬記念の勝ち馬は「ディープインパクト」やで!ほんまに強い馬やったなぁ。あの年のレースは感動もんやったわ!他に何か聞きたいことあったら言うてや~!
他のプロバイダのモデルも単にモデル名を変えればいい。
from promptic import llm
@llm(model="claude-3-5-sonnet-20240620")
def winner(race, year):
"""{year}年の{race}の勝ち馬はなんという馬?"""
print(winner("有馬記念", 2006))
2006年の有馬記念(G1)の勝ち馬は、ディープインパクトです。
ディープインパクトは、日本競馬史上最も有名な競走馬の一頭で、多くの人々に愛された名馬です。2006年の有馬記念は、ディープインパクトにとって現役最後のレースとなりました。
この有馬記念でのディープインパクトの勝利は、彼の輝かしい競走馬としてのキャリアを締めくくるにふさわしい素晴らしいパフォーマンスでした。2着にはポップロックが入り、3着はダイワメジャーでした。
ディープインパクトは、この勝利を含め、G1レースで7勝を挙げ、三冠馬(皐月賞、日本ダービー、菊花賞)としても知られています。彼の活躍は日本競馬界に大きな影響を与え、引退後は種牡馬として多くの優秀な子孫を残しました。
ストリーミングはstream=True
を指定するだけ。
from promptic import llm
@llm(stream=True, model="gpt-4o-mini")
def winner(race, year):
"""{year}年の{race}の勝ち馬はなんという馬?"""
# わかりやすさのため"\n"にしているが実際には""で
print("\n".join(winner("有馬記念", 2006)))
200
6
年
の
有
馬
記
念
の
勝
ち
馬
は
「
ディ
ープ
イン
パ
クト
」です
。
彼
は
この
レ
ース
で
見
事
な
走
り
を
見
せ
、有
終
の
美
を
飾
りました
。
Pydanticを使ったStructured Output
from pydantic import BaseModel
from typing import Literal
from promptic import llm
class RaceCourseInfo(BaseModel):
place: Literal["東京競馬場","中山競馬場","京都競馬場","阪神競馬場","中京競馬場","福島競馬場","新潟競馬場","小倉競馬場","札幌競馬場","函館競馬場"]
course_type: Literal["芝", "ダート"]
distance: int
@llm(model="gpt-4o-mini")
def race_course(race_name) -> RaceCourseInfo:
"""{race_name}が行われるコースについて教えて。"""
print(race_course("有馬記念"))
place='中山競馬場' course_type='芝' distance=2500
まとめ
同じデコレータベースだと、ellと似てるけど
prompticは、ellよりもさらにシンプルに書けて直感的。「プロンプトエンジニアリングフレームワーク」という表現はこちらのほうが近いかもしれないなと個人的に思う。
その反面、機能的にはellのほうがかなり多いというか、prompticは150行に満たないとても小さなものなので、比較するのは難しい。ただ普段遣いならこれで十分と思ってしまうよね。改めてデコレータはほんと強力だなと。
Function Callingや会話履歴には対応してないようだけど、そこまでやるならもうLiteLLM直接使えばいいような気もするし、シンプルにワンターンでやりとりするだけならこれで十分かなという気がする。実際pdf-to-podcast/PDF2Audioでは十分使えてるしね。
自分はデコレータを使いこなせてない感があるので、コードを参考にさせてもらおうと思う。