如何从工具内部流式传输数据¶
如果您的图涉及调用 LLM 的工具(或任何其他 LangChain Runnable
对象,如其他图、LCEL
链或检索器),您可能希望在工具执行期间显示部分结果,尤其是如果工具运行时间较长。
一种常见的场景是流式传输由调用 LLM 的工具生成的 LLM 令牌,但这适用于任何使用 Runnable 对象的情况。
本指南展示了如何使用 astream
API 和 stream_mode="messages"
从工具内部流式传输数据,以及更细粒度的 astream_events
API。astream
API 应该足以满足大多数用例。
设置¶
首先,让我们安装所需的包并设置我们的 API 密钥
%%capture --no-stderr
%pip install -U langgraph langchain-openai
import getpass
import os
def _set_env(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"{var}: ")
_set_env("OPENAI_API_KEY")
定义图¶
我们将使用预构建的 ReAct 代理来进行本指南
PYTHON 中的 ASYNC<=3.10
任何 Langchain `RunnableLambda`、`RunnableGenerator` 或 `Tool`,它们调用其他可运行对象并在 python<=3.10 中运行异步,将必须**手动**将回调传播到子对象。这是因为 LangChain 在这种情况下无法自动将回调传播到子对象。
这是您可能无法看到从自定义可运行对象或工具发出的事件的常见原因。
from langchain_core.callbacks import Callbacks
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
@tool
async def get_items(
place: str,
callbacks: Callbacks, # <--- Manually accept callbacks (needed for Python <= 3.10)
) -> str:
"""Use this tool to look up which items are in the given place."""
# Attention when using async, you should be invoking the LLM using ainvoke!
# If you fail to do so, streaming will not WORK.
return await llm.ainvoke(
[
{
"role": "user",
"content": f"Can you tell me what kind of items i might find in the following place: '{place}'. "
"List at least 3 such items separating them by a comma. And include a brief description of each item..",
}
],
{"callbacks": callbacks},
)
llm = ChatOpenAI(model_name="gpt-4o")
tools = [get_items]
agent = create_react_agent(llm, tools=tools)
使用 stream_mode="messages"¶
如果您在节点内部没有任何复杂的 LCEL 逻辑(或者您不需要来自 LCEL 链内部的超级细粒度的进度),那么使用 stream_mode="messages"
是一个不错的选择。
final_message = ""
async for msg, metadata in agent.astream(
{"messages": [("human", "what items are on the shelf?")]}, stream_mode="messages"
):
# Stream all messages from the tool node
if (
msg.content
and not isinstance(msg, HumanMessage)
and metadata["langgraph_node"] == "tools"
and not msg.name
):
print(msg.content, end="|", flush=True)
# Final message should come from our agent
if msg.content and metadata["langgraph_node"] == "agent":
final_message += msg.content
使用流式传输事件 API¶
为了简单起见,get_items
工具在其内部没有使用任何复杂的 LCEL 逻辑 - 它只调用 LLM。
但是,如果工具更复杂(例如,在其中使用 RAG 链),并且您想查看链内部的更细粒度的事件,那么您可以使用 astream 事件 API。
下面的示例仅说明如何调用 API。
对 astream 事件 API 使用异步
您通常应该使用 `async` 代码(例如,使用 `ainvoke` 调用 llm)来正确利用 astream 事件 API。
from langchain_core.messages import HumanMessage
async for event in agent.astream_events(
{"messages": [{"role": "user", "content": "what's in the bedroom."}]}, version="v2"
):
if (
event["event"] == "on_chat_model_stream"
and event["metadata"].get("langgraph_node") == "tools"
):
print(event["data"]["chunk"].content, end="|", flush=True)
|In| a| bedroom|,| you| might| find| the| following| items|: |1|.| **|Bed|**|:| The| central| piece| of| furniture| in| a| bedroom|,| typically| consisting| of| a| mattress| on| a| frame|,| where| people| sleep|.| It| often| includes| bedding| such| as| sheets|,| blankets|,| and| pillows| for| comfort|. |2|.| **|Ward|robe|**|:| A| large|,| tall| cupboard| or| fre|estanding| piece| of| furniture| used| for| storing| clothes|.| It| may| have| hanging| space|,| shelves|,| and| sometimes| drawers| for| organizing| garments| and| accessories|. |3|.| **|Night|stand|**|:| A| small| table| or| cabinet| placed| beside| the| bed|,| used| for| holding| items| like| a| lamp|,| alarm| clock|,| books|,| or| personal| belongings| that| might| be| needed| during| the| night| or| early| morning|.||