LangChainを学ぶ(2024年3月)
下記のコース(英語)でLangChainを学んでいく際の2024年3月時点の躓きポイントの突破メモです。ファイルをダウンロードもしくはコピペして、WindowsのWSL2でUbuntuを使って試しています。
当方、カウンセリングAIを作っているわけですが、LangChainめっちゃ使えそう。
コース1
コース2
【注意】今は LangChain Expression Language (LCEL) という書き方が標準なようです。この講座で使われているものとは違うかも。
コース1
L1-Model_prompt_parser
ファイルダウンロードできない問題
File→Download as→Notebook (.ipynb) でダウンロードできるはずなんですが、できません。(L4以降はできます)
コピペしました。ウェブ上のNotebookのセルをクリックした状態でCtrl+Aで、全選択できます。ローカルのNotebookに貼り付けると、1つのセルにまとまってしまいますが、半写経的に1セル分ずつコピペして実行してました。
ちなみに、おそらく古いファイルだと思いますが、こちらにもあります。
.env の作成
'OPENAI_API_KEY' は、私の環境では、.env という名前のテキストファイルを作って、入れました。
.env の中身:
'OPENAI_API_KEY' = 'sk-faldkjgaoijeafXXX'
# 右側にはご自身で取得したキーを入れてください。
openai.ChatCompletion.create() の変更
openai.chat.completions.create() に変わってます。
L3-Chains
Data_1_3.csv がない
たぶんないと思います。自分で作りました。2列しかないので、それぞれCSVに貼り付けてご利用ください。
1列目:
Product
Queen Size Sheet Set
Waterproof Phone Pouch
Luxury Air Mattress
Pillows Insert
Milk Frother Handheld\n
L'Or Espresso Café \n
Hervidor de Agua Eléctrico
2列目:
Review
I ordered a king size set. My only criticism would be that I wish seller would offer the king size set with 4 pillowcases. I separately ordered a two pack of pillowcases so I could have a total of four. When I saw the two packages, it looked like the color did not exactly match. Customer service was excellent about sending me two more pillowcases so I would have four that matched. Excellent! For the cost of these sheets, I am satisfied with the characteristics and coolness of the sheets.
I loved the waterproof sac, although the opening was made of a hard plastic. I don’t know if that would break easily. But I couldn’t turn my phone on, once it was in the pouch.
This mattress had a small hole in the top of it (took forever to find where it was), and the patches that they provide did not work, maybe because it's the top of the mattress where it's kind of like fabric and a patch won't stick. Maybe I got unlucky with a defective mattress, but where's quality assurance for this company? That flat out should not happen. Emphasis on flat. Cause that's what the mattress was. Seriously horrible experience, ruined my friend's stay with me. Then they make you ship it back instead of just providing a refund, which is also super annoying to pack up an air mattress and take it to the UPS store. This company is the worst, and this mattress is the worst.
This is the best throw pillow fillers on Amazon. I’ve tried several others, and they’re all cheap and flat no matter how much fluffing you do. Once you toss these in the dryer after you remove them from the vacuum sealed shipping material, they fluff up great
\xa0I loved this product. But they only seem to last a few months. The company was great replacing the first one (the frother falls out of the handle and can't be fixed). The after 4 months my second one did the same. I only use the frother for coffee once a day. It's not overuse or abuse. I'm very disappointed and will look for another. As I understand they will only replace once. Anyway, if you have one good luck.
Je trouve le goût médiocre. La mousse ne tient pas, c'est bizarre. J'achète les mêmes dans le commerce et le goût est bien meilleur...\nVieux lot ou contrefaçon !?
Está lu bonita calienta muy rápido, es muy funcional, solo falta ver cuánto dura, solo llevo 3 días en funcionamiento.
L4-QnA
ValidationError
これは結構時間取られました。ここで出ます↓
llm_replacement_model = OpenAI(temperature=0,
model='gpt-3.5-turbo-instruct')
response = index.query(query,
llm = llm_replacement_model)
エラーの内容を全部載せるとこんな感じ
ValidationError Traceback (most recent call last)
Cell In[44], line 4
1 llm_replacement_model = OpenAI(temperature=0,
2 model='gpt-3.5-turbo-instruct')
----> 4 response = index.query(query,
5 llm = llm_replacement_model)
File ~/.local/lib/python3.10/site-packages/langchain/indexes/vectorstore.py:46, in VectorStoreIndexWrapper.query(self, question, llm, retriever_kwargs, **kwargs)
42 retriever_kwargs = retriever_kwargs or {}
43 chain = RetrievalQA.from_chain_type(
44 llm, retriever=self.vectorstore.as_retriever(**retriever_kwargs), **kwargs
45 )
---> 46 return chain.run(question)
File ~/.local/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:145, in deprecated.<locals>.deprecate.<locals>.warning_emitting_wrapper(*args, **kwargs)
143 warned = True
144 emit_warning()
--> 145 return wrapped(*args, **kwargs)
File ~/.local/lib/python3.10/site-packages/langchain/chains/base.py:545, in Chain.run(self, callbacks, tags, metadata, *args, **kwargs)
543 if len(args) != 1:
544 raise ValueError("`run` supports only one positional argument.")
--> 545 return self(args[0], callbacks=callbacks, tags=tags, metadata=metadata)[
546 _output_key
...
Field required [type=missing, input_value={'embedding': [0.00328532... -0.021133498095708216]}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.6/v/missing
metadata
Field required [type=missing, input_value={'embedding': [0.00328532... -0.021133498095708216]}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.6/v/missing
Google検索
でました!
結局、これでいけます。
%pip install chromadb
# インデックスの作成に用いるクラスをインポート
from langchain.indexes import VectorstoreIndexCreator
from langchain.indexes.vectorstore import VectorStoreIndexWrapper
# ベクターストアの作成
index: VectorStoreIndexWrapper = VectorstoreIndexCreator().from_loaders([loader])
query = "Please list all your shirts with sun protection \
in a table in markdown and summarize each one."
CSVとインデックス後の中身を確認したい
CSVの中身の確認:
# import pandas as pd
# # CSVファイルのパス
# file_path = 'OutdoorClothingCatalog_1000.csv'
# # CSVファイルの読み込み
# df_pre = pd.read_csv(file_path)
# # 先頭の数行を表示
# print("処理前のCSVファイルの内容:")
# print(df_pre.head())
インデックス後の中身の確認:
from langchain.document_loaders import CSVLoader
from langchain.vectorstores import DocArrayInMemorySearch
from langchain.indexes import VectorstoreIndexCreator
# CSVファイルからデータを読み込む
loader = CSVLoader(file_path=file_path)
data = loader.load()
# 文書のインデックスを作成
index = VectorstoreIndexCreator(
vectorstore_cls=DocArrayInMemorySearch
).from_loaders([loader])
# 処理後のデータの例を表示(ここでは原始データの一部として)
print("処理後のデータの例(原始データからの抽出):")
print(data[:1]) # 最初の1つの文書を表示
docs = db.similarity_search(query) でエラー
ValidationError Traceback (most recent call last)
Cell In[29], line 1
----> 1 docs = db.similarity_search(query)
File ~/.local/lib/python3.10/site-packages/langchain_community/vectorstores/docarray/base.py:127, in DocArrayIndex.similarity_search(self, query, k, **kwargs)
115 def similarity_search(
116 self, query: str, k: int = 4, **kwargs: Any
117 ) -> List[Document]:
118 """Return docs most similar to query.
119
120 Args:
(...)
125 List of Documents most similar to the query.
126 """
--> 127 results = self.similarity_search_with_score(query, k=k, **kwargs)
128 return [doc for doc, _ in results]
File ~/.local/lib/python3.10/site-packages/langchain_community/vectorstores/docarray/base.py:106, in DocArrayIndex.similarity_search_with_score(self, query, k, **kwargs)
94 """Return docs most similar to query.
95
96 Args:
(...)
103 Lower score represents more similarity.
104 """
...
Field required [type=missing, input_value={'embedding': [-0.0222515... -0.022251551228798728]}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.6/v/missing
metadata
Field required [type=missing, input_value={'embedding': [-0.0222515... -0.022251551228798728]}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.6/v/missing
前回と同じ感じだけどどう対処していいかわからん。
困ったのでGoogle検索
見つかる。はじめからこのサイトに沿ってやればいいのではないかと思いつつ…
これでいけました!たぶん…
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
from langchain_community.vectorstores import Chroma
# Load the document, split it into chunks, embed each chunk and load it into the vector store.
raw_documents = CSVLoader(file_path=file).load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)
db = Chroma.from_documents(documents, OpenAIEmbeddings())
response = llm.call_as_llm(f"{qdocs} Question: Please list all your \ shirts with sun protection in a table in markdown and summarize each one.") でエラー
とりあえずサイトを確認
いや、リミットがあるのはわかっとんじゃい!
これでいけました。
# 最初の20の文書だけを使用
qdocs = "".join([docs[i].page_content for i in range(min(20, len(docs)))])
response = llm.call_as_llm(f"{qdocs} Question: Please list all your \
shirts with sun protection in a table in markdown and summarize each one.")
ChatGPT 4 に色々聞いてみた
L4コードの解説
後半部分も
Indexの使い方
IndexとRetrievalQAの違い
IndexとRetrievalQAの使い分け
L5-Evaluation
qa.run(examples[0]["query"])で、エラー
qaは…
qa = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=index.vectorstore.as_retriever(),
verbose=True,
chain_type_kwargs = {
"document_separator": "<<<<>>>>>"
}
)
indexが入ってんな。ここじゃね。
ということで、indexの定義をこれに変えて、一件落着。
# インデックスの作成に用いるクラスをインポート
from langchain.indexes import VectorstoreIndexCreator
from langchain.indexes.vectorstore import VectorStoreIndexWrapper
index: VectorStoreIndexWrapper = VectorstoreIndexCreator().from_loaders([loader])
GPT-4の解説
Manual Evaluationの解説
LLM assisted evaluation
predictions = qa.apply(examples)でエラー
ValueError Traceback (most recent call last)
Cell In[73], line 1
----> 1 predictions = qa.apply(examples)
File ~/.local/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:145, in deprecated.<locals>.deprecate.<locals>.warning_emitting_wrapper(*args, **kwargs)
143 warned = True
144 emit_warning()
--> 145 return wrapped(*args, **kwargs)
File ~/.local/lib/python3.10/site-packages/langchain/chains/base.py:708, in Chain.apply(self, input_list, callbacks)
703 @deprecated("0.1.0", alternative="batch", removal="0.2.0")
704 def apply(
705 self, input_list: List[Dict[str, Any]], callbacks: Callbacks = None
706 ) -> List[Dict[str, str]]:
707 """Call the chain on all inputs in the list."""
--> 708 return [self(inputs, callbacks=callbacks) for inputs in input_list]
File ~/.local/lib/python3.10/site-packages/langchain/chains/base.py:708, in <listcomp>(.0)
703 @deprecated("0.1.0", alternative="batch", removal="0.2.0")
704 def apply(
705 self, input_list: List[Dict[str, Any]], callbacks: Callbacks = None
706 ) -> List[Dict[str, str]]:
707 """Call the chain on all inputs in the list."""
--> 708 return [self(inputs, callbacks=callbacks) for inputs in input_list]
...
277 missing_keys = set(self.input_keys).difference(inputs)
278 if missing_keys:
--> 279 raise ValueError(f"Missing some input keys: {missing_keys}")
ValueError: Missing some input keys: {'query'}
どうやら、ここがうまくいってないらしい。
examples += new_examples
examples
[{'query': 'Do the Cozy Comfort Pullover Set have side pockets?',
'answer': 'Yes'},
{'query': 'What collection is the Ultra-Lofty 850 Stretch Down Hooded Jacket from?',
'answer': 'The DownTek collection'},
{'qa_pairs': {'query': "What is the weight per pair of the Women's Campside Oxfords?",
'answer': "The approximate weight per pair of the Women's Campside Oxfords is 1 lb. 1 oz."}},
{'qa_pairs': {'query': 'What is the material used to construct the Recycled Waterhog dog mat?',
'answer': 'The Recycled Waterhog dog mat is constructed from 24 oz. polyester fabric made from 94% recycled materials, with a rubber backing.'}},
{'qa_pairs': {'query': "What are the features of the Infant and Toddler Girls' Coastal Chill Swimsuit, Two-Piece?",
'answer': "The swimsuit features bright colors, ruffles, and exclusive whimsical prints. The four-way-stretch and chlorine-resistant fabric helps the swimsuit keep its shape and resist snags. The swimsuit has a UPF 50+ rated fabric that provides the highest rated sun protection possible, blocking 98% of the sun's harmful rays. The crossover no-slip straps and fully lined bottom ensure a secure fit and maximum coverage. The swimsuit can be machine washed and line dried for best results."}},
{'qa_pairs': {'query': 'What is the percentage of recycled nylon in the body of the Refresh Swimwear V-Neck Tankini Contrasts?',
'answer': 'The body of the Refresh Swimwear V-Neck Tankini Contrasts is made of 82% recycled nylon.'}},
{'qa_pairs': {'query': "What is the main feature of the EcoFlex 3L Storm Pants' TEK O2 technology?",
'answer': "The main feature of the EcoFlex 3L Storm Pants' TEK O2 technology is its exceptional breathability, which is the most breathable the company has ever tested."}}]
と、いうことで修正。
# 新しい例から 'query' と 'answer' のペアを抽出し、元の examples に追加する
for item in new_examples:
# item が 'qa_pairs' キーを持っていることを確認
if 'qa_pairs' in item:
# 'qa_pairs' キーから 'query' と 'answer' を取り出す
extracted_example = {
"query": item['qa_pairs']['query'],
"answer": item['qa_pairs']['answer']
}
# 抽出した例を元の examples リストに追加
examples.append(extracted_example)
for i, eg in enumerate(examples):…でエラー
Example 0:
Question: Do the Cozy Comfort Pullover Set have side pockets?
Real Answer: Yes
Predicted Answer: Yes, the Cozy Comfort Pullover Set has side pockets.
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[95], line 6
4 print("Real Answer: " + predictions[i]['answer'])
5 print("Predicted Answer: " + predictions[i]['result'])
----> 6 print("Predicted Grade: " + graded_outputs[i]['text'])
7 print()
KeyError: 'text'
シンプルにミスってる気がする。次のように修正。これでいいんだよね?
for i, eg in enumerate(examples):
print(f"Example {i}:")
print("Question: " + eg['query']) # 修正: examples から 'query' を取得
print("Real Answer: " + eg['answer']) # 修正: examples から 'answer' を取得
print("Predicted Answer: " + predictions[i]['result']) # 予測された答え
print("Predicted Grade: " + graded_outputs[i]['results']) # 修正: graded_outputs から 'results' を取得
print()
LLM assisted evaluationの解説
LangChain evaluation の補足
L6-Agents
おそらくtoolsの読み込みでエラー
仮想環境を作ってPythonを入れなおしてもダメ…
検索したらあった!
とりあえずこれで一部クリア
pip install langchain_experimental
from langchain_experimental.agents.agent_toolkits import create_python_agent
しかし、これが通らん!
from langchain.tools.python.tool import PythonREPLTool
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[18], line 4
2 from langchain.agents import load_tools, initialize_agent
3 from langchain.agents import AgentType
----> 4 from langchain.tools.python.tool import PythonREPLTool
ModuleNotFoundError: No module named 'langchain.tools.python.tool'
なんだかlangchainのバージョンを上げろとか下げろとか言われている…
とりあえずアンインストールして、再インストールしたら普通にできた…
【ターミナルで】
pip uninstall langchain
pip install langchain
わけわからん
import langchain
print(langchain.__version__)
## 0.1.11
もしかしたらディレクトリ名に半角スペースがあったのが原因かも!ご注意を。
Wikipediaがうまく動かず
result = agent(question)
---------------------------------------------------------------------------
SSLEOFError Traceback (most recent call last)
File ~/.local/lib/python3.10/site-packages/urllib3/connectionpool.py:467, in HTTPConnectionPool._make_request(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)
466 try:
--> 467 self._validate_conn(conn)
468 except (SocketTimeout, BaseSSLError) as e:
File ~/.local/lib/python3.10/site-packages/urllib3/connectionpool.py:1099, in HTTPSConnectionPool._validate_conn(self, conn)
1098 if conn.is_closed:
-> 1099 conn.connect()
1101 # TODO revise this, see https://github.com/urllib3/urllib3/issues/2791
File ~/.local/lib/python3.10/site-packages/urllib3/connection.py:653, in HTTPSConnection.connect(self)
651 server_hostname_rm_dot = server_hostname.rstrip(".")
--> 653 sock_and_verified = _ssl_wrap_socket_and_match_hostname(
654 sock=sock,
655 cert_reqs=self.cert_reqs,
656 ssl_version=self.ssl_version,
657 ssl_minimum_version=self.ssl_minimum_version,
658 ssl_maximum_version=self.ssl_maximum_version,
659 ca_certs=self.ca_certs,
660 ca_cert_dir=self.ca_cert_dir,
661 ca_cert_data=self.ca_cert_data,
662 cert_file=self.cert_file,
663 key_file=self.key_file,
...
--> 517 raise SSLError(e, request=request)
519 raise ConnectionError(e, request=request)
521 except ClosedPoolError as e:
SSLError: HTTPSConnectionPool(host='en.wikipedia.org', port=443): Max retries exceeded with url: /w/api.php?prop=extracts&explaintext=&exintro=&titles=Tom+M.+Mitchell&format=json&action=query (Caused by SSLError(SSLEOFError(8, '[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1007)'))
なんと!職場のセキュリティが原因だった。自分のWifiにつないだら行けた。
これまでのところは、LangChain toolsを使って計算機とwikipediaのエージェントを作って、問題に応じてそれが使い分けられるということが示された。
その後、PythonREPLTool() を使って人名を並び替えて、その裏でLangChainがどう動いているか示したり、DateTime を使って、今日の日付を聞いたり。
コース2
補足資料はこちら
L1-document_loading
とりあえずGithubからすべてクローン
ファイルがいっぱいあるので、とりあえずGithubからプロジェクトごとクローンします。
【ターミナル】
git clone https://github.com/ksm26/LangChain-Chat-with-Your-Data.git
これでクローンできるので、必要なディレクトリ(docs?)だけ、作業ディレクトリへコピー。
今回はPDFからテキスト抽出、YouTubeから音声をダウンロードしてテキスト変換、ウェブページからテキスト抽出。
YouTubeがうまくいかん
---------------------------------------------------------------------------
SSLEOFError Traceback (most recent call last)
File ~/.local/lib/python3.10/site-packages/urllib3/connectionpool.py:467, in HTTPConnectionPool._make_request(self, conn, method, url, body, headers, retries, timeout, chunked, response_conn, preload_content, decode_content, enforce_content_length)
466 try:
--> 467 self._validate_conn(conn)
468 except (SocketTimeout, BaseSSLError) as e:
File ~/.local/lib/python3.10/site-packages/urllib3/connectionpool.py:1099, in HTTPSConnectionPool._validate_conn(self, conn)
1098 if conn.is_closed:
-> 1099 conn.connect()
1101 # TODO revise this, see https://github.com/urllib3/urllib3/issues/2791
File ~/.local/lib/python3.10/site-packages/urllib3/connection.py:653, in HTTPSConnection.connect(self)
651 server_hostname_rm_dot = server_hostname.rstrip(".")
--> 653 sock_and_verified = _ssl_wrap_socket_and_match_hostname(
654 sock=sock,
655 cert_reqs=self.cert_reqs,
656 ssl_version=self.ssl_version,
657 ssl_minimum_version=self.ssl_minimum_version,
658 ssl_maximum_version=self.ssl_maximum_version,
659 ca_certs=self.ca_certs,
660 ca_cert_dir=self.ca_cert_dir,
661 ca_cert_data=self.ca_cert_data,
662 cert_file=self.cert_file,
663 key_file=self.key_file,
...
992 exc_info = sys.exc_info()
--> 993 raise DownloadError(message, exc_info)
994 self._download_retcode = 1
DownloadError: ERROR: [youtube] jGwO_UgTS7I: Unable to download API page: [SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1007) (caused by SSLError('[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1007)')); please report this issue on https://github.com/yt-dlp/yt-dlp/issues?q= , filling out the appropriate issue template. Confirm you are on the latest version using yt-dlp -U
yt-dlpの更新
【ターミナル】
yt-dlp -U
SSL証明書を確認
pip install --upgrade certifi
ターミナルでyoutubeのダウンロードはできる
【ターミナル】
yt-dlp -v jGwO_UgTS7I
Google先生!
あった!
できん。
そもそもダウンロードができん。
最後は公式頼み!
とりあえずインストール
%pip install --upgrade --quiet librosa
同じエラー
新たなエラー発見!
from langchain_community.document_loaders.blob_loaders.youtube_audio import (
YoutubeAudioLoader,
)
from langchain_community.document_loaders.generic import GenericLoader
from langchain_community.document_loaders.parsers import (
OpenAIWhisperParser,
OpenAIWhisperParserLocal,
)
-------------------------------------------------------------------------
ImportError: cannot import name 'OpenAIWhisperParserLocal' from 'langchain_community.document_loaders.parsers'
こちらで直るか…
修正
これもとりあえずインストールしてみる
pip install transformers torch pydub librosa
だめ
このプラグインもダメ↓
絶対できるはず!
で、検索
これは!
これでできたからとりあえずいっか…
import os
from langchain.vectorstores import Chroma
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.indexes import VectorstoreIndexCreator
from langchain.document_loaders import YoutubeLoader
youtube_url = "https://www.youtube.com/watch?v=jGwO_UgTS7I"
loader = YoutubeLoader.from_youtube_url(youtube_url)
docs = loader.load()
音声ファイルのダウンロードができないのが気になる…
一筋の光明が!
import subprocess
# コマンドと引数をリスト形式で指定
command = [
'yt-dlp',
'--extract-audio',
'--audio-format', 'm4a',
'--audio-quality', '0',
'-o', 'docs/youtube/%(title)s.%(ext)s',
youtube_url # 実際のYouTube URLに置き換えてください
]
# コマンドを実行
subprocess.run(command)
--------------------------------------------------------------------
[youtube] Extracting URL: https://www.youtube.com/watch?v=jGwO_UgTS7I
[youtube] jGwO_UgTS7I: Downloading webpage
[youtube] jGwO_UgTS7I: Downloading ios player API JSON
[youtube] jGwO_UgTS7I: Downloading android player API JSON
[youtube] jGwO_UgTS7I: Downloading m3u8 information
[info] jGwO_UgTS7I: Downloading 1 format(s): 251
[download] docs/youtube/Stanford CS229: Machine Learning Course, Lecture 1 - Andrew Ng (Autumn 2018).m4a has already been downloaded
ERROR: Postprocessing: ffprobe and ffmpeg not found. Please install or provide the path using --ffmpeg-location
Ubuntuにffmpegを入れる!
sudo apt update
sudo apt install ffmpeg
できた!
ちなみに、これやったら元のコードでもできた!
でも、何が効いているのかわからないので、一応通ったものを掲載
from langchain.document_loaders.generic import GenericLoader
#from langchain.document_loaders.parsers import OpenAIWhisperParser
from langchain.document_loaders.parsers.audio import OpenAIWhisperParser
from langchain.document_loaders.blob_loaders.youtube_audio import YoutubeAudioLoader
from langchain.document_loaders import YoutubeLoader
url="https://www.youtube.com/watch?v=jGwO_UgTS7I"
save_dir="docs/youtube/"
loader = GenericLoader(
YoutubeAudioLoader([url],save_dir),
OpenAIWhisperParser()
)
docs = loader.load()
新しい環境でこれでやったら、普通に初めからエラー出たわ。
WARNING: jGwO_UgTS7I: writing DASH m4a. Only some players support this container. Install ffmpeg to fix this automatically
ERROR: Postprocessing: ffprobe and ffmpeg not found. Please install or provide the path using --ffmpeg-location
結局、"langchain.document_loaders.parsers" が悪の元凶だったか。
OpenAIWhisper の有無で比較してみる
OpenAIWhisperParser() なし:
OpenAIWhisperParser() あり:
ちょっと違う!
あたりまえだが、OpenAIWhisperParser() なしは、YouTubeの字幕と完璧に一致するな。
OpenAIWhisperParser() の方が音に忠実で、補正されていない気がするが、私の英語レベルでは甲乙つけられない。
L2-document_splitting
いろいろ文章を区切ってく!
c_splitterとr_splitter
トークンやメタデータに基づく分割
NotionもMarkdownファイルなんだ
L3-vectorstores_and_embeddings
embeddingしてドット積(内積)を計算
なんで?
loadしたドキュメントを使ってvectordbの作成
similarity search
不具合の数々
レクチャー3から持ってこいと言われてるのに、2から持ってきちゃってる問題
そもそも、「回帰について何と言ってる?」という質問に対して、生徒の「回帰」という質問を持ってきたり、単語に引っ張られすぎだな。要約して保存して、検索するような感じがよさそう。
検索結果をどのように検証したらいいかがわかってとてもよい。
L-4_retrieval
max_marginal_relevance_search: 検索結果に多様性を持たせる
メタデータを考慮した検索
docs = vectordb.similarity_search(
question,
k=3,
filter={"source":"docs/cs229_lectures/MachineLearning-Lecture03.pdf"}
)
クエリから必要なメタデータを推測
metadata_field_info = [
AttributeInfo(
name="source",
description="The lecture the chunk is from, should be one of `docs/cs229_lectures/MachineLearning-Lecture01.pdf`, `docs/cs229_lectures/MachineLearning-Lecture02.pdf`, or `docs/cs229_lectures/MachineLearning-Lecture03.pdf`",
type="string",
),
AttributeInfo(
name="page",
description="The page from the lecture",
type="integer",
),
]
document_content_description = "Lecture notes"
llm = OpenAI(temperature=0)
retriever = SelfQueryRetriever.from_llm(
llm,
vectordb,
document_content_description,
metadata_field_info,
verbose=True
)
文脈圧縮による検索の効率化
def pretty_print_docs(docs):
print(f"\n{'-' * 100}\n".join([f"Document {i+1}:\n\n" + d.page_content for i, d in enumerate(docs)]))
# Wrap our vectorstore
llm = OpenAI(temperature=0)
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vectordb.as_retriever()
)
question = "what did they say about matlab?"
compressed_docs = compression_retriever.get_relevant_documents(question)
pretty_print_docs(compressed_docs)
どうやって圧縮して、どうやって戻しているの?
圧縮とリトリーバの融合
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vectordb.as_retriever(search_type = "mmr")
)
その他の検索法:TF-IDF, SVM
vectordbのレトリバー、SVMRetriever、およびTFIDFRetrieverの違いとそれぞれの長所は?
L-5_question_answering
RetrievalQAでの応答
dbサーチの結果とRetrievalの結果を見比べると面白い
dbサーチの結果:
Retrievalの結果:
PromptTemplateの利用
何してるの?
検索結果を文脈として参照するように明示するだけでなく、応答の形式を指定している。
プロンプトの結果:
RetrievalQA chain のタイプ
なんか、時間がかかる気がする。
chain_type="map_reduce"の結果:
chain_type="refine"の結果:
map_reduceとrefineはどんな特徴があるの?
RetrivalQAの限界:会話履歴の保存ができない
"確率は授業のテーマですか?"
"なぜそのような前提条件が必要なのですか?"
L6-chat
ModuleNotFoundError: No module named 'panel'
久々にエラーきた!
%pip install panel
ModuleNotFoundError: No module named 'jupyter_bokeh'
%pip install jupyter_bokeh
AttributeError: type object 'hnswlib.Index' has no attribute 'file_handle_count'
%pip install --upgrade hnswlib
%%pip install --upgrade langchain-community
できん!
embeddingに警告が出ていた。
/home/user/.local/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.embeddings.openai.OpenAIEmbeddings` was deprecated in langchain-community 0.0.9 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import OpenAIEmbeddings`.
warn_deprecated(
なので…
%pip install --upgrade langchain-openai
# from langchain.embeddings.openai import OpenAIEmbeddings
from langchain_openai import OpenAIEmbeddings
できん!
Copilotが教えてくれた!
%pip install chromadb==0.4.3
できた…
以下、コースの中身
"確率は授業のテーマですか?"への回答
プロンプトによる:
# Build prompt
from langchain.prompts import PromptTemplate
template = """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. Use three sentences maximum. Keep the answer as concise as possible. Always say "thanks for asking!" at the end of the answer.
{context}
Question: {question}
Helpful Answer:"""
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context", "question"],template=template,)
ConversationalRetrievalChain
メモリーもいれていく!
"確率は授業のテーマですか?"
"なぜそのような前提条件が必要なのですか?"
メモリがあった方が、Q&Aも自然だな
リトリーバルで質問に答えるチャットボット
何してるの?
panelとparamってなに?
細かく見ていく
def load_db(file, chain_type, k): ってなに?
panel と param は何をしている?
チャットボットを作る
チャットGUIのエラー
WARNING:param.Column00118: Displaying Panel objects in the notebook requires the panel extension to be loaded. Ensure you run pn.extension() before displaying objects in the notebook.
対処:
import panel as pn
pn.extension()
Notebook内でやり取りできるのすご!
注意:def load_db(chain_type, k): のセルはコメントアウト
セルを順番通りやっていくと、チャットボットの所で次のように怒られます。
TypeError: load_db() takes 2 positional arguments but 3 were given
def load_db(chain_type, k): のセルをコメントアウトすると、チャットボットの部分でdef load_db(file, chain_type, k): のセルの方がちゃんと動きます
参考Github
おまけ:ブラウザで動かす
Create a chatbot that works on your documents 以下のチャットボットに関わる3つのセルに加えて、次の部分をdashboard.py に保存
import os
import openai
import sys
sys.path.append('../..')
import panel as pn # GUI
pn.extension()
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file
openai.api_key = os.environ['OPENAI_API_KEY']
import datetime
current_date = datetime.datetime.now().date()
if current_date < datetime.date(2023, 9, 2):
llm_name = "gpt-3.5-turbo-0301"
else:
llm_name = "gpt-3.5-turbo"
print(llm_name)
ターミナルから次のコードで、ブラウザに表示
panel serve dashboard.py --show
エラーで動かん
pip install -U langchain-openai
インポート文の修正
# from langchain.embeddings.openai import OpenAIEmbeddings
# from langchain.chat_models import ChatOpenAI
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
# from langchain.vectorstores import DocArrayInMemorySearch
# from langchain.document_loaders import PyPDFLoader
# from langchain.document_loaders import TextLoader
from langchain_community.vectorstores import DocArrayInMemorySearch
from langchain_community.document_loaders import TextLoader, PyPDFLoader
何もうつらん…
ディベロッパーツールでエラーが確認できますね。
console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing");
これはHTML作らないとダメなやつかな…
Panel がダメみたいなんで、Streamlit に変えます。で、機能は入力と履歴の表示だけにします。
dashboard2.py:
import os
import openai
import sys
sys.path.append('../..')
import panel as pn # GUI
pn.extension()
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file
openai.api_key = os.environ['OPENAI_API_KEY']
import datetime
current_date = datetime.datetime.now().date()
if current_date < datetime.date(2023, 9, 2):
llm_name = "gpt-3.5-turbo-0301"
else:
llm_name = "gpt-3.5-turbo"
print(llm_name)
# from langchain.embeddings.openai import OpenAIEmbeddings
# from langchain.chat_models import ChatOpenAI
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
# from langchain.vectorstores import DocArrayInMemorySearch
# from langchain.document_loaders import PyPDFLoader
# from langchain.document_loaders import TextLoader
from langchain_community.vectorstores import DocArrayInMemorySearch
from langchain_community.document_loaders import TextLoader, PyPDFLoader
from langchain.text_splitter import CharacterTextSplitter, RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA, ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
def load_db(file, chain_type, k):
# load documents
loader = PyPDFLoader(file)
documents = loader.load()
# split documents
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=150)
docs = text_splitter.split_documents(documents)
# define embedding
embeddings = OpenAIEmbeddings()
# create vector database from data
db = DocArrayInMemorySearch.from_documents(docs, embeddings)
# define retriever
retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": k})
# create a chatbot chain. Memory is managed externally.
qa = ConversationalRetrievalChain.from_llm(
llm=ChatOpenAI(model_name=llm_name, temperature=0),
chain_type=chain_type,
retriever=retriever,
return_source_documents=True,
return_generated_question=True,
)
return qa
import panel as pn
import param
class cbfs(param.Parameterized):
chat_history = param.List([])
answer = param.String("")
db_query = param.String("")
db_response = param.List([])
def __init__(self, **params):
super(cbfs, self).__init__( **params)
self.panels = []
self.loaded_file = "docs/cs229_lectures/MachineLearning-Lecture01.pdf"
self.qa = load_db(self.loaded_file,"stuff", 4)
def call_load_db(self, count):
if count == 0 or file_input.value is None: # init or no file specified :
return pn.pane.Markdown(f"Loaded File: {self.loaded_file}")
else:
file_input.save("temp.pdf") # local copy
self.loaded_file = file_input.filename
button_load.button_style="outline"
self.qa = load_db("temp.pdf", "stuff", 4)
button_load.button_style="solid"
self.clr_history()
return pn.pane.Markdown(f"Loaded File: {self.loaded_file}")
# def convchain(self, query):
# if not query:
# return pn.WidgetBox(pn.Row('User:', pn.pane.Markdown("", width=600)), scroll=True)
# result = self.qa({"question": query, "chat_history": self.chat_history})
# self.chat_history.extend([(query, result["answer"])])
# self.db_query = result["generated_question"]
# self.db_response = result["source_documents"]
# self.answer = result['answer']
# self.panels.extend([
# pn.Row('User:', pn.pane.Markdown(query, width=600)),
# pn.Row('ChatBot:', pn.pane.Markdown(self.answer, width=600, style={'background-color': '#F6F6F6'}))
# ])
# inp.value = '' #clears loading indicator when cleared
# return pn.WidgetBox(*self.panels,scroll=True)
def convchain(self, query):
if not query:
return # 何もしない
result = self.qa({"question": query, "chat_history": self.chat_history})
self.chat_history.extend([(query, result["answer"])])
self.db_query = result["generated_question"]
self.db_response = result["source_documents"]
self.answer = result['answer']
# inp.value = '' # この行は削除またはコメントアウトします。
return result # 必要に応じて結果を返します。
@param.depends('db_query ', )
def get_lquest(self):
if not self.db_query :
return pn.Column(
pn.Row(pn.pane.Markdown(f"Last question to DB:", styles={'background-color': '#F6F6F6'})),
pn.Row(pn.pane.Str("no DB accesses so far"))
)
return pn.Column(
pn.Row(pn.pane.Markdown(f"DB query:", styles={'background-color': '#F6F6F6'})),
pn.pane.Str(self.db_query )
)
@param.depends('db_response', )
def get_sources(self):
if not self.db_response:
return
rlist=[pn.Row(pn.pane.Markdown(f"Result of DB lookup:", styles={'background-color': '#F6F6F6'}))]
for doc in self.db_response:
rlist.append(pn.Row(pn.pane.Str(doc)))
return pn.WidgetBox(*rlist, width=600, scroll=True)
@param.depends('convchain', 'clr_history')
def get_chats(self):
if not self.chat_history:
return pn.WidgetBox(pn.Row(pn.pane.Str("No History Yet")), width=600, scroll=True)
rlist=[pn.Row(pn.pane.Markdown(f"Current Chat History variable", styles={'background-color': '#F6F6F6'}))]
for exchange in self.chat_history:
rlist.append(pn.Row(pn.pane.Str(exchange)))
return pn.WidgetBox(*rlist, width=600, scroll=True)
def clr_history(self,count=0):
self.chat_history = []
return
cb = cbfs()
# GUI
import streamlit as st
# Streamlit のユーザー入力フィールドを定義
user_input = st.text_input('Enter your question here:')
# session_stateに 'chat_history' が存在しない場合、空のリストとして初期化する
if 'chat_history' not in st.session_state:
st.session_state['chat_history'] = []
# ユーザーが質問を入力した場合の処理
if user_input:
# LangChain または他のロジックを使用して回答を生成する処理をここに実装します。
# 以下は仮の処理フローです。
result = cb.convchain(user_input) # 実際には convchain メソッドの出力を適切に処理する必要があります。
real_answer = result['answer'] # 実際の回答を取得します。
st.session_state['chat_history'].append((user_input, real_answer))
# チャット履歴を表示
st.write('## Chat History')
for question, answer in st.session_state['chat_history']:
st.text(f'You: {question}')
st.text(f'Bot: {answer}')
# チャット履歴をクリアするボタン
if st.button('Clear History'):
st.session_state['chat_history'] = []
で、ターミナルから↓を実行
streamlit run dashboard2.py
できた!
長い旅路はこれで終わり。無事完遂できてよかった。マルチターン&マルチタスク・エージェント作りたい。
LangSmithも使ってみたいな