LLMとのコミュニケーションをより効率化するPythonライブラリ「PromptoGen」をリリースしました🎉
TL;DR
Pythonライブラリ 「PromptoGen」 をリリースしました 🎉
PromptoGenは、「LLMとPythonの間のギャップをシームレスに繋ぐことで、効率的で未来を見据えたコミュニケーションを実現する。」 というビジョンの元、
あえて具体的なLLM実装への依存性をなくし、抽象化を行う ことで、
今後のLLM進化について行きやすい基盤を作る OSSプロジェクトです。
この記事では、PromptoGen プロジェクトの紹介やライブラリの使い方を紹介します。
ドキュメント: https://promptogen.zawakin.dev/ja
ソースコード:
実際にライブラリの内容を紹介していきます。
📚 PromptoGen: LLMとの効率的で拡張可能なコミュニケーションを実現
🚀 ビジョン
"LLMとPythonの間のギャップをシームレスに繋ぐことで、効率的で未来を見据えたコミュニケーションを実現する。"
💡 LLMライブラリの主な課題:
- プロンプトエンジニアリングのエコシステムが不足: プロンプトの作成や共有が困難になる
- 特定のLLMバージョンへの強い依存性: 高頻度なLLMの更新に対して脆弱になる
- 実装が複雑: カスタマイズが困難になる
🎯 PromptoGenを選ぶ理由は?
- LLM I/OとPythonオブジェクト間のシームレスな変換: LLMとのインタラクションを合理化します
- 柔軟でユニークなインターフェース: ユーザーのカスタマイズと拡張性を保証します
- 将来の設計: LLMの進化に対する依存を減少させて将来に備えます
他のライブラリとの比較: 多くは特定のLLMバージョンに結びついており、将来の適応性に課題があります。Pydantic
データクラスライブラリだけに依存するPromptoGenは、LLM文字列とPythonオブジェクトとの間を橋渡しすることに特化しています。
🧰 主な機能:
-
Prompt
データクラス: LLM通信を標準化し、プロンプトエンジニアリングをサポート -
TextLLM
インターフェース: LLMの詳細からの独立 -
PromptFormatter
インターフェース: ユーザーのための高いカスタマイズ性
🎉 利点:
- 🧩 モジュラー&拡張可能: 自由に組み合わせ、カスタムコンポーネントを追加
- 🛡️ 未来の変更に強い: 新しいモデルのアップデートに強く立つ
- 🔧 維持しやすい: 異なるLLMのためのデバッグや微調整が容易
⚠️ サポートされていない機能:
- LLMとの直接通信: 直接のLLMとの会話よりも効率的なインターフェースを優先します
- プロンプトのバージョン管理: シンプルに保つため、バージョンの複雑さを追加することを避けます
- 特定のLLMの最適化: 単一のLLMのための最適化よりもLLM全体の適応性に焦点を当てます
📖 詳細について
PromptoGenとはを参照してください。
リリース情報
随時、以下のアカウントでお知らせします。
動作環境
Python 3.8 以上
インストール
pip install promptogen
インポート
import promptogen as pg
使い方
なにはともあれ、使い方を紹介してみます。
TextLLM: 柔軟なLLM連携
pg.TextLLM
を介すことで、PromptoGenは多種多様な大規模言語モデル(LLM)との連携を実現します。
TextLLMインターフェイスの実装例
import promptogen as pg
class YourTextLLM(pg.TextLLM):
def __init__(self, model: str):
self.model = model
def generate(self, text: str) -> str:
# 具体的な実装はここ
return generate_by_your_text_llm(text, self.model)
text_llm = YourTextLLM(model="your-model")
print(text_llm.generate("Hello, I'm a human."))
# Hello! How can I assist you today as an AI?
このインターフェイスの採用により、PromptoGenは異なるLLMやそのバージョンをスムーズに組み込むことが可能になります。ユーザーはLLMによらない一貫した方法で、様々なLLMを活用できるようになります。
つまり、LLM実装ではなく pg.TextLLM
に依存したプログラムを書くと、どんなLLMでも pg.TextLLM
を実装しさえすればそのプログラムが使えることになります。
OpenAI APIを使ったTextLLM実装例
OpenAI APIを使ったTextLLM実装例 を参照してください。
Prompt: プロンプト
プロンプトの作成
PromptoGen での一番の基本要素は プロンプト(Prompt) です。
例えば、テキストを入力とし、要約とキーワードを抽出するような命令をLLMに対して行う場合を考えてみましょう。
PromptoGen において、この命令は Prompt
データクラスのインスタンスとして表現されます。
Prompt
クラスは次のパラメータを持ちます。
- プロンプト名
- プロンプト説明(命令)
- 入力パラメータ情報
- 出力パラメータ情報
- 入出力テンプレート
- 入出力例(few-shots examples)の配列
具体的には次のような定義となります。
import promptogen as pg
summarizer = pg.Prompt(
# プロンプト名
name="Text Summarizer and Keyword Extractor",
# プロンプト説明(命令)
description="Summarize text and extract keywords.",
# 入力パラメータ情報
input_parameters=[
pg.ParameterInfo(name="text", description="Text to summarize"),
],
# 出力パラメータ情報
output_parameters=[
pg.ParameterInfo(name="summary", description="Summary of text"),
pg.ParameterInfo(name="keywords", description="Keywords extracted from text"),
],
# 入出力テンプレート
template=pg.IOExample(
input={'text': "This is a sample text to summarize."},
output={
'summary': "This is a summary of the text.",
'keywords': ["sample", "text", "summarize"],
},
),
# 入出力例(few-shots examples)の配列
examples=[
pg.IOExample(
input={
'text': "One sunny afternoon, ...(略)"},
output={
'summary': "A group of friends enjoyed an afternoon playing sports and making memories at a local park.",
'keywords': ["friends", "park", "sports", "memories"],
},
)
],
)
このように明確な定義を持つことで、プロンプトのエコシステムを形成しやすくなります。
基本的なユースケースでは、このように一から Prompt
を手動で定義するより、後述するような Prompt
自動生成をLLMにしてもらったあとに手直しをして使うことが多いです。
プロンプトの保存
import promptogen as pg
summarizer = pg.Prompt(
name="Text Summarizer and Keyword Extractor",
# ...(略)
)
summarizer.to_json_file("summarizer.json")
プロンプトの読み込み
import promptogen as pg
summarizer = pg.Prompt.from_json_file("summarizer.json")
PromptFormatter: プロンプトのフォーマット
さて、Prompt
クラスのインスタンスを実際にLLMに送信するために、文字列に変換する必要があります。PromptoGenでは、pg.PromptFormatter
インターフェイスを介して、プロンプトを任意の形式の文字列に変換することができます。
PromptoGenでは様々な形式をサポートしています。
- KeyValue形式
key: value
- JSON形式
{"key": "value"}
- etc.
プロンプトフォーマット基本構造
formatter.format_prompt
メソッドを使用して、プロンプトとそれに対する入力を文字列に変換できます。
key: value
形式のプロンプトのフォーマットを使用するには、pg.KeyValuePromptFormatter
を使用します。
パラメータ情報やテンプレートを表示するかどうかは PromptFormatterConfig
で設定できます。
フォーマットされたプロンプト文字列は、以下のような基本的な構造を持ちます。
import promptogen as pg
summarizer = pg.Prompt(
name="Text Summarizer and Keyword Extractor",
# ...
)
formatter = pg.KeyValuePromptFormatter()
input_value = {
"text": "In the realm of software engineering, ...(略)",
}
print(formatter.format_prompt(summarizer, input_value))
Summarize text and extract keywords.
Input Parameters:
- text: Text to summarize
Output Parameters:
- summary: Summary of text
- keywords: Keywords extracted from text
Template:
Input:
text: "This is a sample text to summarize."
Output:
summary: "This is a summary of the text."
keywords: ['sample', 'text', 'summarize']
Example 1:
Input:
text: "One sunny afternoon, ...(略)"
Output:
summary: "A group of friends enjoyed an afternoon playing sports and making memories at a local park."
keywords: ['friends', 'park', 'sports', 'memories']
--------
Input:
text: "In the realm of software engineering, ...(略)"
Output:
大規模言語モデルからの出力のパース
さきほどのプロンプト文字列を入力として、大規模言語モデル(GPT-3.5, GPT-4など)から出力を得ます。
summary: "This is a summary of the text."
keywords: ['sample', 'text', 'summarize']
formatter.parse
を使って、出力をパースしてみます。結果は dict
型の値となります。
import promptogen as pg
formatter = pg.KeyValuePromptFormatter()
raw_resp = """summary: "This is a summary of the text."
keywords: ['sample', 'text', 'summarize']"""
summarized_resp = formatter.parse(summarizer, raw_resp) # `dict`型で返る
print(summarized_resp)
{'summary': 'This is a summary of the text.', 'keywords': ['sample', 'text', 'summarize']}
PromptRunner: プロンプト実行を効率的に
pg.PromptRunner
は、プロンプトの実行をシンプルに、かつ効率的にサポートします。
PromptRunnerを使ったプロンプト実行
import promptogen as pg
# `pg.TextLLM`インターフェイスを実装したLLMを用意
text_llm = YourTextLLM(model="your-model")
formatter = pg.KeyValuePromptFormatter()
runner = pg.TextLLMPromptRunner(llm=text_llm, formatter=formatter)
summarizer = pg.Prompt(
name="Text Summarizer and Keyword Extractor",
# ...
)
input_value = {
"text": "In the realm of software engineering, ...",
}
output_value = runner.run_prompt(summarizer, input_value)
print(output_value)
pg.PromptRunner
は、PromptoGenを使ったプロンプトの実行をより直感的で効率的にするためのキーとなるツールです。
応用例
いくつかPromptoGenを使った応用例をみてみましょう。
応用例1: プロンプト実行のインターセプタ
プロンプト実行のインターセプタ
TextLLMPromptRunner
では、プロンプトの前処理や後処理を行うために「インターセプタ(PromptInterceptor
)」というインターフェイスがあります。
PromptInterceptor
を使うことで、プロンプトの実行前に入力を書き換えたり、プロンプトの実行後に出力を書き換えたりすることができます。
例えば、 ValueTranslatorInterceptor
を使えば、 ユーザーは日本語で通信しているようにみえるが、実際のLLMとの通信は英語で行う ことができます。
入出力翻訳インターセプタ実装例
このような複雑な処理が PromptoGen では数行変更するだけで実現できます。実際のプロンプト実行用のLLMと、翻訳用のLLMはそれぞれ別のものを使うことができます。
import promptogen as pg
from promptogen.prompt_interceptor.translate_interceptor import ValueTranslatorInterceptor
formatter = pg.KeyValuePromptFormatter()
llm = YourTextLLM(model="your-model")
translator_llm = YourTextLLM(model="your-model-translator")
interceptors = [
ValueTranslatorInterceptor(llm=translator_llm, from_lang="Japanese", to_lang="English"),
]
prompt_runner = pg.TextLLMPromptRunner(llm=llm, formatter=formatter, interceptors=interceptors)
# ...(略)
応用例2: プロンプト自動生成
プロンプト自動生成
pg.Prompt
データクラスも dict
で表せるので、「プロンプト説明」と「作成背景」を入力とし、プロンプトを生成させることができます。
PromptoGen ではいくつか事前に定義されたプロンプトがあり、そのうちの一つである PromptCreatorPrompt
は次のような図で表される入出力パラメータを持ちます。
プロンプト自動生成実装例
実際にプロンプト自動生成を行うコードを見てみましょう。
今回は、文脈(context)と質問(question)を入力とし、回答(answer)を出力する というプロンプトを作成します。
import promptogen as pg
from promptogen.prompt_collection import PromptCreatorPrompt
llm = YourTextLLM(model="your-model")
formatter = pg.KeyValuePromptFormatter()
prompt_runner = pg.TextLLMPromptRunner(llm=llm, formatter=formatter)
prompt_creator_prompt = PromptCreatorPrompt()
def create_context_qa_prompt() -> pg.Prompt:
input_value = {
# ここでプロンプトの説明を定義
"description": "Answer the question for the given context.",
# ここでプロンプトの作成背景を定義
"background": "(context: str, question: str) -> (answer: str)",
}
resp = prompt_runner.run_prompt(prompt_creator_prompt, input_value=input_value)
return pg.Prompt.from_dict(resp["prompt"])
context_qa_prompt = create_context_qa_prompt()
input_value = {
"context": "太郎は花子に花束を渡した。",
"question": "太郎は誰に花束を渡した?",
}
output_value = prompt_runner.run_prompt(context_qa_prompt, input_value=input_value)
print(output_value)
{'answer': '花子'}
このように、PromptoGenでは、プロンプトの自動生成 と プロンプトの実行 を効率的に行うことができます。
(詳細はプロンプト自動生成を参照してください。)
より詳細に知りたい方へ
ドキュメントを参照してください。
ソースコード
スターしてもらえると喜びます
まとめ
PromptoGenは、「効率的で拡張可能な、大規模言語モデル(LLM)とのコミュニケーションを実現する」 というビジョンの元、あえて具体的なLLM実装への依存性をなくし、抽象化を行う ことで、今後のLLM進化について行きやすい基盤を作る OSSプロジェクトです。
多くのLLM関連ライブラリが抱える課題を解決するために、PromptoGenは以下のような特徴を持ちます。
-
プロンプトエンジニアリングのエコシステム形成:
pg.Prompt
データクラス -
LLM実装からの独立性確保:
pg.TextLLM
インターフェイス -
カスタマイズ性の向上:
pg.PromptFormatter
インターフェイス
拡張性が高いため、PromptoGenはさまざまな応用が可能です。
おわりに
ここまで読んでいただき、ありがとうございました!
私は、株式会社ナレッジワークのAIチームで、普段はGoのバックエンド開発や大規模言語モデル(LLM)を活用したプロダクト開発を行っています。
PromptoGenは、私が個人開発として始めたものですが、ナレッジワークの「ナレッジスポンサーシップ」という制度を用いて、業務時間内に開発を行うことができました。
ライブラリの改善を行ったり、日英のドキュメント作成などを行うとなると、個人開発としては時間がかかり、公開するに至るまで難しい部分もありましたが、ナレッジワークの制度を使うことで、十分な時間を確保できたため、より良いライブラリを作ることができたと思います。
実際に、社内においても、PromptoGenを用いてLLMベンチマークを取ってみるなど、PromptoGenの活用例があり、今後もナレッジワークのAIチームで活用していきたいと考えています。
また、ナレッジワークでは採用も積極的に行っていますので、興味のある方はX(Twitter) DMなど、ぜひお声がけください。
特に、MLOps などの分野に興味のあるバックエンドエンジニアの方がいれば大歓迎です!(バックエンド&フロントエンド全体的に募集してます!)
一緒に開発しましょう!
PromptoGenは、まだまだ改善の余地があります。もし、このライブラリに興味を持っていただけた方がいらっしゃいましたら、ぜひ、IssueやPull Requestを送っていただけると嬉しいです。
リリース情報は PromptoGen (@PromptoGen) でお知らせしていく予定です。
いいね、シェア、フォローなど、よろしくお願いします!
(作者もよろしくお願いします。→ zawakin(@zawawahoge) )
Discussion