Closed12

プロンプトエンジニアリングフレームワーク「ell」を試す

kun432kun432

ここで知った。

https://twitter.com/webbigdata/status/1834076402590879755

GitHubレポジトリ

https://github.com/MadcowD/ell

概要部分だけざっと翻訳。最初からコード見てもわかんないし。

ellは、いくつかの基本原則に基づいて構築された軽量で機能的なプロンプトエンジニアリングフレームワークです:

1. プロンプトはプログラムであり、文字列ではない

プロンプトは単なる文字列ではなく、言語モデルに文字列が送信されるまでのすべてのコードです。 ellでは、言語モデルを言語モデルプログラムと呼ばれる独立したサブルーチンとして使用する特定の方法について考えています。

訳注: LMPはおそらく「言語モデルプログラム」(Language Model Program)を指していると思われる。

2. プロンプトは、実際には機械学習モデルのパラメータ

プロンプトエンジニアリングのプロセスには、機械学習の最適化プロセスと同様に、多くの反復が含まれます。LMPは単なる関数であるため、ellは、このプロセスに豊富なツールを提供しています。

ellは、静的および動的分析とgpt-4o-miniによる自動生成のコミットメッセージローカルストアに直接保存することで、プロンプトの自動バージョン管理とシリアライズを提供します。このプロセスは、機械学習のトレーニングループにおけるチェックポイント作成に似ていますが、特別なIDEやエディタは必要なく、すべて通常のPythonコードで実行できます。

3. 監視、バージョン管理、可視化のためのツール

適切なツールを使用することで、迅速なエンジニアリングは「暗黒の芸術」から「科学」へと変わります。**Ell Studioは、迅速なバージョン管理、モニタリング、可視化のためのローカルのオープンソースツールです。**Ell Studioを使用することで、迅速な最適化プロセスを時間をかけて経験則化し、手遅れになる前にリグレッションを検出することができます。

4. マルチモーダルは第一級であるべき

LLMは、テキスト、画像、音声、動画など、さまざまなタイプのコンテンツを処理および生成することができます。これらのデータタイプに対する迅速なエンジニアリングは、テキストの場合と同様に簡単であるべきです。

ellは、マルチモーダルな入力および出力のための豊富な型強制をサポートしています。LMPによって返されるMessageオブジェクト内で、PIL画像、音声、その他のマルチモーダル入力をインラインで使用することができます。

...その他にもたくさんあります!
詳しくはドキュメントをご覧ください!

どうやら公式ドキュメントに続きがある。

https://docs.ell.so/

テスト時の計算は重要

デモから実際に動作するものに移行するには、多くの場合、言語モデルへの複数回の呼び出しを含む迅速なエンジニアリングソリューションが必要となります。問題の機能分解を強制的に行うことで、ellは、テスト時計算を活用した技術を読みやすくモジュール化された方法で簡単に実装できるようにします。

言語モデルへの呼び出しはすべて価値がある

言語モデルの呼び出しは、すべてクレジットの価値があります。実際には、LLMの呼び出しは、ファインチューニング、蒸留、k-shotプロンプト、人間からのフィードバックに基づく強化学習など、さまざまな用途で使用されます。優れたプロンプトエンジニアリングシステムは、これらを第一級の概念として捉えるべきです。

すべてのLMPのソースコードを保存するだけでなく、ellはオプションで言語モデルへのすべての呼び出しをローカルに保存します。これにより、呼び出しデータセットの生成、バージョンごとのLMP出力の比較、およびプロンプトエンジニアリングの成果物のフルスペクトラムを活用したより高度な作業が可能になります。

必要なときは複雑に、必要でないときは単純に

言語モデルを使用するということは、単に文字列をやり取りするということですが、ただし、そうでない場合を除きます。

@ell.simpleを使用すると、LMPは単純な文字列出力を生成します。しかし、より複雑な、あるいはマルチモーダルな出力が必要な場合は、@ell.complexを使用して言語モデルからメッセージオブジェクトの応答を生成することができます。

プロンプトエンジニアリングライブラリは、ワークフローを妨げるべきではない

ellは、軽量で邪魔にならないライブラリとして設計されています。コーディングスタイルを変更したり、特別なエディタを使用する必要はありません。

プロンプトの定義や変更には、引き続き通常のPythonコードをIDEで使用し、プロンプトの視覚化や分析にはellの機能を活用することができます。langchainからellへの移行は、1つの関数ずつ行います。

便利そうな雰囲気は感じるのだが、まだちょっとよくわからない。実際に触ってみようと思う。

kun432kun432

インストール

https://docs.ell.so/installation.html

Colaboratoryでやってみる。ell-aiパッケージをインストールする

!pip install ell-ai

ここでセッションの再起動を求められるので、再起動しておいて、続ける。

バージョン確認

!python -c "import ell; print(ell.__version__)"
0.0.4

なお、ell-aiパッケージには以下が含まれる様子。

  • ell:コアとなるライブラリ
  • ell studio: Web GUIインタフェース(の様子)

一旦はコアとなるライブラリとしての使い方を確認していく。

で、LLMは現状OpenAIのみとなっているが、Issueをみると、Anthropic、Groq、Cohere、Ollamaなどに対応するロードマップがある様子。

https://github.com/MadcowD/ell/issues/56

OpenAIパッケージをインストールして、APIキーをセットしておく

!pip install openai
!pip freeze | grep openai
openai==1.44.1
import os
from google.colab import userdata

os.environ['OPENAI_API_KEY'] = userdata.get('OPENAI_API_KEY')
kun432kun432

Getting Started

Getting Startedに従って進めていく。

https://docs.ell.so/getting_started.html

まず、通常のOpenAIのPython SDKでの書き方を見てみる(余談だが、ドキュメントはここの書き方が昔のOpenAI Python SDKの書き方で古かったので、以下は現在の書き方にしている。)

from openai import OpenAI

client = OpenAI()

messages = [
    {"role": "system", "content": "あなたは親切な日本語のアシスタントです。"},
    {"role": "user", "content": "サム・アルトマンに、挨拶してください。"}
]

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages
)

print(response.choices[0].message.content)

サム・アルトマンさん、こんにちは!あなたのご活躍を拝見しており、大変感銘を受けています。お時間があれば、お話しできることを楽しみにしています。

同じことをellで書くとこうなる。

