2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LangChainのCallbacks

Last updated at Posted at 2024-02-22

LangChainには色んなイベント発生時にログを取ったりすることができるCallbacksという機能がありますが、その使い方で四苦八苦したので記録しようと思います。

イベントの種類に関してはこちらを参考にしました。

前提

使用OS : Windows10
使用言語 : Python 3.11.5
AzureOpenAI : gpt4-turbo 2024-02-15-preview
ライブラリバージョン:
langchain==0.1.5
openai==1.11.0

やりたかったこと

agentとfunction callingを組み合わせたときの各イベント(agentの動作、chainの動作、toolの動作、Modelの動作等)のログを取りたかった。

勘違いしてたこと

agentにコールバックを設定してれば、agentの中でchainやらModelやToolやらが動いてるんだからログを取れるだろうと思ってました。

しかし、実際にagent実行時に動いたイベントは以下でした。

イベント名 説明
on_chain_start チェーンの動作開始時
on_chain_end チェーンの動作終了時
on_chain_error チェーンでエラーが発生した時
on_agent_action エージェントアクション時
on_agent_finish エージェントの終了時

もしかして、ModelとかToolにも設定しないと全部とれないのかなと思い試してみたところ、とれました!

実装まとめ

シンプルにまとめるために、一部コード省略して書いてます。

・Model

from typing import Dict,List,Union,Any
from langchain_core.messages.base import BaseMessage
from langchain.schema.output import LLMResult
from langchain_openai import AzureChatOpenAI
from langchain.callbacks.base import BaseCallbackHandler

class ModelCallbackHandler(BaseCallbackHandler):
    """Base callback handler that can be used to handle callbacks from langchain."""

    def on_llm_start(
        self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
    ) -> Any:
        """Run when LLM starts running."""

    def on_chat_model_start(
        self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs: Any
    ) -> Any:
        """Run when Chat Model starts running."""

    def on_llm_new_token(self, token: str, **kwargs: Any) -> Any:
        """Run on new LLM token. Only available when streaming is enabled."""

    def on_llm_end(self, response: LLMResult, **kwargs: Any) -> Any:
        """Run when LLM ends running."""

    def on_llm_error(
        self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
    ) -> Any:
        """Run when LLM errors."""


llm = AzureChatOpenAI(
    openai_api_version="AOAIのバージョン",
    azure_deployment="AOAIのデプロイ名",
    callbacks=[ModelCallbackHandler()]
)

・Tool

from typing import Dict,Union,Any
from pydantic import BaseModel , Field
from langchain.agents.tools import BaseTool
from langchain.callbacks.base import BaseCallbackHandler
from langchain.agents import load_tools

class ToolCallbackHandler(BaseCallbackHandler):
    """Base callback handler that can be used to handle callbacks from langchain."""

    def on_tool_start(
        self, serialized: Dict[str, Any], input_str: str, **kwargs: Any
    ) -> Any:
        """Run when tool starts running."""

    def on_tool_end(self, output: str, **kwargs: Any) -> Any:
        """Run when tool ends running."""

    def on_tool_error(
        self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
    ) -> Any:
        """Run when tool errors."""

    
# langchainに備わっているツールを使用する場合
tools = load_tools(
    tool_names=["ツールの名前"],callbacks=[ToolCallbackHandler()]
    )

# 作成したカスタムツール使用する場合
class CustomToolInput(BaseModel):
    query: str = Field(..., description="引数の説明書き")

class CustomTool(BaseTool):
    name = "カスタムツールの名前"
    description = "カスタムツールの説明書き"
    args_schema = CustomToolInput

    def _run(self, query: str) -> str:
        # 同期時の処理を書く
        return query
    
    async def _arun(self, query: str) -> str:
        # 非同期時の処理を書く
        return self._run(query)

custom_tool = CustomTool()
custom_tool.callbacks = [ToolCallbackHandler()]
tools = [custom_tool]

・Agent

from typing import Dict,Union,Any
from langchain_core.agents import AgentAction, AgentFinish
from langchain.callbacks.base import BaseCallbackHandler
from langchain_openai import AzureChatOpenAI
from langchain.schema import SystemMessage
from langchain.agents.openai_functions_agent.base import OpenAIFunctionsAgent
from langchain.agents.agent import AgentExecutor

class AgentCallbackHandler(BaseCallbackHandler):
    """Base callback handler that can be used to handle callbacks from langchain."""

    def on_chain_start(
        self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any
    ) -> Any:
        """Run when chain starts running."""

    def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> Any:
        """Run when chain ends running."""

    def on_chain_error(
        self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
    ) -> Any:
        """Run when chain errors."""
    
    def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any:
        """Run on agent action."""

    def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> Any:
        """Run on agent end."""

    
llm = AzureChatOpenAI(...)
tools = [...]
prompt = [SystemMessage("あなたは優秀なAIアシスタントです。")]
agent = OpenAIFunctionsAgent(
    llm=llm,
    tools = tools,
    prompt = prompt
)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    callbacks=[AgentCallbackHandler()]
)


agent_executor.invoke(input={"input":"こんにちわ"})

わからなかったこと

以下の2つだけどういう実装で発火してくれるのかわかりませんでした。

イベント名 説明
on_llm_start LLMの動作開始時
on_text 任意のテキストが出力された時

on_llm_startは普通にLLMで発火してくれると思ったら、on_chat_model_startしか発火しなかった...なぜ...?

on_textはドキュメントみてもよくわかりませんでした。
任意のテキストって...何...。

ご存じの方がいらっしゃれば教えて頂けると幸いです。

追記

LnagChainでは最新バージョンを使用すると、OpenAIFunctionsAgent使用時に0.2以降のバージョンで使用できなくなるという警告が出ます。
推奨ではcreate_openai_functions_agentを使用することとされていますが、当記事執筆時はcreate_openai_functions_agentを使用するとなぜか強制的にストリーミングとなってしまいトークン数が取得できないというバグが発生しておりました。
そのため、OpenAIFunctionsAgentにて検証しておりますことをお断りさせて頂きます。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?