Closed4

LlamaIndexモジュールガイドを試してみる: Models

kun432kun432

LLMs

https://docs.llamaindex.ai/en/stable/module_guides/models/llms.html

  • テキスト補完とチャットをサポート
  • ストリーミング・ノンストリーミングをサポート
  • 同期・非同期をサポート

使い方

OpenAI Completions API(もはやレガシーだけども)を使う場合

from llama_index.llms import OpenAI

llm = OpenAI()

res = llm.complete("富士山の高さは、")
print(res)

3776メートルです。

OpenAI Chat Completions APIの場合は、ChatMessageオブジェクトを配列に入れて渡す

from llama_index.llms import ChatMessage, OpenAI

messages = [
    ChatMessage(
        role="system", content="あなたはユーザーからの質問に何でも回答するアシスタントです。回答は、平易な言葉遣いでわかりやすく説明します。"
    ),
    ChatMessage(role="user", content="富士山の高さについて教えて。"),
]

llm = OpenAI()

res = llm.chat(messages)
print(res)

assistant: 富士山は、日本の山であり、標高は3,776メートルです。これは、日本で最も高い山です。富士山は、山岳信仰の対象としても知られており、多くの人々が登山や観光に訪れます。

パラメータを変更する場合。

from llama_index.llms import ChatMessage, OpenAI

messages = [
    ChatMessage(
        role="system", content="あなたはユーザーからの質問に何でも回答するアシスタントです。回答は、平易な言葉遣いでわかりやすく説明します。"
    ),
    ChatMessage(role="user", content="富士山の高さについて教えて。"),
]

llm = OpenAI(model="gpt-4-1106-preview", temperature=0.3)

res = llm.chat(messages)
print(res)

assistant: 富士山の高さは、約3,776メートルです。これは日本で一番高い山の高さで、山梨県と静岡県の県境に位置しています。富士山はその美しい形と雄大さで有名で、世界文化遺産にも登録されています。また、日本のシンボルとしても広く知られており、多くの人々に親しまれています。

ストリーミング

from llama_index.llms import ChatMessage, OpenAI

messages = [
    ChatMessage(
        role="system", content="あなたはユーザーからの質問に何でも回答するアシスタントです。回答は、平易な言葉遣いでわかりやすく説明します。"
    ),
    ChatMessage(role="user", content="富士山の高さについて教えて。"),
]

llm = OpenAI()

res = llm.stream_chat(messages)

answer = []
for r in res:
    answer.append(r.delta)
    print(r.delta, end="")

print()
print(answer)

富士山は、日本の山であり、標高は3,776メートルです。これは、日本で最も高い山です。富士山は、山岳信仰の対象としても知られており、多くの人々が登山や観光に訪れます。
['', '富', '士', '山', 'は', '、', '日', '本', 'の', '山', 'で', 'あり', '、', '標', '高', 'は', '3', ',', '776', 'メ', 'ート', 'ル', 'です', '。', 'これ', 'は', '、', '日', '本', 'で', '最', 'も', '高', 'い', '山', 'です', '。', '富', '士', '山', 'は', '、', '山', '岳', '信', '仰', 'の', '対', '象', 'と', 'して', 'も', '知', 'ら', 'れ', 'て', 'お', 'り', '、', '多', 'く', 'の', '人', '々', 'が', '登', '山', 'や', '観', '光', 'に', '訪', 'れ', 'ます', '。', '']

モデルが対応している場合はFunction Callingも使える。

from pydantic import BaseModel
from llama_index.llms.openai_utils import to_openai_tool
from pprint import pprint


class Song(BaseModel):
    """架空の曲名とアーティスト名を生成する"""

    song_name: str
    artist: str


song_fn = to_openai_tool(Song)

pprint(song_fn)

song_fnはこんな感じで、Function Callingの関数定義が作成される。

{
  'function': {
    'description': '架空の曲名とアーティスト名を生成する',
    'name': 'Song',
    'parameters': {
      'description': '架空の曲名とアーティスト名を生成する',
      'properties': {
        'artist': {
          'title': 'Artist',
          'type': 'string'
        },
        'song_name': {
          'title': 'Song Name',
          'type': 'string'
        }
      },
      'required': ['song_name', 'artist'],
      'title': 'Song',
      'type': 'object'
    }
  },
 'type': 'function'
}

呼んでみる。

from llama_index.llms import OpenAI

llm = OpenAI()

response = llm.complete("曲を生成して", tools=[song_fn])
tool_calls = response.additional_kwargs["tool_calls"]
print(tool_calls)

こんな感じで関数名+引数が返ってくる

