見出し画像

LLMと調和したプログラミングを体験させてくれるMarvinというライブラリが面白い

過去に「個別の関数を実装をプロンプトで行うような流れは既にきている」と言ってみてはいたものの、考え方としては関数としてプロンプトを実行する、ぐらいの世界観が関の山で、ソフトウェア設計としてLLMとの調和を考えるという域までは達していませんでした。

なんですが、何というかAI時代のプログラムコードってこんな感じになっていくのかなーと思わせるライブラリが登場していたので紹介したいと思います。

Marvinとはどんな感じのライブラリなのか

その名もMarvinと言います。

コンセプトから入るとイメージしづらい気がしたので、いきなり実例から説明させてください。

例えばなんか適当にエナジードリンクをリストしてくれるような関数が欲しかったとしますね?(そんなケースあるかな)

そういう場合はこんな風に書けます。

from marvin import ai_fn

@ai_fn
def list_energy_drinks(n: int) -> list[str]:
    """Generate a list of n energy drinks"""

list_energy_drinks(3)

['Red Bull', 'Monster', 'Rockstar']

@ai_fnデコレータを関数につけて、プロンプトをあらわすdocstringを仕込んでおけば、関数実行時にプロンプトを実行し、関数の戻り型の形で値を返してくれるという仕組みです。

なので、例えばこれをlist[str](文字列のリスト)ではなく、str(文字列)で返して欲しい場合はこんな感じに書くとstr型で返してくれる関数になります。

from marvin import ai_fn

@ai_fn
def list_energy_drinks(n: int) -> str:
    """Generate a list of n energy drinks"""

list_energy_drinks(3)

"['Red Bull', 'Monster Energy', 'Rockstar Energy']"

ここまでの例だけでは単なるお遊びな感じですが、例えば「あるURLを与えたときにそのページ内のキーワードを抽出して欲しい場合」はこんな風に書くことができます。

import httpx
from marvin import ai_fn

@ai_fn(llm_model_name='gpt-3.5-turbo', llm_model_temperature=0)
def _get_keywords(text: str) -> list[str]:
    """extract 10 keywords from string"""

def get_keywords_from_url(url: str) -> list[str]:
    """extract keywords from string"""
    response = httpx.get(url)
    # NOTE: トークン数オーバー対策のために3000文字に丸めている
    return _get_keywords(response.content[:3000].decode('utf-8'))

get_keywords_from_url("https://note.com/mahlab/n/n0c116e38a5b5")

['データ定義形式',
'ChatGPT',
'生成',
'mah_lab',
'西見',
'公宏',
'行政',
'統計文章',
'非構造化データ',
'構造化データ']

「実装のない関数がコメントの通りに動作する」って、何か凄くないですか?

更に面白いのがPydanticのモデルに適用する例です。

例えば名前、電話番号、メールアドレスを含む文章があったときに、これをあるクラスの値としてマッピングしたい、というニーズがあったとします。

そんなコードがこんな風に書けます。

from marvin import ai_model
import pydantic
from typing import Optional

@ai_model
class Resume(pydantic.BaseModel):
    first_name: str
    last_name: str
    phone_number: Optional[str]
    email: str

Resume('Ford Prefect • (555) 5124-5242 • ford@prefect.io')

Resume(first_name='Ford', last_name='Prefect', phone_number='(555) 5124-5242', email='ford@prefect.io')

文字列情報がオブジェクトにマッピングできる!

更に複雑な例として会話文を与えたときに、話している人物名と必要なアクションを抽出する、なんてこともできたりします。

from marvin import ai_model
import datetime
from typing import Literal, List
import pydantic

class ActionItem(pydantic.BaseModel):
    responsible: str
    description: str
    deadline: Optional[datetime.datetime]
    time_sensitivity: Literal['low', 'medium', 'high']

@ai_model
class Conversation(pydantic.BaseModel):
    '''A class representing a team converastion'''
    participants: List[str]
    action_items: List[ActionItem]


result = Conversation('''
Adam: Hey Jeremiah can you approve my PR? I requested you to review it.
Jeremiah: Yeah sure, when do you need it done by?
Adam: By this Friday at the latest, we need to ship it by end of week.
Jeremiah: Oh shoot, I need to make sure that Nate and I have a chance to chat first.
Nate: Jeremiah we can meet today to chat.
Jeremiah: Okay, I'll book something for today.
''').json(indent = 2)
print(result)
{
  "participants": [
    "Adam",
    "Jeremiah",
    "Nate"
  ],
  "action_items": [
    {
      "responsible": "Jeremiah",
      "description": "Approve Adam's PR",
      "deadline": "2023-05-19T00:00:00+09:00",
      "time_sensitivity": "high"
    },
    {
      "responsible": "Jeremiah",
      "description": "Meet with Nate to discuss PR",
      "deadline": "2023-05-15T00:00:00+09:00",
      "time_sensitivity": "high"
    }
  ]
}

項目を抽出するプロンプトだけならサクッと書けるとは思いますが、これをサクッとオブジェクトにマッピングしてくれるところが素晴らしいです。

Marvinのコンセプト

と、ここまで見てきたとおり、Marvinのコンセプトは大きく以下の2つです。

AI Functions
ソースコードに依存せず、LLMをランタイムとして使用し、AIでオンデマンドに出力を生成する。コード書くのではなく、ニーズを書くことで欲しい出力を得られるようになる。

AI Models
構造化されていないコンテキスト(文字列)をPydanticで表現されるモデルスキーマで表されるようなデータ構造に変換する。これによってAIによる推論出力をプログラム上で扱いやすいタイプセーフなデータ構造として扱うことが可能になる。

関数の例で実際に見てみましたが、AI Functionsの動きはプロンプトを通して受け取った任意の値を任意の型に変換するような動きだとも言えます。これをMarvinでは「functional prompt engineering(関数型プロンプトエンジニアリング)」だなんて言っていたりします。

冒頭ではシンプルに文字列のリストを生成するようなコードを例にしていましたが、例えば返り値の型をPydanticのモデルにすることで、モデル定義に合ったデータを生成することだってできます。

from marvin import ai_fn
import pydantic

class Drink(pydantic.BaseModel):
    name: str
    price: int
    brand: str

@ai_fn
def list_energy_drinks(n: int) -> list[Drink]:
    """Generate a list of n energy drinks"""

list_energy_drinks(3)

[Drink(name='Red Bull', price=200, brand='Red Bull GmbH'),
Drink(name='Monster Energy', price=250, brand='Monster Beverage Corporation'),
Drink(name='Rockstar Energy Drink', price=180, brand='PepsiCo')]

他にもBotが作れたり、そのBotを動かすUIが提供されていたり、LangChainみたいにLoaderが用意されていたりと幅広い機能を提供しているライブラリなのですが、個人的にはもっとこの2つのコンセプトに関連した機能が安全かつ高速に動作するアップデートに注力して欲しいなと感じました(例えばキャッシュ機構だとか、トークン数オーバー時の対策フックを入れられるとか)。

何にせよ、LLMを活用した未来のプログラミングを体験させてくれるという意味で、とても今後が楽しみなライブラリだと思っています。みなさんもぜひ遊んでみてください!

現場からは以上です。

この記事が気に入ったらサポートをしてみませんか?