🎉

LLMとのコミュニケーションをより効率化するPythonライブラリ「PromptoGen」をリリースしました🎉

2023/08/22に公開

TL;DR

Pythonライブラリ 「PromptoGen」 をリリースしました 🎉

PromptoGenは、「LLMとPythonの間のギャップをシームレスに繋ぐことで、効率的で未来を見据えたコミュニケーションを実現する。」 というビジョンの元、
あえて具体的なLLM実装への依存性をなくし、抽象化を行う ことで、
今後のLLM進化について行きやすい基盤を作る OSSプロジェクトです。

この記事では、PromptoGen プロジェクトの紹介やライブラリの使い方を紹介します。


ドキュメント: https://promptogen.zawakin.dev/ja

ソースコード:
https://github.com/zawakin/promptogen


実際にライブラリの内容を紹介していきます。

📚 PromptoGen: LLMとの効率的で拡張可能なコミュニケーションを実現

🚀 ビジョン

"LLMとPythonの間のギャップをシームレスに繋ぐことで、効率的で未来を見据えたコミュニケーションを実現する。"

💡 LLMライブラリの主な課題:

  • プロンプトエンジニアリングのエコシステムが不足: プロンプトの作成や共有が困難になる
  • 特定のLLMバージョンへの強い依存性: 高頻度なLLMの更新に対して脆弱になる
  • 実装が複雑: カスタマイズが困難になる

🎯 PromptoGenを選ぶ理由は?

  1. LLM I/OとPythonオブジェクト間のシームレスな変換: LLMとのインタラクションを合理化します
  2. 柔軟でユニークなインターフェース: ユーザーのカスタマイズと拡張性を保証します
  3. 将来の設計: LLMの進化に対する依存を減少させて将来に備えます

他のライブラリとの比較: 多くは特定のLLMバージョンに結びついており、将来の適応性に課題があります。Pydanticデータクラスライブラリだけに依存するPromptoGenは、LLM文字列とPythonオブジェクトとの間を橋渡しすることに特化しています。

🧰 主な機能:

  • Prompt データクラス: LLM通信を標準化し、プロンプトエンジニアリングをサポート
  • TextLLM インターフェース: LLMの詳細からの独立
  • PromptFormatter インターフェース: ユーザーのための高いカスタマイズ性

🎉 利点:

  • 🧩 モジュラー&拡張可能: 自由に組み合わせ、カスタムコンポーネントを追加
  • 🛡️ 未来の変更に強い: 新しいモデルのアップデートに強く立つ
  • 🔧 維持しやすい: 異なるLLMのためのデバッグや微調整が容易

⚠️ サポートされていない機能:

  • LLMとの直接通信: 直接のLLMとの会話よりも効率的なインターフェースを優先します
  • プロンプトのバージョン管理: シンプルに保つため、バージョンの複雑さを追加することを避けます
  • 特定のLLMの最適化: 単一のLLMのための最適化よりもLLM全体の適応性に焦点を当てます

📖 詳細について

PromptoGenとはを参照してください。

リリース情報

随時、以下のアカウントでお知らせします。

https://twitter.com/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では、プロンプトの自動生成プロンプトの実行 を効率的に行うことができます。

(詳細はプロンプト自動生成を参照してください。)

より詳細に知りたい方へ

ドキュメントを参照してください。

ソースコード

https://github.com/zawakin/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) でお知らせしていく予定です。

https://twitter.com/PromptoGen

いいね、シェア、フォローなど、よろしくお願いします!

https://twitter.com/zawawahoge/status/1693769751246655640

https://twitter.com/PromptoGen/status/1693768220266881224

(作者もよろしくお願いします。→ zawakin(@zawawahoge)

参考にしたプロジェクト

株式会社ナレッジワーク

Discussion