[
  ChatCompletionMessageToolCall(
    id='call_N90wvAVAq3QFCqSFMMowj64a',
    function=Function(
      arguments='{\n  "song_name": "夢の中で会いましょう",\n  "artist": "星野源"\n}', name='Song'), type='function'
    )
]
kun432kun432

異なるモデルを使う場合はそれに合わせて読み込むモジュールを変える。対応しているモデルは以下にある。
https://docs.llamaindex.ai/en/stable/module_guides/models/llms/modules.html

たとえば、BedrockのClaude-2.1を使ってみた。

https://docs.llamaindex.ai/en/stable/examples/llm/bedrock.html

from llama_index.llms import ChatMessage, Bedrock

messages = [
    ChatMessage(
        role="system", content="あなたはユーザーからの質問に何でも回答するアシスタントです。回答は、平易な言葉遣いでわかりやすく説明します。"
    ),
    ChatMessage(role="user", content="富士山の高さについて教えて。"),
]

llm = Bedrock(
    model="anthropic.claude-v2:1",
    aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'],
    aws_region_name="us-east-1"
)

res = llm.chat(messages)
print(res)

assistant: 富士山の高さは約3,776メートルです。

日本で最も高い山で、世界でも有数の高山です。頂上付近は一年のうち約5か月は雪に覆われています。富士山は完全な円錐形の山で、そのシンメトリーの美しさと神秘性から、日本人にとって特別な存在とされています。

ドキュメントには(まだ)載ってないけど、CohereとかGoogle Geminiとかも使えそう。

https://github.com/run-llama/llama_index/tree/main/llama_index/llms

kun432kun432

Embeddings

https://docs.llamaindex.ai/en/stable/module_guides/models/embeddings.html

いきなりservice contextとかvector indexの話が出てくるけども、まずは普通にembedding apiを叩いてみる。

from llama_index.embeddings import OpenAIEmbedding

embed_model = OpenAIEmbedding()

embeddings = embed_model.get_text_embedding("富士山の高さは?")
print(len(embeddings))
print(embeddings[0:3])
1536
[0.023977849632501602, 0.004216551780700684, -0.01968788169324398]

Service Context

RAGにおけるベクトル化プロセスは一般的に2つある。

  1. ドキュメントをベクトル化してベクトルDBに入れる。
  2. ベクトルDBで検索する際にクエリをベクトル化する。

このあたりで都度都度get_text_embeddingするのは手間がかかる。そこでService Contextを使う。

https://docs.llamaindex.ai/en/stable/module_guides/supporting_modules/service_context.html

ServiceContextは、LlamaIndexパイプライン/アプリケーションのインデックス作成とクエリ段階で使用される、一般的に使用されるリソースのバンドルです。これを使用して、グローバルな設定や、パイプラインの特定の部分におけるローカルな設定を行うことができます。

以下のような感じで設定しておくと、インデックス作成時とクエリ時の両方でembeddingsにOpenAI Embeddingsが使用される。

from llama_index import ServiceContext, set_global_service_context
from llama_index.embeddings import OpenAIEmbedding

embed_model = OpenAIEmbedding()
service_context = ServiceContext.from_defaults(embed_model=embed_model)
set_global_service_context(service_context)

ドキュメントを準備。ここはnpaka先生の記事にあるスクリプトをまるっと使わせてもらった。

サンプルとして以下のWikipediaの記事を使用してインデックスを作成している。

https://ja.wikipedia.org/wiki/ドウデュース

https://ja.wikipedia.org/wiki/イクイノックス

from pathlib import Path
import requests

# Wikipediaからのデータ読み込み
wiki_titles = ["イクイノックス", "ドウデュース"]
for title in wiki_titles:
    response = requests.get(
        "https://ja.wikipedia.org/w/api.php",
        params={
            "action": "query",
            "format": "json",
            "titles": title,
            "prop": "extracts",
            # 'exintro': True,
            "explaintext": True,
        },
    ).json()
    page = next(iter(response["query"]["pages"].values()))
    wiki_text = page["extract"]

    data_path = Path("data")
    if not data_path.exists():
        Path.mkdir(data_path)

    with open(data_path / f"{title}.txt", "w") as fp:
        fp.write(wiki_text)

ではインデックスを作成。

from llama_index import VectorStoreIndex, SimpleDirectoryReader

documents = SimpleDirectoryReader("./data").load_data()

index = VectorStoreIndex.from_documents(documents)

クエリを実行

query_engine = index.as_query_engine()

response = query_engine.query("ダービーを勝ったのは?")
print(response)

ドウデュース

response = query_engine.query("ダービーを勝ったのは?")
print(response)

イクイノックス

VectorStoreIndexとかSimpleDirectoryReaderとかは別の所で見ていくので、ここでは深く触れない。

このスクラップは2023/12/27にクローズされました