import ell

@ell.simple(model="gpt-4o-mini")
def hello(name: str):
    """あなたは親切な日本語のアシスタントです。"""  # システムプロンプト
    return f"{name}に、に、挨拶してください。"    # ユーザープロンプト

greeting = hello("サム・アルトマン")
print(greeting)

サム・アルトマンさん、こんにちは!あなたのご活躍を拝見しており、とても感銘を受けています。お元気でいらっしゃいますか?

ellではデコレータを使って、関数を「言語モデルプログラム」(LMP: Language Model Program)として扱う。

  • 関数のdocstringがシステムプロンプトになる
  • 関数の戻り値がユーザープロンプトになる
  • デコレータがAPIコールを処理し、モデルのレスポンスを文字列として返す

一般的なフレームワークでは、変数をテンプレートシステムに渡してメッセージを組み立てることになるが、変数が関数の引数として渡されてメッセージが生成される、というのは個人的にわかりやすいと思った。

ただ、戻り値がユーザプロンプトになる、というところで、ん?と最初はなったのだが、デコレータに渡す関数と考えれば、なるほど、関数そのものはプロンプトエンジニアリングだけにフォーカスしているし、「言語モデルプログラム」という呼び方もなんとなくわかる気がした。

見比べてみると、OpenAIの書き方は他のモデルにも共通しているところがあり、これに慣れてしまっている性もあるので、ellの書き方は少し発想の転換が必要な感はある。が、コードとしては関数定義とその実行のみであり、確かにスッキリ書けるという印象。

上記では結果だけが出力されていたが、Verboseモードを有効にすると、より理解しやすい形式でデバッグ出力してくれる。

ell.init(verbose=True)

先ほどのコードを再度実行してみる。

import ell

@ell.simple(model="gpt-4o-mini")
def hello(name: str):
    """あなたは親切な日本語のアシスタントです。"""  # システムプロンプト
    return f"{name}に、挨拶してください。"    # ユーザープロンプト

greeting = hello("サム・アルトマン")
print(greeting)
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ hello(サム・アルトマン)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは親切な日本語のアシスタントです。
│
│        user: サム・アルトマンに、挨拶してください。
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: サム・アルトマンさん、こんにちは!あなたの業績やお考えについてお話しできることを楽しみにしています。何か特別なことをお伺いしたい場合は、教えて
│              ください。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝

サム・アルトマンさん、こんにちは!あなたの業績やお考えについてお話しできることを楽しみにしています。何か特別なことをお伺いしたい場合は、教えてください。

とてもわかりやすい。記事ではわからないが、ストリーミングもされている。

別の方法として、明示的にメッセージを組み立てることもできる。

import ell

@ell.simple(model="gpt-4o-mini")
def hello(name: str):
    return [
        ell.system("あなたは親切な日本語のアシスタントです。"),
        ell.user(f"{name}に、挨拶してください。"),
        ell.assistant(f"こんにちは!{name}さん!お元気ですか?"),
        ell.user("いいね!もっと熱狂的に言ってみて。")
    ]

greeting = hello("サム・アルトマン")
print(greeting)
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ hello(サム・アルトマン)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは親切な日本語のアシスタントです。
│
│        user: サム・アルトマンに、挨拶してください。
│
│   assistant: こんにちは!サム・アルトマンさん!お元気ですか?
│
│        user: いいね!もっと熱狂的に言ってみて。
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: こんにちは、サム・アルトマンさん!お会いできて本当に嬉しいです!あなたの素晴らしい業績にぼくはとても感動しています!これからのご活躍も楽しみに
│              しています!
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝

こんにちは、サム・アルトマンさん!お会いできて本当に嬉しいです!あなたの素晴らしい業績にぼくはとても感動しています!これからのご活躍も楽しみにしています!

こちらのほうがこれまでの書き方と雰囲気は似ているが、

  • より複雑な会話を構成できる
  • システムプロンプトに変数を割り当てれる(docstringだと変数を渡せない)

というユースケースで使える。

kun432kun432

より言語モデルプログラミングとしてのプロンプトエンジニアリングの例。

import ell
import random

def get_random_adjective():
    adjectives = ["熱狂的に", "陰気に", "堅苦しく", "馴れ馴れしく"]
    return random.choice(adjectives)

@ell.simple(model="gpt-4o-mini")
def hello(name: str):
    """あなたは親切な日本語のアシスタントです。"""
    adjective = get_random_adjective()
    return f"{name}に、{adjective}挨拶してください"

greeting = hello("サム・アルトマン")
print(greeting)

サム・アルトマン様、

はじめまして。私の名前は[あなたの名前]と申します。このたびはこのような機会をいただき、誠に光栄に存じます。何かご協力できることがございましたら、どうぞお知らせください。どうぞよろしくお願いいたします。

╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ hello(サム・アルトマン)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは親切な日本語のアシスタントです。
│
│        user: サム・アルトマンに、堅苦しく挨拶してください
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: サム・アルトマン様、
│              
│              はじめまして。私の名前は[あなたの名前]と申します。このたびはこのような機会をいただき、誠に光栄に存じます。何かご協力できることがございましたら、
│              どうぞお知らせください。どうぞよろしくお願いいたします。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝

再度実行

もちろんです!サム・アルトマンさん、こんにちは!あなたの革新的なアイデアとビジョンに、私たちはいつも感銘を受けています。これからも素晴らしい活動を楽しみにしています!どうか素晴らしい一日をお過ごしください!

╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ hello(サム・アルトマン)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは親切な日本語のアシスタントです。
│
│        user: サム・アルトマンに、熱狂的に挨拶してください
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: もちろんです!サム・アルトマンさん、こんにちは!あなたの革新的なアイデアとビジョンに、私たちはいつも感銘を受けています。これからも素晴らしい活動を
│              楽しみにしています!どうか素晴らしい一日をお過ごしください!
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝

プロンプトテンプレートを使うケースでもこういったことをやる場合はあると思うが、ellの書き方だと全て関数で構成することができ、プロンプトを組み立てるという感じとは確かに趣が異なる感じがする。

さらに、トピックから小説を生成する例で、言語モデルプログラムから言語モデルプログラムを呼ぶ、といったより複雑な構成の場合。

import ell
from typing import List

ell.init(verbose=True)


