LangChainのTracingをためしてみる
LangChainのデバッグ
LangChainで何が行われているかを見るためにはverbose=True
をつけるのが一般的だと思うけど、例えばLLMに投げているプロンプトを全部見たい、とかは、使うChainやAgentなどによってはできないものがある。
でこれを使うとできそうな雰囲気があるのでためしてみる。
環境
- Mac
- Docker Desktop
- python-3.10.11
手順
仮想環境作る
$ pyenv install 3.10.11
$ pyenv virtualenv 3.10.11 langchain-tracing
$ mkdir langchain-tracing && cd langchain-tracing
$ pyenv local langchain-tracing
パッケージインストール
$ pip install openai langchain python-dotenv
langchain-serverを起動
$ langchain-server
langchain-db, langchain-backend, langchain-frontendの3つのコンテナが立ち上がる。
WARNING: Compose V1 is no longer supported and will be removed from Docker Desktop in an upcoming release. See https://docs.docker.com/go/compose-v1-eol/
Pulling langchain-db ... done
Pulling langchain-backend ... done
Pulling langchain-frontend ... done
WARNING: Compose V1 is no longer supported and will be removed from Docker Desktop in an upcoming release. See https://docs.docker.com/go/compose-v1-eol/
Creating network "langchain_default" with the default driver
Creating langchain_langchain-db_1 ... done
Creating langchain_langchain-backend_1 ... done
Creating langchain_langchain-frontend_1 ... done
Attaching to langchain_langchain-db_1, langchain_langchain-backend_1, langchain_langchain-frontend_1
以下のように表示されればOK。http://localhost:4173/
がTracingのインタフェースらしい。
langchain-frontend_1 | PUBLIC_BASE_URL is set to: http://localhost:8000
langchain-frontend_1 | DEV_MODE is set to: true
langchain-frontend_1 | ➜ Local: http://localhost:4173/
langchain-frontend_1 | ➜ Network: http://172.19.0.4:4173/
ブラウザでhttp://localhost:4173/
にアクセスするとこんな感じ。
ドキュメントを読むと、この「セッション」をスクリプト側と紐付けておくことで、実行された内容がトレースできる、というものらしい。
とりあえずSession: default
をクリックしてみる。
当然ながらなにもない。
ではスクリプトを実行してみる。環境変数LANGCHAIN_HANDLER
を有効にする。今回はOpenAIのAPIキーもどうせ読み込まないといけないので.envに記述した。
LANGCHAIN_HANDLER=langchain
OPENAI_API_KEY=xxxxxxxxxx
とりあえずLangChainのシンプルなサンプルを使ってみる。なおLANGCHAIN_HANDLER
はLangChainの各モジュールをインポートする前に設定しないといけない様子。なのでdotenvで.envを読み出してからLangChainをインポートしている。
import os
from dotenv import load_dotenv
load_dotenv()
from langchain import PromptTemplate, OpenAI, LLMChain
prompt_template = "What is a good name for a company that makes {product}?"
llm = OpenAI(temperature=0)
llm_chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template(prompt_template),
#verbose=True
)
print(llm_chain("colorful socks"))
実行してみる。
$ python sample.py
{'product': 'colorful socks', 'text': '\n\nSocktastic!'}
verbose=True
を有効にしてみる。
(snip)
llm_chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template(prompt_template),
verbose=True
)(snip)
LLMChainだと出力されるんだよねー。よいサンプルではなかったが、とりあえず。
$ python sample.py
> Entering new LLMChain chain...
Prompt after formatting:
What is a good name for a company that makes colorful socks?
> Finished chain.
{'product': 'colorful socks', 'text': '\n\nSocktastic!'}
実行した分だけこういう感じでInput/Outputのトレースが表示される。
なるほど、コンポーネントの親子関係みたいな感じでまとまっているっぽい。
Explorerをクリックすると、クリップボードにコピペできたり、子コンポーネントがあれば同じように中身を辿れる。
RetrivalQAChainでためしてみる。
$ pip install chromadb tiktoken
LangChainのVectorDBサンプルでよく出てくるやつを持ってくる。
$ wget https://raw.githubusercontent.com/hwchase17/langchain/master/docs/modules/state_of_the_union.txt
サンプルコード
import os
from dotenv import load_dotenv
load_dotenv()
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain.llms import OpenAI
from langchain.chains import RetrievalQA
from langchain.document_loaders import TextLoader
loader = TextLoader("state_of_the_union.txt")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
texts = text_splitter.split_documents(documents)
embeddings = OpenAIEmbeddings()
docsearch = Chroma.from_documents(texts, embeddings)
qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=docsearch.as_retriever())
query = "What did the president say about Ketanji Brown Jackson"
print(qa.run(query))
実行してみる
$ python sample2.py
(warning的なものはsnip)
The president said that Judge Ketanji Brown Jackson is one of our nation's top legal minds, and will continue Justice Breyer's legacy of excellence.
verbose=True
にして再度実行してみる。
(snip)
qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=docsearch.as_retriever(), verbose=True)
(snip)
$ python sample2.py
(warning的なものはsnip)
> Entering new RetrievalQA chain...
> Finished chain.
The president said that Ketanji Brown Jackson is one of the nation's top legal minds and will continue Justice Breyer's legacy of excellence.
そう、これなのよね、verbose=True
が役に立たない。
Tracingでみてみるとこういう感じ。
一番下のllmのところを見てみると、こういう感じでLLMに投げられたプロンプトが見える。
プロンプトはこんな感じ。
Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.
And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.
Last month, I announced our plan to supercharge
the Cancer Moonshot that President Obama asked me to lead six years ago.
This is personal to me and Jill, to Kamala, and to so many of you.
Vice President Harris and I ran for office with a new economic vision for America.
Question: What did the president say about Ketanji Brown Jackson
Helpful Answer:
chain_type
をmap_reduce
に変えてみる。
(snip)
qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="map_reduce", retriever=docsearch.as_retriever(), verbose=True)
(snip)
$ python sample2.py
(warning的なものはsnip)
> Entering new RetrievalQA chain...
> Finished chain.
The president said that he nominated Circuit Court of Appeals Judge Ketanji Brown Jackson to continue Justice Breyer's legacy of excellence.
Tracingでみると、2回LLMに投げられているのがわかる。
1回目は、VectorDBからの検索結果をひとつづつ、質問に対して関連性があるかを確認している。
2回目は、上記で関連性があると判断されたものだけをLLMに渡して最終的な回答を生成している。
まとめ
複雑なChainやAgentの場合はverbose=True
だけでは全ての情報を見ることができないが、Tracing環境を用意すれば細かいところ、例えばLLMに投げるプロンプトとかが見えるようになる。
自分の場合は、
- RetrievalQAChainのプロンプトテンプレートを少しカスタマイズしてみたい
- でもデフォルトのプロンプトがどうなってるのかわからない
- LangChainのコードをざっくり追いかけてみたけど、QAChainはchain_typeでプロンプト変わる?
- 各chain_typeごとのプロンプトのコード、ちょっとわかりにくい
みたいな感じで、Tracingは十分役に立ちそうだと思った。それ以外でもデバッグ目的で使うのは良さそう。
あと、セッションは以下のように定義しておけば、分けることができるので、チームで開発している場合なんかでも使えそうではある(事前にGUIでセッションを作成しておく必要がある)
os.environ["LANGCHAIN_SESSION"] = "my_session"
んー、というかこれはdevcontainerにいれておきたいなー、docker-outside-of-dockerにしないといけなさそうな気がするけど。そしたらセッションとか考えなくて良さそうだし。