はじめに
クラウド事業部の大久保と申します。
LangChainが世に出てから2年、言い換えるとLLMアプリケーションの普及が始まってから2年以上経った現在においてプロダクションとして世に送り出されているものも増えており、実践的な書籍もそろっているところはあるかと思います。
私自身、実案件で設計する中で各種媒体の情報は有益で全体感をつかむためには役に立ってはいるものの、 本番向けの実装を検討する中でAPI Referenceを直接読みに行く場面が多いです。 そこで、実践LangChainと自分なりにフレーズを定期的に細かなTipsを記事として書いていきたいと思います。
以下のコードはjupyter notebookを実行したときのものです。 import分については前のコードを参照しております。
対象読者
- プロダクションでLangChainを扱おうとしている方
- 特に共通処理や運用周りとの連携を検討する方
テーマ
今日のテーマは tags
です。
細かすぎて「なんのこっちゃ?」という話かもしれませんが、LangChainで各種LLMを実行する際、Modelのクラスに tags
というプロパティを付与することで、実行結果に任意のメタデータを付加することができます。
from dotenv import load_dotenv from langchain_community.chat_models.azure_openai import AzureChatOpenAI load_dotenv() tags = ["internal", "azure"] llm = AzureChatOpenAI(azure_deployment="gpt-4o-mini", tags=tags) response = llm.invoke("hello")
これは主にトレースに使われるもので、LangSmithや各種トレーサーでは自然な形で取得され、表示されます。
PoCの段階ではあまり気にしないかもしれませんが、本番環境でセキュリティや運用面を検討する際には、非常に重要なメタデータです。運用に関心がある方であれば、ピンとくる部分かと思います。
実行結果の中身を確認する
では、実際の出力を見てみましょう。
print(type(response)) print(response)
<class 'langchain_core.messages.ai.AIMessage'> content='Hello! How can I assist you today?' additional_kwargs={} response_metadata={'token_usage': {...}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_b705f0c291', 'finish_reason': 'stop', 'logprobs': None} id='run-37066f7f-7a43-41b7-9325-6e18b6066fcb-0'
「なんと、ありません」
メタデータなのだから、出力結果に含まれているだろうと思った皆さん。残念ながら、ここには出てこないんです。
みなさん一緒に、tags
の在処を探してみましょう。
tags
の在処
公式ドキュメントにもある通り、出力結果である AIMessage
には tags
は含まれません。
AIMessage — 🦜🔗 LangChain documentation
では、どこにあるのでしょうか?
答えは以下のコードで明らかになります。
from langchain_core.callbacks import BaseCallbackHandler from typing import Any class TagsPrintCallbackHandler(BaseCallbackHandler): def on_llm_end(self, output: Any, run_id: str, parent_run_id: str = None, **kwargs: Any) -> None: tags = kwargs.get("tags", []) print(f"[on_llm_end] run_id={run_id}") print(f"tags: {tags}") handler = TagsPrintCallbackHandler() tags = ["internal", "azure"] llm_for_tags = AzureChatOpenAI(azure_deployment="gpt-4o-mini", tags=tags, callbacks=[handler]) response = llm_for_tags.invoke("hello")
[on_llm_end] run_id=7f4e60a2-2801-472a-a822-988fed3760fe tags: ['internal', 'azure']
このコードは、LLMによる出力が終わった後に、LangChainの config
に含まれるパラメータを表示するカスタムコールバックです。
ご覧のように、tags
はコールバック関数内で扱われています。
コールバックイベントについて
LangChainでは、コールバックイベントは以下の形式で定義されています:
on_[runnable_type]_(start|stream|end)
runnable_type
は以下のいずれかです:
llm
:チャットモデル以外のLLMchat_model
:チャットモデル用prompt
:ChatPromptTemplate
などtool
:@tool
デコレータなどで定義されたツールchain
:大半のRunnable
はこれに該当
イベントカテゴリは以下のとおり:
start
:処理の開始stream
:ストリーミング中end
:処理の終了
それぞれのイベントでは、異なる情報(ペイロード)が渡されます。詳しくは以下を参照:
BaseStreamEvent — 🦜🔗 LangChain documentation
chain
間の tags
の伝播について
LangChainの特徴である chain
を考えると、tags
は本当に前後処理だけで完結しているのか、気になりませんか?
私も気になって、夜も眠れませんでした。
そこで、簡単な chain
を作って、config
に tags
が含まれるか確認してみます。
from langchain_core.runnables import RunnableLambda def print_config(input_from_llm, config=None): print("Input from llm:", input_from_llm) print("Config:", config) find_tags = RunnableLambda(print_config) chain = llm | print_config chain.invoke("hello")
Input from llm: ... Config: {'tags': [], 'metadata': {}, 'callbacks': ..., ...}
tags
は空ですね!
スコープコントロールがしっかりしていて、非常に好感が持てます。
ただし、Chainの間で一貫した tags
を持たせたい場面もあります。
chain.invoke("hello", config={"tags": ["chain:azure"]})
Config: {'tags': ['chain:azure'], ...}
このように、明示的に config
を渡せば、Chainの中でも tags
を伝播させることが可能です。
ユースケースについて
主に以下のようなユースケースが考えられます:
- 本番環境でのObservabilityやSecurity対応
- 実行時のメタデータとしてログや監視フィルターに利用
- LangGraphでのマルチエージェント構成で、内部用LLM出力をフロントに返さない識別子として活用
また、より高度で複雑なアプリケーションになればなるほど、細かいメタデータが運用上必要になります。
本番実装を目指すのであれば、こういった「細かい話」も知っておいて損はありません。
おわりに
私達クラウド事業部はクラウド技術を活用したSI/SESのご支援をしております。
また、一緒に働いていただける仲間も募集中です! ご興味持っていただけましたらぜひお声がけください。