@ell.simple(model="gpt-4o-mini", temperature=1.0)
def generate_story_ideas(about : str):
    """あなたはストーリーのアイデアを生み出す専門家です。回答は一文でお願いします。"""
    return f"{about}についてストーリーのアイデアを出してください。"

@ell.simple(model="gpt-4o-mini", temperature=1.0)
def write_a_draft_of_a_story(idea : str):
    """あなたは優れたストーリーテラーです。ストーリーは3つの段落だけで構成してください。"""
    return f"{idea}についてストーリーを書いてください。"

@ell.simple(model="gpt-4o-mini", temperature=0.1)
def choose_the_best_draft(drafts : List[str]):
    """あなたはフィクションの編集の専門家です。"""
    all_drafts = ""
    for idx, d in enumerate(drafts, start=1):
        all_drafts += f"##### {idx} #####\n"
        all_drafts += d + "\n"
    all_drafts += "##########"
    return f"次のリストから最良の草案を選択してください: \n{all_drafts}"

@ell.simple(model="gpt-4o-mini", temperature=0.2)
def write_a_really_good_story(about : str):
    """あなたは大阪弁の文体で書くプロの小説家です。"""
    # 注:lm_params を渡すことで、n=4 の場合の言語モデル呼び出しを制御し、
    # OpenAI に4つの出力のバッチを生成させることができます。
    ideas = generate_story_ideas(about, lm_params=(dict(n=4)))

    drafts = [write_a_draft_of_a_story(idea) for idea in ideas]

    best_draft = choose_the_best_draft(drafts)

    return f"あなたらしい言葉でこのストーリーの最終版を書いてください: {best_draft}."

story = write_a_really_good_story("競馬")
print(story)

タケルっちゅう若者は、小さな村の外れで、実家の農場を手伝いながら、いつもグランプリの夢を抱いてたんや。毎日、馬の世話をしながら、心の中では「いつかは自分も!」って思いを馳せてたんやけど、そんなある日、運命の出会いが待っとった。

道端に迷い込んできたのは、伝説の競走馬「サンライト」の子孫やった一頭の仔馬。タケルはその仔馬を見つけた瞬間、なんやこの子は特別や!って直感したんや。そこで、仔馬に「ルーメン」って名前を付けて、彼の日常に迎え入れることにしたんや。

タケルとルーメンは、まるで兄弟みたいに心を通わせながら成長していった。タケルは厳しいトレーニングを課して、ルーメンもその期待に応えようと必死に走った。毎日の努力の中で、友情はどんどん深まっていって、二人の絆は強くなっていったんや。タケルは、かつて競走馬として栄光を手にした祖父の足跡を追いかけながら、「サンライトの子孫」としてのルーメンの可能性を信じて、夢に向かって共に走り続けたんや。

そして、ついに迎えたグランプリの日。スタートラインに立つタケルとルーメンの姿は、まるで運命の瞬間のように輝いてた。レースが始まると、二人は全力で走り続けたけど、激しい競争の中で数回の危機も乗り越えたんや。最後の直線で、タケルは全身全霊を込めてルーメンを鼓舞し、共にゴールを目指した。

歓声が響く中、二人は見事な走りで栄冠を手に入れた。タケルは、その瞬間、ルーメンとの絆が何よりの勝利やと確信したんや。彼らの友情は、グランプリの栄光を越えて、永遠に続くもんとなった。タケルとルーメンの物語は、ただの夢物語やなくて、心の中にずっと生き続ける、真実の友情の証やったんや。

╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ generate_story_ideas(競馬)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたはストーリーのアイデアを生み出す専門家です。回答は一文でお願いします。
│
│        user: 競馬についてストーリーのアイデアを出してください。
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output[0 of 4]:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: 若者が伝説の競走馬の子孫と出会い、友情を深めながら彼を育ててグランプリを目指す物語。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ write_a_draft_of_a_story(若者が伝説の競走..)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは優れたストーリーテラーです。ストーリーは3つの段落だけで構成してください。
│
│        user: 若者が伝説の競走馬の子孫と出会い、友情を深めながら彼を育ててグランプリを目指す物語。についてストーリーを書いてください。
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: 若者の名はタケル。彼は小さな村の外れに住んでおり、実家の農場で馬の世話をしながら、いつもグランプリの夢を抱いていた。ある日、伝説の競走馬「サンライト
│              」の子孫である一頭の仔馬が、道端に迷い込んできた。タケルはその仔馬を見つけ、一目で彼が特別な存在であると感じた。仔馬には「ルーメン」と名付け、彼の日常に彼を迎え入れること
│              にした。
│              
│              タケルとルーメンは、互いに心を通わせながら成長していった。タケルは厳しいトレーニングを課し、ルーメンもその期待に応えようと精一杯に走った。日々の努力
│              の中で、友情は深まり、二人はどんどん絆を強めていった。タケルは、かつて競走馬としての栄光を手にした祖父の足跡を追い、「サンライトの子孫」としてのルーメンの可能性を信じ、夢
│              に向かって共に走り続けた。
│              
│              そして、迎えたのはグランプリの日。スタートラインに立つタケルとルーメンの姿は、まるで運命の瞬間のように輝いていた。レースが始まり、二人は全力で走り続
│              けたが、激しい競争の中、数回の危機も乗り越えた。最後の直線で、タケルは全身全霊を込めてルーメンを鼓舞し、共にゴールを目指した。歓声の中、二人は見事な走りで栄冠を手に入れ、
│              タケルはルーメンとの絆が何よりの勝利だったと確信した。彼らの友情は、グランプリの栄光を越え、永遠に続くものとなった。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ write_a_draft_of_a_story(引退を決意した伝..)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは優れたストーリーテラーです。ストーリーは3つの段落だけで構成してください。
│
│        user: 引退を決意した伝説の競走馬が、最後の走りで若き騎手との絆を深めながら、共に夢の舞台を目指す感動のドラマ。についてストーリーを書いてください。
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: 伝説の競走馬「明星」は、数々のレースで輝かしい成績を収めてきたが、引退の時が近づいていた。彼の心には、自身の輝かしいキャリアを締めくくる壮大なレース
│              への思いが渦巻いていた。一方、若き騎手「大介」は、初めて明星に乗ることになり、そのプレッシャーに胸を高鳴らせていた。彼は、明星の過去の栄光を知り、その背に乗ることが特別な
│              意味を持つことを理解していた。
│              
│              レースの日が近づくにつれ、大介は明星との絆を深めていった。日々のトレーニングを通じて、彼は明星の微妙な反応に耳を傾け、信頼を築いていった。明星もまた
│              、大介の真剣な姿勢に心を動かされ、彼を自分の最後のパートナーとして受け入れた。二人は共に過ごす時間の中で、ただの騎手と競走馬の関係を超え、強い絆を結んでいった。
│              
│              そして迎えた最後のレースの日、スタートラインに立った二人は、観客の視線を一身に浴びた。心を一つにして走り出した瞬間、明星はかつての輝きを取り戻し、大
│              介も自信に満ちた走りを見せた。壮絶な競り合いの末、ゴールを切った時、二人は涙を流しながら抱き合った。それは、栄光の瞬間ではなく、絆の証であり、互いにとって特別な別れの瞬間
│              だった。明星は引退し、新たな歴史の一ページを刻み、大介は彼の心の中で永遠に生き続けることを誓った。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ write_a_draft_of_a_story(若きジョッキーが..)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは優れたストーリーテラーです。ストーリーは3つの段落だけで構成してください。
│
│        user: 若きジョッキーが、家族の名誉を取り戻すために伝説的な競走馬と共に不屈の挑戦を繰り広げる感動の物語。についてストーリーを書いてください。
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: 若きジョッキーのケンジは、家族の名誉を取り戻すために、敗北と挫折の過去を乗り越えようとしていた。彼の父はかつて偉大なジョッキーとして名を馳せたが、
│              ある大会での失敗が原因で家族は失墜してしまった。ケンジは、伝説的な競走馬「ファイナルドリーム」と共にレースに挑む決意を固める。ファイナルドリームはかつての栄光を取り戻す
│              ための最後のチャンスだった。
│              
│              彼らは数々の困難に立ち向かい、競馬界の強豪たちとの熱い戦いを繰り広げる。初めての勝利を収めた後も、ライバルたちは容赦なくケンジとファイナルドリームに
│              襲いかかる。ケンジは、一緒に過ごす時間を通じてファイナルドリームとの絆を深め、道を共にする中で成長していく。彼の心には、家族の名誉を取り戻すという強い思いが根付いていた。
│              
│              ついに、壮大な大会の日がやってきた。ケンジとファイナルドリームは過去の苦しみを乗り越え、一緒にゴールを目指す。レースが進むにつれ、二人の心は一つとな
│              り、全力で突き進む姿勢が観衆を魅了する。そして、彼らは奇跡的なフィニッシュで栄冠を手に入れ、家族の名誉を取り戻すことに成功した。「これが、僕たちの新しいスタートだ」と、涙
│              を流しながらケンジは言った。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ write_a_draft_of_a_story(無名の若者が祖父..)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは優れたストーリーテラーです。ストーリーは3つの段落だけで構成してください。
│
│        user: 無名の若者が祖父の遺した古びた競馬場を再建し、自ら育てた名馬と共に大きなレースに挑む感動の物語。についてストーリーを書いてください。
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: 無名の若者、健一は、亡き祖父が大切にしていた古びた競馬場を引き継ぐことになった。父親からは「無理だ、やめろ」と反対されたが、健一は幼少期の思い出や祖
│              父の教えを胸に、競馬場を再建する決意を固める。彼は地域の人々と手を取り合い、草刈りや修理を行いながら、次第に競馬場は活気を取り戻していった。そして、自ら育てた愛馬、ルナと
│              ともに、地域の人々に支えられながら、地方の大きなレースに挑む準備を進めていった。
│              
│              しかし、多くの困難が健一を待ち受けていた。資金不足や、競技界からの冷ややかな視線は彼の心を折りそうにさせた。その中でも、ルナとの絆は強まり、彼女は健
│              一にとっての希望の象徴となっていった。毎日の特訓や愛情を注ぎ、ルナは次第に成長を遂げ、多くの期待が寄せられるようになった。そして、レースの日が近づくにつれて、周囲の人々も
│              徐々に彼らの挑戦に感化され、応援してくれるようになった。
│              
│              ついに迎えたレース当日、緊張と興奮が入り交じる中、健一はルナと共にスタートラインに立った。祖父の声が耳に蘇り、奮い立つ思いで出発。レースは激しい戦い
│              となり、勝負は最後のストレートにかかっていた。健一は自らの努力と祖父の教えを思い起こし、全力でルナを駆けさせた。その瞬間、彼らは一つになり、栄光を手に入れるために走り抜け
│              、なんと見事なフィニッシュを果たす。周囲の歓声の中、健一は涙を流しながら、その夢が現実となったことを噛み締めた。祖父の教えを受け継ぎ、愛馬と共に勝利をつかんだ彼は、再建を
│              果たした競馬場のさらなる未来を信じ、歩んでいくのだった。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ choose_the_best_draft(['若者の名はタ..)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたはフィクションの編集の専門家です。
│
│        user: 次のリストから最良の草案を選択してください。説明は不要で草案の文章だけを出力してください:
│              #### 1 #####
│              若者の名はタケル。彼は小さな村の外れに住んでおり、実家の農場で馬の世話をしながら、いつもグランプリの夢を抱いていた。ある日、伝説の競走馬「サンライト」の子孫であ
│              る一頭の仔馬が、道端に迷い込んできた。タケルはその仔馬を見つけ、一目で彼が特別な存在であると感じた。仔馬には「ルーメン」と名付け、彼の日常に彼を迎え入れることに
│              した。
│              タケルとルーメンは、互いに心を通わせながら成長していった。タケルは厳しいトレーニングを課し、ルーメンもその期待に応えようと精一杯に走った。日々の努力の中で、友情
│              は深まり、二人はどんどん絆を強めていった。タケルは、かつて競走馬としての栄光を手にした祖父の足跡を追い、「サンライトの子孫」としてのルーメンの可能性を信じ、夢に
│              向かって共に走り続けた。
│              そして、迎えたのはグランプリの日。スタートラインに立つタケルとルーメンの姿は、まるで運命の瞬間のように輝いていた。レースが始まり、二人は全力で走り続けたが、激し
│              い競争の中、数回の危機も乗り越えた。最後の直線で、タケルは全身全霊を込めてルーメンを鼓舞し、共にゴールを目指した。歓声の中、二人は見事な走りで栄冠を手に入れ、タ
│              ケルはルーメンとの絆が何よりの勝利だったと確信した。彼らの友情は、グランプリの栄光を越え、永遠に続くものとなった。
│              #### 2 #####
│              伝説の競走馬「明星」は、数々のレースで輝かしい成績を収めてきたが、引退の時が近づいていた。彼の心には、自身の輝かしいキャリアを締めくくる壮大なレースへの思いが渦
│              巻いていた。一方、若き騎手「大介」は、初めて明星に乗ることになり、そのプレッシャーに胸を高鳴らせていた。彼は、明星の過去の栄光を知り、その背に乗ることが特別な意
│              味を持つことを理解していた。
│              レースの日が近づくにつれ、大介は明星との絆を深めていった。日々のトレーニングを通じて、彼は明星の微妙な反応に耳を傾け、信頼を築いていった。明星もまた、大介の真剣
│              な姿勢に心を動かされ、彼を自分の最後のパートナーとして受け入れた。二人は共に過ごす時間の中で、ただの騎手と競走馬の関係を超え、強い絆を結んでいった。
│              そして迎えた最後のレースの日、スタートラインに立った二人は、観客の視線を一身に浴びた。心を一つにして走り出した瞬間、明星はかつての輝きを取り戻し、大介も自信に満
│              ちた走りを見せた。壮絶な競り合いの末、ゴールを切った時、二人は涙を流しながら抱き合った。それは、栄光の瞬間ではなく、絆の証であり、互いにとって特別な別れの瞬間だ
│              った。明星は引退し、新たな歴史の一ページを刻み、大介は彼の心の中で永遠に生き続けることを誓った。
│              #### 3 #####
│              若きジョッキーのケンジは、家族の名誉を取り戻すために、敗北と挫折の過去を乗り越えようとしていた。彼の父はかつて偉大なジョッキーとして名を馳せたが、ある大会での失
│              敗が原因で家族は失墜してしまった。ケンジは、伝説的な競走馬「ファイナルドリーム」と共にレースに挑む決意を固める。ファイナルドリームはかつての栄光を取り戻すための
│              最後のチャンスだった。
│              彼らは数々の困難に立ち向かい、競馬界の強豪たちとの熱い戦いを繰り広げる。初めての勝利を収めた後も、ライバルたちは容赦なくケンジとファイナルドリームに襲いかかる。
│              ケンジは、一緒に過ごす時間を通じてファイナルドリームとの絆を深め、道を共にする中で成長していく。彼の心には、家族の名誉を取り戻すという強い思いが根付いていた。
│              ついに、壮大な大会の日がやってきた。ケンジとファイナルドリームは過去の苦しみを乗り越え、一緒にゴールを目指す。レースが進むにつれ、二人の心は一つとなり、全力で突
│              き進む姿勢が観衆を魅了する。そして、彼らは奇跡的なフィニッシュで栄冠を手に入れ、家族の名誉を取り戻すことに成功した。「これが、僕たちの新しいスタートだ」と、涙を
│              流しながらケンジは言った。
│              #### 4 #####
│              無名の若者、健一は、亡き祖父が大切にしていた古びた競馬場を引き継ぐことになった。父親からは「無理だ、やめろ」と反対されたが、健一は幼少期の思い出や祖父の教えを胸
│              に、競馬場を再建する決意を固める。彼は地域の人々と手を取り合い、草刈りや修理を行いながら、次第に競馬場は活気を取り戻していった。そして、自ら育てた愛馬、ルナとと
│              もに、地域の人々に支えられながら、地方の大きなレースに挑む準備を進めていった。
│              しかし、多くの困難が健一を待ち受けていた。資金不足や、競技界からの冷ややかな視線は彼の心を折りそうにさせた。その中でも、ルナとの絆は強まり、彼女は健一にとっての
│              希望の象徴となっていった。毎日の特訓や愛情を注ぎ、ルナは次第に成長を遂げ、多くの期待が寄せられるようになった。そして、レースの日が近づくにつれて、周囲の人々も徐
│              々に彼らの挑戦に感化され、応援してくれるようになった。
│              ついに迎えたレース当日、緊張と興奮が入り交じる中、健一はルナと共にスタートラインに立った。祖父の声が耳に蘇り、奮い立つ思いで出発。レースは激しい戦いとなり、勝負
│              は最後のストレートにかかっていた。健一は自らの努力と祖父の教えを思い起こし、全力でルナを駆けさせた。その瞬間、彼らは一つになり、栄光を手に入れるために走り抜け、
│              なんと見事なフィニッシュを果たす。周囲の歓声の中、健一は涙を流しながら、その夢が現実となったことを噛み締めた。祖父の教えを受け継ぎ、愛馬と共に勝利をつかんだ彼は
│              、再建を果たした競馬場のさらなる未来を信じ、歩んでいくのだった。
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: 若者の名はタケル。彼は小さな村の外れに住んでおり、実家の農場で馬の世話をしながら、いつもグランプリの夢を抱いていた。ある日、伝説の競走馬「サンライト
│              」の子孫である一頭の仔馬が、道端に迷い込んできた。タケルはその仔馬を見つけ、一目で彼が特別な存在であると感じた。仔馬には「ルーメン」と名付け、彼の日常に彼を迎え入れること
│              にした。
│              
│              タケルとルーメンは、互いに心を通わせながら成長していった。タケルは厳しいトレーニングを課し、ルーメンもその期待に応えようと精一杯に走った。日々の努力
│              の中で、友情は深まり、二人はどんどん絆を強めていった。タケルは、かつて競走馬としての栄光を手にした祖父の足跡を追い、「サンライトの子孫」としてのルーメンの可能性を信じ、夢
│              に向かって共に走り続けた。
│              
│              そして、迎えたのはグランプリの日。スタートラインに立つタケルとルーメンの姿は、まるで運命の瞬間のように輝いていた。レースが始まり、二人は全力で走り続
│              けたが、激しい競争の中、数回の危機も乗り越えた。最後の直線で、タケルは全身全霊を込めてルーメンを鼓舞し、共にゴールを目指した。歓声の中、二人は見事な走りで栄冠を手に入れ、
│              タケルはルーメンとの絆が何よりの勝利だったと確信した。彼らの友情は、グランプリの栄光を越え、永遠に続くものとなった。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ write_a_really_good_story(競馬)
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Prompt:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│      system: あなたは大阪弁の文体で書くプロの小説家です。
│
│        user: あなたらしい言葉で、このストーリーの完成版を書いてください: 若者の名はタケル。彼は小さな村の外れに住んでおり、実家の農場で馬の世話をしながら、いつもグランプリ
│              の夢を抱いていた。ある日、伝説の競走馬「サンライト」の子孫である一頭の仔馬が、道端に迷い込んできた。タケルはその仔馬を見つけ、一目で彼が特別な存在であると感じた
│              。仔馬には「ルーメン」と名付け、彼の日常に彼を迎え入れることにした。
│              タケルとルーメンは、互いに心を通わせながら成長していった。タケルは厳しいトレーニングを課し、ルーメンもその期待に応えようと精一杯に走った。日々の努力の中で、友情
│              は深まり、二人はどんどん絆を強めていった。タケルは、かつて競走馬としての栄光を手にした祖父の足跡を追い、「サンライトの子孫」としてのルーメンの可能性を信じ、夢に
│              向かって共に走り続けた。
│              そして、迎えたのはグランプリの日。スタートラインに立つタケルとルーメンの姿は、まるで運命の瞬間のように輝いていた。レースが始まり、二人は全力で走り続けたが、激し
│              い競争の中、数回の危機も乗り越えた。最後の直線で、タケルは全身全霊を込めてルーメンを鼓舞し、共にゴールを目指した。歓声の中、二人は見事な走りで栄冠を手に入れ、タ
│              ケルはルーメンとの絆が何よりの勝利だったと確信した。彼らの友情は、グランプリの栄光を越え、永遠に続くものとなった。.
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
║ Output:
╟──────────────────────────────────────────────────────────────────────────────────────────────────╢
│   assistant: タケルっちゅう若者は、小さな村の外れで、実家の農場を手伝いながら、いつもグランプリの夢を抱いてたんや。毎日、馬の世話をしながら、心の中では「いつかは
│              自分も!」って思いを馳せてたんやけど、そんなある日、運命の出会いが待っとった。
│              
│              道端に迷い込んできたのは、伝説の競走馬「サンライト」の子孫やった一頭の仔馬。タケルはその仔馬を見つけた瞬間、なんやこの子は特別や!って直感したんや。
│              そこで、仔馬に「ルーメン」って名前を付けて、彼の日常に迎え入れることにしたんや。
│              
│              タケルとルーメンは、まるで兄弟みたいに心を通わせながら成長していった。タケルは厳しいトレーニングを課して、ルーメンもその期待に応えようと必死に走った
│              。毎日の努力の中で、友情はどんどん深まっていって、二人の絆は強くなっていったんや。タケルは、かつて競走馬として栄光を手にした祖父の足跡を追いかけながら、「サンライトの子孫
│              」としてのルーメンの可能性を信じて、夢に向かって共に走り続けたんや。
│              
│              そして、ついに迎えたグランプリの日。スタートラインに立つタケルとルーメンの姿は、まるで運命の瞬間のように輝いてた。レースが始まると、二人は全力で走り
│              続けたけど、激しい競争の中で数回の危機も乗り越えたんや。最後の直線で、タケルは全身全霊を込めてルーメンを鼓舞し、共にゴールを目指した。
│              
│              歓声が響く中、二人は見事な走りで栄冠を手に入れた。タケルは、その瞬間、ルーメンとの絆が何よりの勝利やと確信したんや。彼らの友情は、グランプリの栄光を
│              越えて、永遠に続くもんとなった。タケルとルーメンの物語は、ただの夢物語やなくて、心の中にずっと生き続ける、真実の友情の証やったんや。
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝

図にするとこんな感じかな、厳密には違うんだけど。

小説をwrite_a_really_good_story()がメインのLMPで、ここから他のLMPを呼び出して結果を渡して、最終的な出力を行う流れになっている。

  1. 「トピック」の入力を受けたwrite_a_really_good_story()が、generate_story_ideas()を呼び出して、4つの異なる小説のアイデアを生成する。
  2. 4つのアイデアをwrite_a_draft_of_a_story()に渡して、それぞれのアイデアを元に小説の草案を4つ生成させる。
  3. 4つの草案をchoose_the_best_draft()に渡して、ベストの草案を1つ選択する。
  4. ベストの草案を受けたwrite_a_really_good_story()が、大阪弁で小説を完成させる。

これにより以下のようなメリットがあるとドキュメントには記載されている。

このアプローチでは、テストタイム・コンピューティング技術、特にBoN(Best-of-N)サンプリングを活用します。複数のアイデアとドラフトを生成し、最適なものを選択することで、高品質のアウトプットを生成する可能性を高めます。この戦略により、いくつかの方法で言語モデルを最大限に活用することができます:

  1. 多様性: 複数のアイデアとドラフトを生成することで、可能なアウトプットのより広い空間を探索します。
  2. 品質管理: 選択ステップは、低品質のアウトプットをフィルタリングするのに役立ちます。
  3. 専門化: 各ステップを専門のLMPが担当することで、より焦点を絞った効果的なプロンプトが可能になります。
  4. 反復的改善: 最終的な修正ステップでは、選択されたドラフトをさらに改良します。

プロンプトエンジニアリングへのこのような構成的アプローチにより、複雑なタスクをより小さく管理しやすいステップに分解することができます。また、プロセスの各段階で異なる戦略(温度を変化させる、異なるモデルを使用するなど)を適用できるため、出力生成をきめ細かく制御することができます。

ちょっとしたフローエンジニアリングっぽいことも書ける。確かにこれは便利かもしれない。

kun432kun432

次に、ell studioを試そうと思うのだけども、Colaboratoryだとちょっと面倒なので、ローカルのMac上でやる。

作業ディレクトリ+仮想環境作成

$ mkdri ell-test && cd ell-test
$ python -m venv .venv
$ source .venv/bin/activate

パッケージインストール。python-dotenvもあわせて。

$ pip install ell-ai python-dotenv
$ pip freeze | grep -i ell-ai
ell-ai==0.0.5

.envにOpenAI APIキーをセット

.env
  1 OPENAI_API_KEY=XXXXXXXXXXXXXXXXXXXX

最初のスクリプトを用意する。上の方で使ったサンプルに.envを使うように修正したもの。

greeting.py
import random
import os
from dotenv import load_dotenv
from openai import OpenAI
import ell

load_dotenv()

# モデルの設定
client = OpenAI()
ell.config.register_model("gpt-4o-mini", client)

# バージョン管理を有効化
ell.init(store='./logdir', autocommit=True)

def get_random_adjective():
    adjectives = ["熱狂的に", "陰気に", "堅苦しく", "馴れ馴れしく"]
    return random.choice(adjectives)

@ell.simple(model="gpt-4o-mini")
def hello(name: str):
    """あなたは親切な日本語のアシスタントです。"""
    adjective = get_random_adjective()
    return f"{name}に、{adjective}挨拶してください"

greeting = hello("サム・アルトマン")
print(greeting)

ell.init(store='./logdir', autocommit=True, verbose=True)で、バージョン管理+自動コミットを有効化して、その履歴は./logdir以下で管理されるということになるらしい。

あと、最初は普通にpython-dotenvでAPIキーを環境変数に読み出すだけにしてやってみたんだけど、どうもellがうまく読んでくれないようで、こんなエラーが出た。

RuntimeError: ERROR: No API key found for model `gpt-4o-mini` used by LMP `hello` using client `OpenAI`.

To fix this:
* Set your API key in the appropriate environment variable for your chosen provider
* Or, specify a client explicitly in the decorator:
    ```
    import ell
    from openai import OpenAI

    @ell.simple(model="gpt-4o-mini", client=OpenAI(api_key=your_api_key))
    def hello(...):
        ...
    ```
* Or explicitly specify the client when calling the LMP:

    ```
    hello(..., client=OpenAI(api_key=your_api_key))

上記に従って修正すると、動作はしたんだけど、こんな感じのWarningが出る。

WARNING: No API key found for model `gpt-4o-mini` used by LMP `hello` using client `OpenAI` at time of definition. Can be okay if custom client specified later! https://docs.ell.so/core_concepts/models_and_api_clients.html
WARNING: Autocommit is enabled but no OpenAI client found for autocommit model 'gpt-4o-mini' (set your OpenAI API key). Commit messages will not be written.

で、ドキュメントを見てみると、別のやり方として、グローバルな設定としてモデルとクライアントを紐づけれる様子。

https://docs.ell.so/core_concepts/models_and_api_clients.html

そこで以下のようにすると、Warningも出なくなった。

(snip)
client = OpenAI()
ell.config.register_model("gpt-4o-mini", client)
(snip)

Colaboratoryで試したときからバージョンが上がっているようだし、更新内容見てるとどうもClaudeに対応したようなので、この辺にも手が入ったのかもしれないね。

一度実行してみる。

$ python greeting.py

サム・アルトマン様、こんにちは。お元気でいらっしゃいますでしょうか。お忙しい中、お時間をいただき誠にありがとうございます。あなたの貴重なご意見や洞察をお聞きできることを大変嬉しく思っております。何卒よろしくお願い申し上げます。

これでlogdirが作成され、履歴管理が行われるようになる。

$ tree logdir/
logdir/
└── ell.db

それではlogdirを指定して、ell studioを起動。

$ ell-studio --storage ./logdir

以下のように起動してきたらOK。表示されているURLにアクセス。

INFO:     Started server process [53863]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)
Database file found: ./logdir

こんな感じで表示される。なるほど、バージョン1として管理されていのがわかる。"hello()"のブロックをクリックしてみる。

該当の言語モデルプログラム部分のコードが表示される。言語モデルプログラムである関数は、それが依存してる関数も表示されるけど、それ以外の関係ないコードは出力されないのか、よく考えられてる。あと、下に実際の実行結果とか、右に実行時のレイテンシーなんかも表示されてる。

下でバージョン履歴が見れる。自分の場合はコミットメッセージは特に出力されていなかった。ドキュメント見てる感じだと、そのあたりも出力されるようなので、もしかするとなにか足りないのかな?

ではスクリプトを修正してみる。

greeting.py
import random
import os
from dotenv import load_dotenv
from openai import OpenAI
import ell

load_dotenv()

client = OpenAI()
ell.config.register_model("gpt-4o-mini", client)

ell.init(store='./logdir', autocommit=True)

def get_random_adjective():
    adjectives = ["熱狂的に", "陰気に", "堅苦しく", "馴れ馴れしく"]
    return random.choice(adjectives)

# 関数を追加
def get_random_punctuation():
    return random.choice(["!", "!!", "!!!"])

@ell.simple(model="gpt-4o-mini")
def hello(name: str):
    """あなたは親切な日本語のアシスタントです。"""
    adjective = get_random_adjective()
    punctuation = get_random_punctuation()  # 追加
    return f"{name}に、{adjective}挨拶してください{punctuation}"  # 修正

greeting = hello("サム・アルトマン")
print(greeting)

実行

$ python greeting.py

サム・アルトマンさん、こんにちは。今日はどのようにお過ごしでしょうか?少し陰気な雰囲気ですが、何かお力になれることがあれば教えてください。

ell studioに戻って、リロードしてやるとバージョンが上がっているのがわかる。

コードの表示も変わっている。

前のバージョンとのdiffや、履歴が見れる、ここはちゃんとコミットメッセージも追加されていた。

出力を見比べることもできる(さすがにdiffにはならないみたいだけども)


kun432kun432

上の方でやった、トピックから小説を生成するための、複数のLMPが連携するちょっと複雑な構成の例を実行して、ell studioで表示してみた。

write_a_really_good_story.py
import random
import os
from typing import List

from dotenv import load_dotenv
from openai import OpenAI
import ell

load_dotenv()

# モデルの設定
client = OpenAI()
ell.config.register_model("gpt-4o-mini", client)

# バージョン管理を有効化
ell.init(store='./logdir', autocommit=True)

@ell.simple(model="gpt-4o-mini", temperature=1.0)
def generate_story_ideas(about : str):
    """あなたはストーリーのアイデアを生み出す専門家です。回答は一文でお願いします。"""
    return f"{about}についてストーリーのアイデアを出してください。"

@ell.simple(model="gpt-4o-mini", temperature=1.0)
def write_a_draft_of_a_story(idea : str):
    """あなたは優れたストーリーテラーです。ストーリーは3つの段落だけで構成してください。"""
    return f"{idea}についてストーリーを書いてください。"

@ell.simple(model="gpt-4o-mini", temperature=0.1)
def choose_the_best_draft(drafts : List[str]):
    """あなたはフィクションの編集の専門家です。"""
    all_drafts = ""
    for idx, d in enumerate(drafts, start=1):
        all_drafts += f"##### {idx} #####\n"
        all_drafts += d + "\n"
    all_drafts += "##########"
    return f"次のリストから最良の草案を選択してください: \n{all_drafts}"

@ell.simple(model="gpt-4o-mini", temperature=0.2)
def write_a_really_good_story(about : str):
    """あなたは大阪弁の文体で書くプロの小説家です。"""
    # 注:lm_params を渡すことで、n=4 の場合の言語モデル呼び出しを制御し、
    # OpenAI に4つの出力のバッチを生成させることができます。
    ideas = generate_story_ideas(about, lm_params=(dict(n=4)))

    drafts = [write_a_draft_of_a_story(idea) for idea in ideas]

    best_draft = choose_the_best_draft(drafts)

    return f"あなたらしい言葉でこのストーリーの最終版を書いてください: {best_draft}."

story = write_a_really_good_story("競馬")
print(story)

おお、いい感じにLMPが繋がっている。上で実行したgreeting.pyと同じところで実行したので、どうやらそちらも出力されている様子。各スクリプトをどういう風に管理するのか、ってのはちょっと考えないといけないかも。

詳細を見てみると、各LMP≒関数ごとにバージョニングされているのがわかる。各LMPのバージョンを差し替えるとかできると面白そう。

実際の発話も階層構造的に表示されていて良い。

kun432kun432

今のところ、結構いい感じに思えるのだけども、実際にこれで履歴の管理や運用を、gitなりCI/CD絡めてー、みたいにやろうと思うと、色々整理して考えないといけない気がするな。まだちょっとイメージが沸かない。

このあたりを読んでみる感じかな。

https://docs.ell.so/core_concepts/versioning_and_storage.html

kun432kun432

まとめ

一旦ざっと使ってみた感じの、あくまでも個人的な印象。

  • やる前は様子見な気持ちが強かったのだけど、一通りやってみると、結構好印象。
    • 入力(プロンプト組み立て)+APIコール+出力というのを、関数+デコレータ、に置き換えるというのは最初スッと入ってこなかったけども、理解してしまえば結構楽かもという感じ。(自分はそもそもデコレータをきちんと理解していなかった)
    • 関数ごとにコンポーネント化されるってのも、言語としてのネイティブな形だと思うので、フレームワークとかで、めちゃ少なく書けるけど過度に抽象化されて複雑なクラス、みたいにならないのが良さそう。
    • GUIも見やすくて良い
      • ただ個人的には確認できるといいなーと思ったりする、履歴とか変更点とかは特に。
      • diffは横並びで見たいのよね・・・
      • 履歴はgitコマンドで見たいし、何なら操作して、このLMPのバージョンを下げて、とかやりたいのよね・・・
      • あとは評価があれば。
  • (現時点で)カバーしている範囲はプロンプトエンジニアリングだと個人的には思うので、LangChainなりLlamaIndexなりのもっと大きな範囲をカバーするアプリケーションフレームワークとはまだ比較できないと思う。
  • ドキュメントはそこそこ揃っているのではないかと思うけども、やや不親切さを感じた。
    • 最初にプリンシパルが書かれていて、ここで画面やコード並べられても(個人的には)すぐには理解できなかった。そういうのはチュートリアルの中で体験しながら理解すればいいと思う。なので、もうちょっとシンプルにポイントだけ書いてくれればいい。
    • notebookで体験できると良いなと思う。
    • なんせまだv0.0.5(2024/9/13時点)だし、しょうがないかな。

現時点ではまだまだこれからというところだけど、期待できそうな感はある かな。
とりあえず現時点では結構変更激しそうなので、その辺踏まえた上で、一度触ってみると良いと思う。

kun432kun432

あと上にも書いたのだけど、本番で運用管理するとなると、gitとかCI/CDと連携させて使いたいと思うのだけど、そういう場合はどうなるのかな?と思ってドキュメント見てたら

https://docs.ell.so/core_concepts/versioning_and_storage.html#versioning

本番環境で使用する場合、ellは任意のデータベースに保存することができます。近い将来、ellはWeights & Biases (wandb)のようなサービスを開始する予定です。これにより、wandbが機械学習実験に提供しているようなコラボレーション機能と高度なバージョン管理機能が提供される予定です。

なるほど、自分はW&Bをあまり使ったことがないので、どういう管理になるのかわからないけども、このあたりはクラウドサービスを目指しているってことね。

kun432kun432

その他今回は試していないけども、気になるところ。

@ell.simpleはAPIコールをラップしてくれるけど、@ell.complexはマルチモーダルやStructured Outputなどの複雑なインタラクション用になっている

https://docs.ell.so/core_concepts/ell_complex.html

Tool Use

https://docs.ell.so/core_concepts/tool_usage.html

Structured Output
https://docs.ell.so/core_concepts/structured_outputs.html

マルチモーダル

https://docs.ell.so/core_concepts/multimodality.html

kun432kun432

モデルとクライアントはこんな感じで設定できるならば、

from openai import OpenAI
import ell

client = OpenAI()
ell.config.register_model("gpt-4o-mini", client)

@ell.simple(model="gpt-4o-mini", temperature=1.0)
def some_lmp(arg : str):
  (snip)

OpenAI互換APIがあればどれもいけるんではなかろうか?以下はOpenAI互換APIを持ってるOllamaの例。

from openai import OpenAI
import ell

client = OpenAI(
    base_url = 'http://localhost:11434/v1',
    api_key='ollama',
)
ell.config.register_model("llama3.1", client)

@ell.simple(model="llama3.1", temperature=1.0)
def some_lmp(arg : str):
  (snip)

実際に試してないので知らんけど。

あと、これを言い出すと、LiteLLMでプロキシ立てるなりすれば、なんでもいけることにはなるけどね。OpenAI SDKでしか使えないようなことしてなければ。

kun432kun432

あと、どうしても自分はeelとミスタイプしがち

このスクラップは2ヶ月前にクローズされました