mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-29 19:29:43 +02:00
554 lines
33 KiB
Plaintext
554 lines
33 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
|
|
"metadata": {},
|
|
"source": [
|
|
"# How to pass graph state to tools\n",
|
|
"\n",
|
|
"Sometimes we need to pass in agent state to our tools. This type of stateful tools is useful when a tool's output is affected by past agent steps (e.g. if you're using a sub-agent as a tool, and want to pass the message history in to the sub-agent), or when a tool's input needs to be validated given context from past agent steps. \n",
|
|
"\n",
|
|
"In this guide we'll demonstrate how to create tools that take agent state as input.\n",
|
|
"\n",
|
|
"This is a special case of [passing runtime arguments to tools](https://python.langchain.com/v0.2/docs/how_to/tool_runtime/), which you can learn about in the LangChain docs."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "7cbd446a-808f-4394-be92-d45ab818953c",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Setup\n",
|
|
"\n",
|
|
"First we need to install the packages required"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 41,
|
|
"id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"%%capture --no-stderr\n",
|
|
"%pip install --quiet -U langgraph langchain langchain-openai"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next, we need to set API keys for OpenAI (the chat model we will use)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import getpass\n",
|
|
"import os\n",
|
|
"\n",
|
|
"os.environ[\"OPENAI_API_KEY\"] = os.environ.get(\"OPENAI_API_KEY\") or getpass.getpass(\n",
|
|
" \"OpenAI API Key:\"\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c",
|
|
"metadata": {},
|
|
"source": [
|
|
"Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
|
|
"os.environ[\"LANGCHAIN_API_KEY\"] = os.environ.get(\n",
|
|
" \"LANGCHAIN_API_KEY\"\n",
|
|
") or getpass.getpass(\"LangSmith API Key:\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "21ac643b-cb06-4724-a80c-2862ba4773f1",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Defining the tools\n",
|
|
"\n",
|
|
"We'll want our tool to take graph state as an input, but we don't want the model to try to generate this input when calling the tool. We can use the `InjectedState` annotation to mark arguments as required graph state (or some field of graph state. These arguments will not be generated by the model. When using `ToolNode`, graph state will automatically be passed in to the relevant tools and arguments.\n",
|
|
"\n",
|
|
"In this example we'll create a tool that returns Documents and then another tool that actually cites the Documents that justify a claim."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "1d36e782-80f4-4334-b7d7-ee4c79864480",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from typing import List, Tuple\n",
|
|
"from typing_extensions import Annotated\n",
|
|
"\n",
|
|
"from langchain_core.documents import Document\n",
|
|
"from langchain_core.pydantic_v1 import BaseModel\n",
|
|
"from langchain_core.tools import tool\n",
|
|
"\n",
|
|
"from langgraph.prebuilt import InjectedState\n",
|
|
"\n",
|
|
"\n",
|
|
"@tool(parse_docstring=True, response_format=\"content_and_artifact\")\n",
|
|
"def get_context(question: List[str]) -> Tuple[str, List[Document]]:\n",
|
|
" \"\"\"Get context on the question.\n",
|
|
"\n",
|
|
" Args:\n",
|
|
" question: The user question\n",
|
|
" \"\"\"\n",
|
|
" # return constant dummy output\n",
|
|
" docs = [\n",
|
|
" Document(\n",
|
|
" \"FooBar company just raised 1 Billion dollars!\",\n",
|
|
" metadata={\"source\": \"twitter\"},\n",
|
|
" ),\n",
|
|
" Document(\n",
|
|
" \"FooBar company is now only hiring AI's\", metadata={\"source\": \"twitter\"}\n",
|
|
" ),\n",
|
|
" Document(\n",
|
|
" \"FooBar company was founded in 2019\", metadata={\"source\": \"wikipedia\"}\n",
|
|
" ),\n",
|
|
" Document(\n",
|
|
" \"FooBar company makes friendly robots\", metadata={\"source\": \"wikipedia\"}\n",
|
|
" ),\n",
|
|
" ]\n",
|
|
" return \"\\n\\n\".join(doc.page_content for doc in docs), docs\n",
|
|
"\n",
|
|
"\n",
|
|
"@tool(parse_docstring=True, response_format=\"content_and_artifact\")\n",
|
|
"def cite_context_sources(\n",
|
|
" claim: str, state: Annotated[dict, InjectedState]\n",
|
|
") -> Tuple[str, List[Document]]:\n",
|
|
" \"\"\"Cite which source a claim was based on.\n",
|
|
"\n",
|
|
" Args:\n",
|
|
" claim: The claim that was made.\n",
|
|
" \"\"\"\n",
|
|
" docs = []\n",
|
|
" # We get the potentially cited docs from past ToolMessages in our state.\n",
|
|
" for msg in state[\"messages\"]:\n",
|
|
" if isinstance(msg, ToolMessage) and msg.name == \"get_context\":\n",
|
|
" docs.extend(msg.artifact)\n",
|
|
"\n",
|
|
" class Cite(BaseModel):\n",
|
|
" \"\"\"Return the index(es) of the documents that justify the claim\"\"\"\n",
|
|
"\n",
|
|
" indexes: List[int]\n",
|
|
"\n",
|
|
" structured_model = model.with_structured_output(Cite)\n",
|
|
" system = f\"Which of the following documents best justifies the claim:\\n\\n{claim}\"\n",
|
|
" context = \"\\n\\n\".join(\n",
|
|
" f\"Document {i}:\\n\" + doc.page_content for i, doc in enumerate(docs)\n",
|
|
" )\n",
|
|
" citation = structured_model.invoke([(\"system\", system), (\"human\", context)])\n",
|
|
" cited_docs = [docs[i] for i in citation.indexes]\n",
|
|
" sources = \", \".join(doc.metadata[\"source\"] for doc in cited_docs)\n",
|
|
" return sources, cited_docs"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "1c2d0de0-0f3e-4bbe-b0b6-cc0f70b11993",
|
|
"metadata": {},
|
|
"source": [
|
|
"If we look at the input schemas for these tools, we'll see that `state` is still listed:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "1092929b-c939-4b2a-9f9c-e725b0e34af2",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'title': 'cite_context_sourcesSchema',\n",
|
|
" 'description': 'Cite which source a claim was based on.',\n",
|
|
" 'type': 'object',\n",
|
|
" 'properties': {'claim': {'title': 'Claim',\n",
|
|
" 'description': 'The claim that was made.',\n",
|
|
" 'type': 'string'},\n",
|
|
" 'state': {'title': 'State', 'type': 'object'}},\n",
|
|
" 'required': ['claim', 'state']}"
|
|
]
|
|
},
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"cite_context_sources.get_input_schema().schema()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e346a26e-e00b-48e5-82c5-c930ea6084a4",
|
|
"metadata": {},
|
|
"source": [
|
|
"But if we look at the tool call schema, which is what is passed to the model for tool-calling, `state` has been removed:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "3912bb51-3107-4335-a659-021c5d89fb37",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'title': 'cite_context_sources',\n",
|
|
" 'description': 'Cite which source a claim was based on.',\n",
|
|
" 'type': 'object',\n",
|
|
" 'properties': {'claim': {'title': 'Claim',\n",
|
|
" 'description': 'The claim that was made.',\n",
|
|
" 'type': 'string'}},\n",
|
|
" 'required': ['claim']}"
|
|
]
|
|
},
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"cite_context_sources.tool_call_schema.schema()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8e8b9211-93d0-4ad5-aa7a-9c09099c53ff",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the agent state\n",
|
|
"\n",
|
|
"The main type of graph in `langgraph` is the `StateGraph`.\n",
|
|
"This graph is parameterized by a state object that it passes around to each node.\n",
|
|
"Each node then returns operations to update that state.\n",
|
|
"These operations can either SET specific attributes on the state (e.g. overwrite the existing values) or ADD to the existing attribute.\n",
|
|
"Whether to set or add is denoted by annotating the state object you construct the graph with.\n",
|
|
"\n",
|
|
"For this example, the state we will track will just be a list of messages.\n",
|
|
"We want each node to just add messages to that list.\n",
|
|
"Therefore, we will use a `TypedDict` with one key (`messages`) and annotate it so that the `messages` attribute is always added to.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "ea793afa-2eab-4901-910d-6eed90cd6564",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import operator\n",
|
|
"from typing import Annotated, Sequence, TypedDict\n",
|
|
"\n",
|
|
"from langchain_core.messages import BaseMessage\n",
|
|
"\n",
|
|
"\n",
|
|
"class AgentState(TypedDict):\n",
|
|
" messages: Annotated[Sequence[BaseMessage], operator.add]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e03c5094-9297-4d19-a04e-3eedc75cefb4",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the nodes\n",
|
|
"\n",
|
|
"We now need to define a few different nodes in our graph.\n",
|
|
"In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel).\n",
|
|
"There are two main nodes we need for this:\n",
|
|
"\n",
|
|
"1. The agent: responsible for deciding what (if any) actions to take.\n",
|
|
"2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action.\n",
|
|
"\n",
|
|
"We will also need to define some edges.\n",
|
|
"Some of these edges may be conditional.\n",
|
|
"The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n",
|
|
"The path that is taken is not known until that node is run (the LLM decides).\n",
|
|
"\n",
|
|
"1. Conditional Edge: after the agent is called, we should either:\n",
|
|
" a. If the agent said to take an action, then the function to invoke tools should be called\n",
|
|
" b. If the agent said that it was finished, then it should finish\n",
|
|
"2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n",
|
|
"\n",
|
|
"Let's define the nodes, as well as a function to decide how what conditional edge to take."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"id": "3b541bb9-900c-40d0-964d-7b5dfee30667",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from copy import deepcopy\n",
|
|
"\n",
|
|
"from langchain_core.messages import ToolMessage\n",
|
|
"from langchain_openai import ChatOpenAI\n",
|
|
"\n",
|
|
"from langgraph.prebuilt import ToolNode\n",
|
|
"\n",
|
|
"model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Define the function that determines whether to continue or not\n",
|
|
"def should_continue(state, config):\n",
|
|
" messages = state[\"messages\"]\n",
|
|
" last_message = messages[-1]\n",
|
|
" # If there is no function call, then we finish\n",
|
|
" if not last_message.tool_calls:\n",
|
|
" return \"end\"\n",
|
|
" # Otherwise if there is, we continue\n",
|
|
" else:\n",
|
|
" return \"continue\"\n",
|
|
"\n",
|
|
"\n",
|
|
"tools = [get_context, cite_context_sources]\n",
|
|
"\n",
|
|
"# Define the function that calls the model\n",
|
|
"def call_model(state, config):\n",
|
|
" messages = state[\"messages\"]\n",
|
|
" model_with_tools = model.bind_tools(tools)\n",
|
|
" response = model_with_tools.invoke(messages)\n",
|
|
" # We return a list, because this will get added to the existing list\n",
|
|
" return {\"messages\": [response]}\n",
|
|
"\n",
|
|
"\n",
|
|
"# ToolNode will automatically take care of injecting state into tools\n",
|
|
"tool_node = ToolNode(tools)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Define the graph\n",
|
|
"\n",
|
|
"We can now put it all together and define the graph!"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"id": "813ae66c-3b58-4283-a02a-36da72a2ab90",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from langgraph.graph import END, START, StateGraph\n",
|
|
"\n",
|
|
"# Define a new graph\n",
|
|
"workflow = StateGraph(AgentState)\n",
|
|
"\n",
|
|
"# Define the two nodes we will cycle between\n",
|
|
"workflow.add_node(\"agent\", call_model)\n",
|
|
"workflow.add_node(\"action\", tool_node)\n",
|
|
"\n",
|
|
"# Set the entrypoint as `agent`\n",
|
|
"# This means that this node is the first one called\n",
|
|
"workflow.add_edge(START, \"agent\")\n",
|
|
"\n",
|
|
"# We now add a conditional edge\n",
|
|
"workflow.add_conditional_edges(\n",
|
|
" # First, we define the start node. We use `agent`.\n",
|
|
" # This means these are the edges taken after the `agent` node is called.\n",
|
|
" \"agent\",\n",
|
|
" # Next, we pass in the function that will determine which node is called next.\n",
|
|
" should_continue,\n",
|
|
" # Finally we pass in a mapping.\n",
|
|
" # The keys are strings, and the values are other nodes.\n",
|
|
" # END is a special node marking that the graph should finish.\n",
|
|
" # What will happen is we will call `should_continue`, and then the output of that\n",
|
|
" # will be matched against the keys in this mapping.\n",
|
|
" # Based on which one it matches, that node will then be called.\n",
|
|
" {\n",
|
|
" # If `tools`, then we call the tool node.\n",
|
|
" \"continue\": \"action\",\n",
|
|
" # Otherwise we finish.\n",
|
|
" \"end\": END,\n",
|
|
" },\n",
|
|
")\n",
|
|
"\n",
|
|
"# We now add a normal edge from `tools` to `agent`.\n",
|
|
"# This means that after `tools` is called, `agent` node is called next.\n",
|
|
"workflow.add_edge(\"action\", \"agent\")\n",
|
|
"\n",
|
|
"# Finally, we compile it!\n",
|
|
"# This compiles it into a LangChain Runnable,\n",
|
|
"# meaning you can use it as you would any other runnable\n",
|
|
"app = workflow.compile()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 20,
|
|
"id": "a8afd6ef",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/jpeg": "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",
|
|
"text/plain": [
|
|
"<IPython.core.display.Image object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"from IPython.display import Image, display\n",
|
|
"\n",
|
|
"try:\n",
|
|
" display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n",
|
|
"except Exception:\n",
|
|
" # This requires some extra dependencies and is optional\n",
|
|
" pass"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "547c3931-3dae-4281-ad4e-4b51305594d4",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Use it!\n",
|
|
"\n",
|
|
"We can now use it!\n",
|
|
"This now exposes the [same interface](https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language-lcel) as all other LangChain runnables."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 21,
|
|
"id": "8edb04b9-40b6-46f1-a7a8-4b2d8aba7752",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Output from node 'agent':\n",
|
|
"---\n",
|
|
"{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_BidVTw5NiW2wp8Ez7m8dDoHI', 'function': {'arguments': '{\"question\":[\"latest news about FooBar\"]}', 'name': 'get_context'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 87, 'total_tokens': 106}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_c4e5b6fa31', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-fcac1b73-563e-4f4c-b1b0-626f55d377be-0', tool_calls=[{'name': 'get_context', 'args': {'question': ['latest news about FooBar']}, 'id': 'call_BidVTw5NiW2wp8Ez7m8dDoHI', 'type': 'tool_call'}], usage_metadata={'input_tokens': 87, 'output_tokens': 19, 'total_tokens': 106})]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"Output from node 'action':\n",
|
|
"---\n",
|
|
"{'messages': [ToolMessage(content=\"FooBar company just raised 1 Billion dollars!\\n\\nFooBar company is now only hiring AI's\\n\\nFooBar company was founded in 2019\\n\\nFooBar company makes friendly robots\", name='get_context', tool_call_id='call_BidVTw5NiW2wp8Ez7m8dDoHI', artifact=[Document(metadata={'source': 'twitter'}, page_content='FooBar company just raised 1 Billion dollars!'), Document(metadata={'source': 'twitter'}, page_content=\"FooBar company is now only hiring AI's\"), Document(metadata={'source': 'wikipedia'}, page_content='FooBar company was founded in 2019'), Document(metadata={'source': 'wikipedia'}, page_content='FooBar company makes friendly robots')])]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"Output from node 'agent':\n",
|
|
"---\n",
|
|
"{'messages': [AIMessage(content='The latest news about FooBar is that the company has just raised 1 billion dollars!', response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 150, 'total_tokens': 169}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_c4e5b6fa31', 'finish_reason': 'stop', 'logprobs': None}, id='run-a8407471-7715-4c16-bd46-c29e5751e882-0', usage_metadata={'input_tokens': 150, 'output_tokens': 19, 'total_tokens': 169})]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"from langchain_core.messages import HumanMessage\n",
|
|
"\n",
|
|
"messages = [HumanMessage(\"what's the latest news about FooBar\")]\n",
|
|
"for output in app.stream({\"messages\": messages}):\n",
|
|
" # stream() yields dictionaries with output keyed by node name\n",
|
|
" for key, value in output.items():\n",
|
|
" print(f\"Output from node '{key}':\")\n",
|
|
" print(\"---\")\n",
|
|
" print(value)\n",
|
|
" messages.extend(value[\"messages\"])\n",
|
|
" print(\"\\n---\\n\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"id": "4a2128ed-e23f-4f25-a026-0c6590f01a1c",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Output from node 'agent':\n",
|
|
"---\n",
|
|
"{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_EB0zaQypXMqEUzaqwflUr0zH', 'function': {'arguments': '{\"claim\":\"FooBar company just raised 1 Billion dollars!\"}', 'name': 'cite_context_sources'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 183, 'total_tokens': 208}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_c4e5b6fa31', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b4952777-e2b3-4448-be87-200e6e80981b-0', tool_calls=[{'name': 'cite_context_sources', 'args': {'claim': 'FooBar company just raised 1 Billion dollars!'}, 'id': 'call_EB0zaQypXMqEUzaqwflUr0zH', 'type': 'tool_call'}], usage_metadata={'input_tokens': 183, 'output_tokens': 25, 'total_tokens': 208})]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"Output from node 'action':\n",
|
|
"---\n",
|
|
"{'messages': [ToolMessage(content='twitter', name='cite_context_sources', tool_call_id='call_EB0zaQypXMqEUzaqwflUr0zH', artifact=[Document(metadata={'source': 'twitter'}, page_content='FooBar company just raised 1 Billion dollars!')])]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"Output from node 'agent':\n",
|
|
"---\n",
|
|
"{'messages': [AIMessage(content='The information that FooBar company just raised 1 billion dollars comes from Twitter.', response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 218, 'total_tokens': 235}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_400f27fa1f', 'finish_reason': 'stop', 'logprobs': None}, id='run-a0dede05-dadd-46f6-8654-746520d4cef8-0', usage_metadata={'input_tokens': 218, 'output_tokens': 17, 'total_tokens': 235})]}\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"messages.append(HumanMessage(\"where did you get this information?\"))\n",
|
|
"for output in app.stream({\"messages\": messages}):\n",
|
|
" # stream() yields dictionaries with output keyed by node name\n",
|
|
" for key, value in output.items():\n",
|
|
" print(f\"Output from node '{key}':\")\n",
|
|
" print(\"---\")\n",
|
|
" print(value)\n",
|
|
" print(\"\\n---\\n\")"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "poetry-venv-311",
|
|
"language": "python",
|
|
"name": "poetry-venv-311"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.11.9"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|