Get started | 🦜️🔗 Langchainの翻訳です。
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
LCELによって、基本的なコンポーネントによる複雑なチェーンを容易に構築することができ、ストリーミング、並列処理、ロギングのような機能をすぐに活用できます。
基本的な例: プロンプト + モデル + 出力パーサー
最も基本的で一般的なユースケースは、プロンプトテンプレートとモデルのチェーンを構成するというものです。これがどのように動作するのかを見るために、トピックを受け取りジョークを生成するチェーンを作成しましょう:
%pip install --upgrade --quiet langchain-core langchain-community langchain-openai
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")
model = ChatOpenAI(model="gpt-4")
output_parser = StrOutputParser()
chain = prompt | model | output_parser
chain.invoke({"topic": "ice cream"})
"Why don't ice creams ever get invited to parties?\n\nBecause they always drip when things heat up!"
LCELを用いて異なるコンポーネントを単一のチェーンにまとめ上げているこのコードの以下の行に注意してください:
chain = prompt | model | output_parser
シンボル|
は、さまざまなコンポーネントの出力を次のコンポーネントの入力として流し込むチェーンであるunixのパイプ演算子と似ています。
このチェーンでは、ユーザーの入力はプロンプトテンプレートに引き渡され、プロンプトテンプレートの出力はモデルに引き渡され、モデルの出力は出力パーサーに引き渡されます。何が起きているのかをきちんと理解するためにそれぞれのコンポーネントを個別に見ていきましょう。
1. プロンプト
prompt
はBasePromptTemplate
であり、テンプレート変数のディクショナリーを受け取り、PromptValue
を生成することを意味します。PromptValue
は、(入力として文字列を受け取る)LLM
や(入力としてメッセージのシーケンスを受け取る)ChatModel
に引き渡されるコンプリートされたプロンプトに対するラッパーです。BaseMessages
や文字列を生成する両方のロジックを定義しているので、いずれの言語モデルでも動作します。
prompt_value = prompt.invoke({"topic": "ice cream"})
prompt_value
ChatPromptValue(messages=[HumanMessage(content='tell me a short joke about ice cream')])
prompt_value.to_messages()
[HumanMessage(content='tell me a short joke about ice cream')]
prompt_value.to_string()
'Human: tell me a short joke about ice cream'
2. モデル
PromptValue
がmodel
に引き渡されます。この場合、model
はChatModel
であり、出力はBaseMessage
であることを意味します。
message = model.invoke(prompt_value)
message
AIMessage(content="Why don't ice creams ever get invited to parties?\n\nBecause they always bring a melt down!")
model
がLLM
の場合、出力は文字列となります。
from langchain_openai.llms import OpenAI
llm = OpenAI(model="gpt-3.5-turbo-instruct")
llm.invoke(prompt_value)
'\n\nRobot: Why did the ice cream truck break down? Because it had a meltdown!'
3. 出力パーサー
そして、最後にmodel
の出力はBaseOutputParser
であるoutput_parser
に引き渡され、入力として文字列やBaseMessage
を受け取ることを意味します。特にStrOutputParser
はシンプルにいかなる入力を文字列に変換します。
output_parser.invoke(message)
"Why did the ice cream go to therapy? \n\nBecause it had too many toppings and couldn't find its cone-fidence!"
4. 全体のパイプライン
以下のステップを踏みます:
-
{"topic": "ice cream"}
のように欲しいトピックに関するユーザー入力を引き渡します。 - プロンプトを構成するために
topic
を用いてPromptValueを構築するために、prompt
コンポーネントがユーザー入力を受け取ります。 - 生成されたプロンプトを
model
コンポーネントを受け取り、評価のためにOpenAIのLLMモデルに引き渡します。モデルから生成された出力はChatMessage
オブジェクトとなります。 - 最後に、
output_parser
コンポーネントがChatMessage
を受け取り、呼び出されたメソッドから返却されるPython文字列に変換します。
任意のコンポーネントの出力に興味があるのであれば、中間結果を確認するためにpronpt
やprompt | model
のように小さいチェーンをいつでもテストすることができます:
input = {"topic": "ice cream"}
prompt.invoke(input)
# > ChatPromptValue(messages=[HumanMessage(content='tell me a short joke about ice cream')])
(prompt | model).invoke(input)
# > AIMessage(content="Why did the ice cream go to therapy?\nBecause it had too many toppings and couldn't cone-trol itself!")
RAG検索の例
次の例として、質問に反応する際にいくつかのコンテキストを追加するために、retrieval-augmented generationのチェーンを実行したいものとします。
# Requires:
# pip install langchain docarray tiktoken
from langchain_community.vectorstores import DocArrayInMemorySearch
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_openai.chat_models import ChatOpenAI
from langchain_openai.embeddings import OpenAIEmbeddings
vectorstore = DocArrayInMemorySearch.from_texts(
["harrison worked at kensho", "bears like to eat honey"],
embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever()
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI()
output_parser = StrOutputParser()
setup_and_retrieval = RunnableParallel(
{"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser
chain.invoke("where did harrison work?")
この場合、コンポーネントのチェーンは以下のようになります:
chain = setup_and_retrieval | prompt | model | output_parser
これを説明すると、最初に上のプロンプトテンプレートが、プロンプトで置き換えられる値としてcontext
とquestion
を受け取ることがわかります。プロンプトテンプレートを構築する前に、検索に適した文書を取得し、コンテキストの一部としてそれらを含める必要があります。
準備段階として、クエリーに基づいて文書を取得できるメモリストアを用いたリトリーバーをセットアップしています。これは、実行可能なコンポーネントであり、他のコンポーネントとチェーンすることができますが、これらを個別に実行することもできます:
retriever.invoke("where did harrison work?")
そして、文書検索のリトリーバとユーザーの質問を引き渡すためのRunnablePassthroughを用いて取得した文書のエントリーとオリジナルのユーザーの質問を用いて、期待される入力をプロンプトにまとめるためにRunnableParallel
を使用します:
setup_and_retrieval = RunnableParallel(
{"context": retriever, "question": RunnablePassthrough()}
)
完全なチェーンを確認します:
setup_and_retrieval = RunnableParallel(
{"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser
フローは以下の通りとなります:
- 最初のステップでは2つのエントリーを持つ
RunnableParallel
オブジェクトを作成します。最初のエントリーcontext
には、リトリーバーによって取得された結果文書が含まれます。2つ目のエントリーquestion
には、ユーザーのオリジナルの質問が含まれます。質問を引き渡すために、このエントリーをコピーするためにRunnablePassthrough
を使っています。 - 上のステップのディクショナリーを
prompt
コンポーネントに入力します。そして、プロンプトを構成し、PromptValueを出力するために、ユーザー入力であるquestion
と取得した文書であるcontext
を受け取ります。 -
model
コンポーネントは生成されたプロンプトを受け取り、評価のためにOpenAIのLLMモデルに入力します。モデルから生成された出力はChatMessage
オブジェクトとなります。 - 最後に、
output_parser
コンポーネントがChatMessage
を受け取り、呼び出しメソッドの戻り値となるPython文字列に変換します。
次のステップ
LCELがある場合とない場合とで、一般的な機能を作成するために必要アコードの隣り合わせの比較をするために、次のセクションWhy use LCELを読むことをお勧めします。