llama-index
llama-indexとは
LlamaIndex (GPT Index) is a project that provides a central interface to connect your LLM’s with external data.
LlamaIndex (GPT Index)は、あなたのLLMと外部データを接続するための中央インターフェースを提供するプロジェクトです。
https://gpt-index.readthedocs.io/en/stable/index.html
LLMと外部データソースがつながると何故嬉しいのか
LLMを個人データで補強することができる。
LLMを個人データで補強するための手段
fine-tuning
事前トレーニング済みモデルの重みが新しいデータでトレーニングされる転移学習へのアプローチ
https://en.wikipedia.org/wiki/Fine-tuning_(machine_learning)
プロンプトに文脈を埋め込む
https://speakerdeck.com/ryoheiigushi/chatgpt-apinoembedding-kasutamaisuru-men?slide=15 より引用
※ llama-indexはこの方法を簡単に実現できるようにしてくれる
具体的にできること
- API、PDF、SQLなどの外部データソースを取得する
- 構造化データと非構造化データのインデックス
参考: https://gpt-index.readthedocs.io/en/stable/index.html#proposed-solution
検証環境
python -V
Python 3.9.16
パッケージのインストール
pip install llama-index
pip show llama-index
Name: llama-index
Version: 0.6.0
Summary: Interface between LLMs and your data
いろんな外部データソースと接続してみる
ローカルにあるデータからドキュメントを作成する
データをテキストファイルとして保存しておく。
今回はリコリス・リコイル - アニヲタWiki(仮) - atwiki(アットウィキ)をコピペ
from llama_index import GPTVectorStoreIndex, SimpleDirectoryReader
# dataディレクトリ配下にテキストファイルを置いておく
documents = SimpleDirectoryReader('data').load_data()
index = GPTVectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("リコリコとは?")
print(response)
リコリコとは、社会福祉公社が開発したリコリス(Robot Corps)を操作するためのプログラムである。リコリスは戦闘技術だけでなく、他言語の取得や車の運転技術取得も必須であり、取れなければリコリスになれないとの事。リコリスは制服の色でランクが区別され、エース扱いのファーストが赤、そのファーストを補助するセカンドが紺、そのさらに下の使い捨てモブサードリコリスがベージュとなっている。
URLを指定してドキュメントを作成する
参考:https://llamahub.ai/l/web-simple_web
データソース
リコリス・リコイル - Wikipedia
リコリス・リコイル - アニヲタWiki(仮) - atwiki(アットウィキ)
リコリス・リコイル(漫画)- マンガペディア
from llama_index import download_loader, GPTVectorStoreIndex
SimpleWebPageReader = download_loader("SimpleWebPageReader")
loader = SimpleWebPageReader()
urls = [
'https://ja.wikipedia.org/wiki/%E3%83%AA%E3%82%B3%E3%83%AA%E3%82%B9%E3%83%BB%E3%83%AA%E3%82%B3%E3%82%A4%E3%83%AB',
'https://w.atwiki.jp/aniwotawiki/pages/51534.html',
'https://mangapedia.com/%E3%83%AA%E3%82%B3%E3%83%AA%E3%82%B9%E3%83%BB%E3%83%AA%E3%82%B3%E3%82%A4%E3%83%AB-wa9beuxlr',
]
documents = loader.load_data(urls=urls)
index = GPTVectorStoreIndex.from_documents(documents)
# indexをローカルに保存
index.storage_context.persist(persist_dir="./storage")
# indexをロードする場合は
# from llama_index import StorageContext, load_index_from_storage
# storage_context = StorageContext.from_defaults(persist_dir="<persist_dir>")
# index = load_index_from_storage(storage_context)
query_engine = index.as_query_engine()
response = query_engine.query("リコリコとは?")
print(response)
リコリコは、都内某所の小さな喫茶店であり、美味しいコーヒーや甘いスイーツが評判なだけでなく、ご町内の困りごとを解決するボランティアも行っているという場所です。
response = query_engine.query("リコリコの主人公について教えて下さい")
錦木千束がリコリコの主人公です。17歳で、いつも明朗快活で、人との間に壁を作らず、他人にも作らせない程人懐っこい。どんな依頼にも一生懸命であり、その裏表のない人柄は多くの人を惹きつける。一方、年齢の割にどこか達観した部分もある。
その他
Llama Hubに利用できるデータソースのリストがある
自分でデータソースを追加することも可能
LLMのカスタマイズ
参考:https://gpt-index.readthedocs.io/en/stable/guides/primer/usage_pattern.html#customizing-llm-s
詳細:https://gpt-index.readthedocs.io/en/stable/how_to/customization/custom_llms.html
from llama_index import LLMPredictor, GPTVectorStoreIndex, PromptHelper, ServiceContext, SimpleDirectoryReader
from langchain import OpenAI
llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="gpt-3.5-turbo"))
# define prompt helper
# set maximum input size
max_input_size = 4096
# set number of output tokens
num_output = 256
# set maximum chunk overlap
max_chunk_overlap = 20
prompt_helper = PromptHelper(max_input_size, num_output, max_chunk_overlap)
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor, prompt_helper=prompt_helper)
documents = SimpleDirectoryReader('data').load_data()
index = GPTVectorStoreIndex.from_documents(documents, service_context=service_context)
query_engine = index.as_query_engine()
response = query_engine.query("リコリコとは?")
print(response)
"""
リコリコは、戦闘技術や他言語の取得、車の運転技術などが必須とされる、特殊な兵士のことを指す。
彼女たちは、洗脳されていないが、教育の質や待遇が良いため、上官の命令に忠実であり、暴走・反逆の危険性も殺人への忌避感もない理想的な兵隊に仕上がっている。
制服の色でランクが区別されており、エース扱いのファーストが赤、そのファーストを補助するセカンドが紺、そのさらに下の使い捨てモブサードリコリスはベージュとなっている。
"""
プロンプトのカスタマイズ
from llama_index import QuestionAnswerPrompt, GPTVectorStoreIndex, SimpleDirectoryReader
# load documents
documents = SimpleDirectoryReader('data').load_data()
# define custom QuestionAnswerPrompt
QA_PROMPT_TMPL = (
"We have provided context information below. \n"
"---------------------\n"
"{context_str}"
"\n---------------------\n"
"Given this information, please answer the question: {query_str}\n"
"you should answer the question in the following format:\n"
"question: {query_str}\n"
"answer: [here is your answer]"
)
QA_PROMPT = QuestionAnswerPrompt(QA_PROMPT_TMPL)
index = GPTVectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(text_qa_template=QA_PROMPT)
response = query_engine.query("リコリコとは?")
print(response)
Answer: リコリコとは、社会福祉公社が開発したリコリス(Robot Corps)を操作するためのプログラムである。リコリスは、戦闘技術だけでなく、他言語の取得や車の運転技術取得も必須であり、取れなければリコリスになれないとの事。リコリスは、薬物・催眠等を用いた洗脳を施されていないが、上官の命令に忠実であり、暴走・反逆の危険性も殺人への忌避感もない理想的な兵隊
response = query_engine.query("主人公は誰ですか?")
print(response)
question: 主人公は誰ですか?
answer: 錦木千束と井ノ上たきなです。
参考
query engineのカスタマイズ
0.6.0からhigh-level-apiとLow-level APIが提供された
細かい解説はこちら
細かい設定
High-level API
from llama_index import StorageContext, load_index_from_storage
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)
query_engine = index.as_query_engine()
response = query_engine.query("リコリコの主な登場人物は?")
print(response)
"""The main characters of Rikoriko are 錦木千束 and 井ノ上たきな."""
Low-level API
from llama_index import (
GPTVectorStoreIndex,
ResponseSynthesizer,
)
from llama_index.retrievers import VectorIndexRetriever
from llama_index.query_engine import RetrieverQueryEngine
from llama_index.indices.postprocessor import SimilarityPostprocessor
# build index
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)
# configure retriever
retriever = VectorIndexRetriever(
index=index,
similarity_top_k=1,
)
# configure response synthesizer
response_synthesizer = ResponseSynthesizer.from_args(
node_postprocessors=[
SimilarityPostprocessor(similarity_cutoff=0.7)
]
)
# assemble query engine
query_engine = RetrieverQueryEngine(
retriever=retriever,
response_synthesizer=response_synthesizer,
)
# query
response = query_engine.query("リコリコの主な登場人物は?")
print(response)
"""
1. 井ノ上たきな
2. エリカ
3. 真島
4. マシンガンを殲滅した武器商人達
5. アランチルドレン
"""
indexについて
langchainを利用したchat bot
from langchain.chains.conversation.memory import ConversationBufferMemory
from llama_index.langchain_helpers.agents import LlamaToolkit, create_llama_chat_agent, IndexToolConfig
from llama_index.indices.query.query_transform.base import DecomposeQueryTransform
from llama_index import StorageContext, load_index_from_storage, LLMPredictor
from langchain import OpenAI
llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, max_tokens=512))
decompose_transform = DecomposeQueryTransform(llm_predictor, verbose=True)
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)
query_engine = index.as_query_engine()
# tool config
graph_config = IndexToolConfig(
query_engine=query_engine,
name=f"ricorico Index",
description="useful for when you want to answer queries that ricorico or リコリコ can answer",
tool_kwargs={"return_direct": True}
)
toolkit = LlamaToolkit(index_configs=[graph_config])
memory = ConversationBufferMemory(memory_key="chat_history")
llm=OpenAI(temperature=0)
agent_chain = create_llama_chat_agent(
toolkit,
llm,
memory=memory,
verbose=True
)
while True:
text_input = input("User: ")
response = agent_chain.run(input=text_input)
print(f'Agent: {response}')