doc updates (#1639)

---------

Co-authored-by: vbarda <vadym@langchain.dev>
This commit is contained in:
Isaac Francisco
2024-09-08 16:55:52 +00:00
committed by GitHub
co-authored by vbarda
parent 38a644cf79
commit db306cd01b
98 changed files with 3227 additions and 978 deletions
+2 -2
View File
@@ -55,7 +55,7 @@ jobs:
run: |
if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.event_name }}" == "workflow_dispatch" ] || ([ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == "refs/heads/main" ]); then
echo "Running link check on all notebooks in examples directory..."
poetry run pytest -v --check-links-ignore "https://(api|web)\.smith\.langchain\.com/.*" --check-links-ignore "https://x.com/.*" --check-links examples
poetry run pytest -v --check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" --check-links-ignore "https://x.com/.*" --check-links examples
else
echo "Fetching changes from origin/main..."
git fetch origin main
@@ -64,7 +64,7 @@ jobs:
echo "Changed files: ${CHANGED_FILES}"
if [ -n "${CHANGED_FILES}" ]; then
echo "Running link check on changed notebook files..."
poetry run pytest -v --check-links-ignore "https://(api|web)\.smith\.langchain\.com/.*" --check-links-ignore "https://x.com/.*" --check-links ${CHANGED_FILES} || ([ $? = 5 ] && exit 0 || exit $?)
poetry run pytest -v --check-links-ignore "https://(api|web|docs)\.smith\.langchain\.com/.*" --check-links-ignore "https://x.com/.*" --check-links ${CHANGED_FILES} || ([ $? = 5 ] && exit 0 || exit $?)
else
echo "No notebook files changed."
fi
+2 -2
View File
@@ -117,7 +117,7 @@ Now we can invoke our graph to ensure it is working. Make sure to change the inp
=== "Python"
```python
input = {"messages": [{"role": "human", "content": "what's the weather in sf"}]}
input = {"messages": [{"role": "user", "content": "what's the weather in sf"}]}
async for chunk in client.runs.stream(
thread["thread_id"],
assistant_id,
@@ -131,7 +131,7 @@ Now we can invoke our graph to ensure it is working. Make sure to change the inp
=== "Javascript"
```js
const input = { "messages": [{ "role": "human", "content": "what's the weather in sf"}] }
const input = { "messages": [{ "role": "user", "content": "what's the weather in sf"}] }
const streamResponse = client.runs.stream(
thread["thread_id"],
+11 -4
View File
@@ -1,8 +1,11 @@
# How to kick off background runs
This guide covers how to kick off background runs for your agent.
This can be useful for long running jobs.
## Setup
First let's set up our client and thread:
=== "Python"
@@ -52,6 +55,8 @@ Output:
'values': None
}
## Check runs on thread
If we list the current runs on this thread, we will see that it's empty:
=== "Python"
@@ -79,19 +84,21 @@ Output:
[]
## Start runs on thread
Now let's kick off a run:
=== "Python"
```python
input = {"messages": [{"role": "human", "content": "what's the weather in sf"}]}
input = {"messages": [{"role": "user", "content": "what's the weather in sf"}]}
run = await client.runs.create(thread["thread_id"], assistant_id, input=input)
```
=== "Javascript"
```js
let input = {"messages": [{"role": "human", "content": "what's the weather in sf"}]};
let input = {"messages": [{"role": "user", "content": "what's the weather in sf"}]};
let run = await client.runs.create(thread["thread_id"], assistantID, { input });
```
@@ -141,7 +148,7 @@ Output:
"input": {
"messages": [
{
"role": "human",
"role": "user",
"content": "what's the weather in sf"
}
]
@@ -212,7 +219,7 @@ Output:
"input": {
"messages": [
{
"role": "human",
"role": "user",
"content": "what's the weather in sf"
}
]
@@ -219,7 +219,7 @@ We can verify the config is indeed taking effect:
"input": {
"messages": [
{
"role": "human",
"role": "user",
"content": "who made you?"
}
]
+10 -5
View File
@@ -1,9 +1,10 @@
## Enqueue
# Enqueue
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../concepts/api.md#double-texting).
The guide covers the `enqueue` option for double texting, which adds the interruptions to a queue and executes them in the order they are received by the client. Below is a quick example of using the `enqueue` option.
## Setup
First, we will define a quick helper function for printing out JS and CURL model outputs (you can skip this if using Python):
@@ -82,6 +83,8 @@ Then, let's import our required packages and instantiate our client, assistant,
--data '{}'
```
## Create runs
Now let's start two runs, with the second interrupting the first one with a multitask strategy of "enqueue":
=== "Python"
@@ -90,12 +93,12 @@ Now let's start two runs, with the second interrupting the first one with a mult
first_run = await client.runs.create(
thread["thread_id"],
assistant_id,
input={"messages": [{"role": "human", "content": "what's the weather in sf?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
)
second_run = await client.runs.create(
thread["thread_id"],
assistant_id,
input={"messages": [{"role": "human", "content": "what's the weather in nyc?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
multitask_strategy="enqueue",
)
```
@@ -106,13 +109,13 @@ Now let's start two runs, with the second interrupting the first one with a mult
const firstRun = await client.runs.create(
thread["thread_id"],
assistantId,
input={"messages": [{"role": "human", "content": "what's the weather in sf?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
)
const secondRun = await client.runs.create(
thread["thread_id"],
assistantId,
input={"messages": [{"role": "human", "content": "what's the weather in nyc?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
multitask_strategy="enqueue",
)
```
@@ -136,6 +139,8 @@ Now let's start two runs, with the second interrupting the first one with a mult
}"
```
## View run results
Verify that the thread has data from both runs:
=== "Python"
@@ -61,7 +61,7 @@ And, now let's compile it with a breakpoint before the tool node:
=== "Python"
```python
input = {"messages": [{"role": "human", "content": "what's the weather in sf"}]}
input = {"messages": [{"role": "user", "content": "what's the weather in sf"}]}
async for chunk in client.runs.stream(
thread["thread_id"],
assistant_id,
@@ -77,7 +77,7 @@ Let's look at an example when no review is required (because no tools are called
=== "Javascript"
```js
const input = { "messages": [{ "role": "human", "content": "hi!" }] };
const input = { "messages": [{ "role": "user", "content": "hi!" }] };
const streamResponse = client.runs.stream(
thread["thread_id"],
@@ -64,7 +64,7 @@ Before replaying a state - we need to create states to replay from! In order to
=== "Javascript"
```js
const input = { "messages": [{ "role": "human", "content": "Please search the weather in SF" }] }
const input = { "messages": [{ "role": "user", "content": "Please search the weather in SF" }] }
const streamResponse = client.runs.stream(
thread["thread_id"],
@@ -62,7 +62,7 @@ Now, let's invoke our graph by interrupting before `ask_human` node:
input = {
"messages": [
{
"role": "human",
"role": "user",
"content": "Use the search tool to ask the user where they are, then look up the weather there",
}
]
@@ -1,9 +1,11 @@
## Interrupt
# Interrupt
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide](../concepts/api.md#double-texting).
The guide covers the `interrupt` option for double texting, which interrupts the prior run of the graph and starts a new one with the double-text. This option does not delete the first run, but rather keeps it in the database but sets its status to `interrupted`. Below is a quick example of using the `interrupt` option.
## Setup
First, we will define a quick helper function for printing out JS and CURL model outputs (you can skip this if using Python):
=== "Javascript"
@@ -79,6 +81,8 @@ Now, let's import our required packages and instantiate our client, assistant, a
--data '{}'
```
## Create runs
Now we can start our two runs and join the second on euntil it has completed:
=== "Python"
@@ -88,13 +92,13 @@ Now we can start our two runs and join the second on euntil it has completed:
interrupted_run = await client.runs.create(
thread["thread_id"],
assistant_id,
input={"messages": [{"role": "human", "content": "what's the weather in sf?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
)
await asyncio.sleep(2)
run = await client.runs.create(
thread["thread_id"],
assistant_id,
input={"messages": [{"role": "human", "content": "what's the weather in nyc?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
multitask_strategy="interrupt",
)
# wait until the second run completes
@@ -145,6 +149,8 @@ Now we can start our two runs and join the second on euntil it has completed:
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/join
```
## View run results
We can see that the thread has partial data from the first run + data from the second run
+10 -5
View File
@@ -1,9 +1,11 @@
## Reject
# Reject
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide][double-texting].
The guide covers the `reject` option for double texting, which rejects the new run of the graph by throwing an error and continues with the original run until completion. Below is a quick example of using the `reject` option.
## Setup
First, we will define a quick helper function for printing out JS and CURL model outputs (you can skip this if using Python):
=== "Javascript"
@@ -78,6 +80,8 @@ Now, let's import our required packages and instantiate our client, assistant, a
--data '{}'
```
## Create runs
Now we can run a thread and try to run a second one with the "reject" option, which should fail since we have already started a run:
@@ -87,14 +91,14 @@ Now we can run a thread and try to run a second one with the "reject" option, wh
run = await client.runs.create(
thread["thread_id"],
assistant_id,
input={"messages": [{"role": "human", "content": "what's the weather in sf?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
)
try:
await client.runs.create(
thread["thread_id"],
assistant_id,
input={
"messages": [{"role": "human", "content": "what's the weather in nyc?"}]
"messages": [{"role": "user", "content": "what's the weather in nyc?"}]
},
multitask_strategy="reject",
)
@@ -108,7 +112,7 @@ Now we can run a thread and try to run a second one with the "reject" option, wh
const run = await client.runs.create(
thread["thread_id"],
assistantId,
input={"messages": [{"role": "human", "content": "what's the weather in sf?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
);
try {
@@ -116,7 +120,7 @@ Now we can run a thread and try to run a second one with the "reject" option, wh
thread["thread_id"],
assistantId,
{
input: {"messages": [{"role": "human", "content": "what's the weather in nyc?"}]},
input: {"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
multitask_strategy:"reject"
},
);
@@ -149,6 +153,7 @@ Output:
Failed to start concurrent run Client error '409 Conflict' for url 'http://localhost:8123/threads/f9e7088b-8028-4e5c-88d2-9cc9a2870e50/runs'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409
## View run results
We can verify that the original thread finished executing:
@@ -1,9 +1,11 @@
## Rollback
# Rollback
This guide assumes knowledge of what double-texting is, which you can learn about in the [double-texting conceptual guide][double-texting].
The guide covers the `rollback` option for double texting, which interrupts the prior run of the graph and starts a new one with the double-text. This option is very similar to the `interrupt` option, but in this case the first run is completely deleted from the database and cannot be restarted. Below is a quick example of using the `rollback` option.
## Setup
First, we will define a quick helper function for printing out JS and CURL model outputs (you can skip this if using Python):
=== "Javascript"
@@ -80,6 +82,8 @@ Now, let's import our required packages and instantiate our client, assistant, a
--data '{}'
```
## Create runs
Now let's run a thread with the multitask parameter set to "rollback":
=== "Python"
@@ -89,13 +93,13 @@ Now let's run a thread with the multitask parameter set to "rollback":
rolled_back_run = await client.runs.create(
thread["thread_id"],
assistant_id,
input={"messages": [{"role": "human", "content": "what's the weather in sf?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in sf?"}]},
)
await asyncio.sleep(2)
run = await client.runs.create(
thread["thread_id"],
assistant_id,
input={"messages": [{"role": "human", "content": "what's the weather in nyc?"}]},
input={"messages": [{"role": "user", "content": "what's the weather in nyc?"}]},
multitask_strategy="rollback",
)
# wait until the second run completes
@@ -146,6 +150,8 @@ Now let's run a thread with the multitask parameter set to "rollback":
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/<RUN_ID>/join
```
## View run results
We can see that the thread has data only from the second run
=== "Python"
+10 -2
View File
@@ -6,6 +6,8 @@ This means that you can run multiple agents on the same thread, which allows a d
In this example, we will create two agents and then call them both on the same thread.
You'll see that the second agent will respond using information from the [checkpoint](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer-state) generated in the thread by the first agent as context.
## Setup
=== "Python"
```python
@@ -124,6 +126,10 @@ Output:
}
}
## Run assistants on thread
### Run OpenAI assistant
We can now run the OpenAI assistant on the thread first.
=== "Python"
@@ -178,7 +184,7 @@ We can now run the OpenAI assistant on the thread first.
"input": {
"messages": [
{
"role": "human",
"role": "user",
"content": "who made you?"
}
]
@@ -218,6 +224,8 @@ Output:
Receiving event of type: updates
{'agent': {'messages': [{'content': 'I was created by OpenAI, a research organization focused on developing and advancing artificial intelligence technology.', 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-f5735b86-b80d-4c71-8dc3-4782b5a9c7c8', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
### Run default assistant
Now, we can run it on the default assistant and see that this second assistant is aware of the initial question, and can answer the question, "and you?":
=== "Python"
@@ -266,7 +274,7 @@ Now, we can run it on the default assistant and see that this second assistant i
"input": {
"messages": [
{
"role": "human",
"role": "user",
"content": "and you?"
}
]
+5 -5
View File
@@ -59,7 +59,7 @@ Now, let's run the graph on the first thread, and provide it some information ab
=== "Python"
```python
input = {"messages": [{"role": "human", "content": "i like pepperoni pizza"}]}
input = {"messages": [{"role": "user", "content": "i like pepperoni pizza"}]}
config = {"configurable": {"user_id": "123"}}
# stream values
async for chunk in client.runs.stream(
@@ -162,7 +162,7 @@ Let's stay on the same thread and provide some additional information. Note that
=== "Python"
```python
input = {"messages": [{"role": "human", "content": "i also just moved to SF"}]}
input = {"messages": [{"role": "user", "content": "i also just moved to SF"}]}
# stream values
async for chunk in client.runs.stream(
thread["thread_id"],
@@ -270,7 +270,7 @@ Now, let's run the graph on a completely different thread, and see that it remem
```python
# new thread for new conversation
thread = await client.threads.create()
input = {"messages": [{"role": "human", "content": "where and what should i eat for dinner? Can you list some restaurants?"}]}
input = {"messages": [{"role": "user", "content": "where and what should i eat for dinner? Can you list some restaurants?"}]}
# stream values
async for chunk in client.runs.stream(
thread["thread_id"],
@@ -330,7 +330,7 @@ Now, let's run the graph on a completely different thread, and see that it remem
"assistant_id": "agent",
"input": {
"messages": [{
"role": "human",
"role": "user",
"content": "where and what should i eat for dinner? Can you list some restaurants?"
}]
},
@@ -389,7 +389,7 @@ Let's now run the graph for another user to verify that the preferences of the f
# new thread for new conversation
thread = await client.threads.create()
# create input
input = {"messages": [{"role": "human", "content": "where do I live? what do I like to eat?"}]}
input = {"messages": [{"role": "user", "content": "where do I live? what do I like to eat?"}]}
config = {"configurable": {"user_id": "321"}}
# stream values
async for chunk in client.runs.stream(
+4 -2
View File
@@ -6,6 +6,8 @@ This guide covers how to stream debug events from your graph (`stream_mode="debu
- `task`: These events will get streamed before each super-step, and will contain information about a single task. Each super-step works by executing a list of tasks, where each task is scoped to a specific node and input. Below we will discuss the format of these tasks in more detail.
- `task_result`: After each `task` event, you will see a corresponding `task_result` event which as the name suggests contains information on the results of the task executed in the super-step. Scroll more to learn about the exact structure of these events.
## Setup
First let's set up our client and thread:
=== "Python"
@@ -56,7 +58,7 @@ Output:
'values': None
}
## Stream graph in debug mode
=== "Python"
@@ -65,7 +67,7 @@ Output:
input = {
"messages": [
{
"role": "human",
"role": "user",
"content": "What's the weather in SF?",
}
]
+5 -3
View File
@@ -2,6 +2,8 @@
This guide covers how to stream events from your graph (`stream_mode="events"`). Depending on the use case and user experience of your LangGraph application, your application may process event types differently. Read more about events in this [conceptual guide](https://langchain-ai.github.io/langgraph/concepts/low_level/#astream_events-for-streaming-tokens-of-llm-calls).
## Setup
=== "Python"
```python
@@ -50,7 +52,7 @@ Output:
'values': None
}
## Stream graph in events mode
Streaming events produces responses containing an `event` key (in addition to other keys such as `data`). See the LangChain [`Runnable.astream_events()` reference](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.Runnable.html#langchain_core.runnables.base.Runnable.astream_events) for all event types.
@@ -62,7 +64,7 @@ Streaming events produces responses containing an `event` key (in addition to ot
input = {
"messages": [
{
"role": "human",
"role": "user",
"content": "What's the weather in SF?",
}
]
@@ -87,7 +89,7 @@ Streaming events produces responses containing an `event` key (in addition to ot
const input = {
"messages": [
{
"role": "human",
"role": "user",
"content": "What's the weather in SF?",
}
]
@@ -38,6 +38,8 @@ With `stream_mode="messages"` two things will be streamed back:
Read more about how the `messages` streaming mode works [here](https://langchain-ai.github.io/langgraph/cloud/concepts/api/#modemessages)
## Setup
First let's set up our client and thread:
=== "Python"
@@ -179,6 +181,7 @@ Let's also define a helper function for better formatting of the tool calls in m
done
```
## Stream graph in messages mode
Now we can stream by messages, which will return complete messages (at the end of node execution) as well as tokens for any messages generated inside a node:
+5 -1
View File
@@ -2,6 +2,8 @@
This guide covers how to configure multiple streaming modes at the same time.
## Setup
First let's set up our client and thread:
=== "Python"
@@ -51,6 +53,8 @@ Output:
'values': None
}
## Stream graph with multiple modes
When configuring multiple streaming modes for a run, responses for each respective mode will be produced. In the following example, note that a `list` of modes (`messages`, `events`, `debug`) is passed to the `stream_mode` parameter and the response contains `events`, `debug`, `messages/complete`, `messages/metadata`, and `messages/partial` event types.
=== "Python"
@@ -60,7 +64,7 @@ When configuring multiple streaming modes for a run, responses for each respecti
input = {
"messages": [
{
"role": "human",
"role": "user",
"content": "What's the weather in SF?",
}
]
+5 -1
View File
@@ -2,6 +2,8 @@
This guide covers how to use `stream_mode="updates"` for your graph, which will stream the updates to the graph state that are made after each node is executed. This differs from using `stream_mode="values"`: instead of streaming the entire value of the state at each superstep, it only streams the updates from each of the nodes that made an update to the state at that superstep. Read [this conceptual guide](https://langchain-ai.github.io/langgraph/concepts/low_level/#stream-and-astream) to learn more.
## Setup
First let's set up our client and thread:
=== "Python"
@@ -51,6 +53,8 @@ Output:
'values': None
}
## Stream graph in updates mode
Now we can stream by updates, which outputs updates made to the state by each node after it has executed:
@@ -60,7 +64,7 @@ Now we can stream by updates, which outputs updates made to the state by each no
input = {
"messages": [
{
"role": "human",
"role": "user",
"content": "what's the weather in la"
}
]
+6 -2
View File
@@ -2,6 +2,8 @@
This guide covers how to use `stream_mode="values"`, which streams the value of the state at each superstep. This differs from using `stream_mode="updates"`: instead of streaming just the updates to the state from each node, it streams the entire graph state at that superstep. Read [this conceptual guide](https://langchain-ai.github.io/langgraph/concepts/low_level/#stream-and-astream) to learn more.
## Setup
First let's set up our client and thread:
=== "Python"
@@ -51,12 +53,14 @@ Output:
'values': None
}
## Stream graph in values mode
Now we can stream by values, which streams the full state of the graph after each node has finished executing:
=== "Python"
```python
input = {"messages": [{"role": "human", "content": "what's the weather in la"}]}
input = {"messages": [{"role": "user", "content": "what's the weather in la"}]}
# stream values
async for chunk in client.runs.stream(
@@ -73,7 +77,7 @@ Now we can stream by values, which streams the full state of the graph after eac
=== "Javascript"
```js
const input = {"messages": [{"role": "human", "content": "what's the weather in la"}]}
const input = {"messages": [{"role": "user", "content": "what's the weather in la"}]}
const streamResponse = client.runs.stream(
thread["thread_id"],
+8 -2
View File
@@ -14,7 +14,11 @@ The following endpoints accept `webhook` as a parameter:
- Stream Run Stateless -> POST /runs/stream
- Wait Run Stateless -> POST /runs/wait
In this example, we will show calling a webhook after streaming a run. First, let's setup our assistant and thread:
In this example, we will show calling a webhook after streaming a run.
## Setup
First, let's setup our assistant and thread:
=== "Python"
@@ -70,13 +74,15 @@ Output:
'values': None
}
## Use graph with a webhook
Now we can invoke a run with a webhook:
=== "Python"
```python
# create input
input = { "messages": [{ "role": "human", "content": "Hello!" }] }
input = { "messages": [{ "role": "user", "content": "Hello!" }] }
async for chunk in client.runs.stream(
thread_id=thread["thread_id"],
+6 -12
View File
@@ -74,18 +74,12 @@
"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": 4,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+23
View File
@@ -17,6 +17,16 @@
"![Screenshot 2024-07-09 at 2.55.56 PM.png](attachment:51f122de-b2ce-4c21-a5a7-c3be70c28a91.png)"
]
},
{
"cell_type": "markdown",
"id": "66b6b42d",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"First, let's install the required packages"
]
},
{
"cell_type": "code",
"execution_count": 2,
@@ -28,6 +38,19 @@
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"id": "73bac559",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "d6c05fc4-ecd8-483f-a9fd-b1a055f922d9",
@@ -17,7 +17,9 @@
"\n",
"![diagram](./img/virtual_user_diagram.png)\n",
"\n",
"First, we'll set up our environment."
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -27,8 +29,8 @@
"metadata": {},
"outputs": [],
"source": [
"# %%capture --no-stderr\n",
"# %pip install -U langgraph langchain langchain_openai"
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain langchain_openai"
]
},
{
@@ -47,13 +49,20 @@
" os.environ[var] = getpass.getpass(f\"Please provide your {var}\")\n",
"\n",
"\n",
"_set_if_undefined(\"OPENAI_API_KEY\")\n",
"_set_if_undefined(\"LANGCHAIN_API_KEY\")\n",
"\n",
"# Optional, add tracing in LangSmith.\n",
"# This will help you visualize and debug the control flow\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Agent Simulation Evaluation\""
"_set_if_undefined(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "95c9332f",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -61,7 +70,7 @@
"id": "6ef4528d-6b2a-47c7-98b5-50f14984a304",
"metadata": {},
"source": [
"## 1. Define Chat Bot\n",
"## Define Chat Bot\n",
"\n",
"Next, we will define our chat bot. For this notebook, we assume the bot's API accepts a list of messages and responds with a message. If you want to update this, all you'll have to change is this section and the \"get_messages_for_agent\" function in \n",
"the simulator below.\n",
@@ -123,7 +132,7 @@
"id": "419340a3-5ecf-48e7-9028-4f2fad750502",
"metadata": {},
"source": [
"## 2. Define Simulated User\n",
"## Define Simulated User\n",
"\n",
"We're now going to define the simulated user. \n",
"This can be anything we want, but we're going to build it as a LangChain bot."
@@ -192,7 +201,7 @@
"id": "321312b4-a1f0-4454-a481-fdac4e37cb7d",
"metadata": {},
"source": [
"## 3. Define the Agent Simulation\n",
"## Define the Agent Simulation\n",
"\n",
"The code below creates a LangGraph workflow to run the simulation. The main components are:\n",
"\n",
@@ -207,7 +216,7 @@
"id": "65bc4446-462b-4ee8-b017-2862fbbdfaf5",
"metadata": {},
"source": [
"**Nodes**\n",
"### Define nodes\n",
"\n",
"First, we define the nodes in the graph. These should take in a list of messages and return a list of messages to ADD to the state.\n",
"These will be thing wrappers around the chat bot and simulated user we have above.\n",
@@ -278,7 +287,7 @@
"id": "a48d8a3e-9171-4c43-a595-44d312722148",
"metadata": {},
"source": [
"**Edges**\n",
"### Define edges\n",
"\n",
"We now need to define the logic for the edges. The main logic occurs after the simulated user goes, and it should lead to one of two outcomes:\n",
"\n",
@@ -310,7 +319,7 @@
"id": "d0856d4f-9334-4f28-944b-06d303e913a4",
"metadata": {},
"source": [
"**Graph**\n",
"### Define graph\n",
"\n",
"We can now define the graph that sets up the simulation!"
]
@@ -358,7 +367,7 @@
"id": "2e0bd26e-8c1d-471d-9fef-d95dc0163491",
"metadata": {},
"source": [
"## 4. Run Simulation\n",
"## Run Simulation\n",
"\n",
"Now we can evaluate our chat bot! We can invoke it with empty messages (this will simulate letting the chat bot start the initial conversation)"
]
@@ -9,7 +9,9 @@
"\n",
"Building on our [previous example](./agent-simulation-evaluation.ipynb), we can show how to use simulated conversations to benchmark your chat bot using LangSmith.\n",
"\n",
"First, we'll install the prerequisites."
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -39,12 +41,20 @@
" os.environ[var] = getpass.getpass(f\"Please provide your {var}\")\n",
"\n",
"\n",
"_set_if_undefined(\"OPENAI_API_KEY\")\n",
"_set_if_undefined(\"LANGCHAIN_API_KEY\")\n",
"\n",
"# Optional, add tracing in LangSmith.\n",
"# This will help you visualize and debug the control flow\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\""
"_set_if_undefined(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "f84b7874",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
File diff suppressed because one or more lines are too long
@@ -28,6 +28,16 @@
"![Screenshot 2024-05-23 at 2.17.42 PM.png](attachment:67b615fe-0c25-4410-9d58-835982547001.png)"
]
},
{
"cell_type": "markdown",
"id": "95a34aa2",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"First, let's install our required packages and set the API keys we will need"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -38,6 +48,39 @@
"! pip install -U langchain_community langchain-openai langchain-anthropic langchain langgraph bs4"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "602be48f",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "0963fd21",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "38330223-d8c8-4156-82b6-93e63343bc01",
+49 -2
View File
@@ -9,7 +9,54 @@
"\n",
"Sometimes you want to be able to configure your agent when calling it. \n",
"Examples of this include configuring which LLM to use.\n",
"Below we walk through an example of doing so."
"Below we walk through an example of doing so.\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "03df6e04",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain_anthropic"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a00c45e0",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "55e8be3b",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -17,7 +64,7 @@
"id": "df1ff9cf-f8d2-4109-adf9-2adec83f5a95",
"metadata": {},
"source": [
"## Base\n",
"## Define graph\n",
"\n",
"First, let's create a very simple graph"
]
+19 -17
View File
@@ -17,7 +17,9 @@
"id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -33,18 +35,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "23a1885c-04ab-4750-aefa-105891fddf3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -55,12 +49,20 @@
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\""
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "d4c5c054",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+19 -17
View File
@@ -17,7 +17,9 @@
"id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -33,18 +35,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "23a1885c-04ab-4750-aefa-105891fddf3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -55,12 +49,20 @@
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\""
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "87a00ce9",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+19 -17
View File
@@ -17,7 +17,9 @@
"id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -33,18 +35,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "23a1885c-04ab-4750-aefa-105891fddf3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -55,12 +49,20 @@
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\""
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "715867c6",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+19 -17
View File
@@ -31,7 +31,9 @@
"id": "7be3889f-3c17-4fa1-bd2b-84114a2c7247",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First let's install the required packages and set our API keys"
]
},
{
@@ -47,18 +49,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "23a1885c-04ab-4750-aefa-105891fddf3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -69,12 +63,20 @@
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Create ReAct Agent Tutorial\""
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "035b920d",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -54,12 +54,20 @@
"\n",
"\n",
"_set_env(\"ANTHROPIC_API_KEY\")\n",
"_set_env(\"TAVILY_API_KEY\")\n",
"\n",
"# Recommended\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Customer Support Bot Tutorial\""
"_set_env(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "caae4bb8",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+33 -23
View File
@@ -15,15 +15,9 @@
"\n",
"We will cover two approaches to the last technique here, since it is generally applicable across any LLM that supports tool calling.\n",
"\n",
"## Regular Extraction with Retries\n",
"## Setup\n",
"\n",
"Both examples here invoke a simple looping graph that takes following approach:\n",
"1. Prompt the LLM to respond.\n",
"2. If it responds with tool calls, validate those.\n",
"3. If the calls are correct, return. Otherwise, format the validation error as a new [ToolMessage](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolMessage.html#langchain_core.messages.tool.ToolMessage) and prompt the LLM to fix the errors. Taking us back to step (1).\n",
"\n",
"\n",
"The techniques differ only on step (3). In this first step, we will prompt the original LLM to regenerate the function calls to fix the validation errors. In the next section, we will instead prompt the LLM to generate a **patch** to fix the errors, meaning it doesn't have to re-generate data that is valid."
"First, let's install the required packages and set our API keys"
]
},
{
@@ -34,16 +28,7 @@
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langchain-anthropic langgraph\n",
"# Or do langchain-{groq|openai|etc.} for another package with tool calling"
]
},
{
"cell_type": "markdown",
"id": "27b25a1c-f437-482a-97d3-c7f168986df5",
"metadata": {},
"source": [
"Set up your environment. If you are using groq, anthropic, etc., you will need to update different API keys."
"%pip install -U langchain-anthropic langgraph"
]
},
{
@@ -62,11 +47,36 @@
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"# Recommended to visualize the retry steps\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Extraction Notebook\""
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "f07bc7a6",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "ba53b3c0",
"metadata": {},
"source": [
"## Regular Extraction with Retries\n",
"\n",
"Both examples here invoke a simple looping graph that takes following approach:\n",
"1. Prompt the LLM to respond.\n",
"2. If it responds with tool calls, validate those.\n",
"3. If the calls are correct, return. Otherwise, format the validation error as a new [ToolMessage](https://api.python.langchain.com/en/latest/messages/langchain_core.messages.tool.ToolMessage.html#langchain_core.messages.tool.ToolMessage) and prompt the LLM to fix the errors. Taking us back to step (1).\n",
"\n",
"\n",
"The techniques differ only on step (3). In this first step, we will prompt the original LLM to regenerate the function calls to fix the validation errors. In the next section, we will instead prompt the LLM to generate a **patch** to fix the errors, meaning it doesn't have to re-generate data that is valid."
]
},
{
+8 -14
View File
@@ -55,7 +55,7 @@
"metadata": {},
"outputs": [
{
"name": "stdin",
"name": "stdout",
"output_type": "stream",
"text": [
"ANTHROPIC_API_KEY: ········\n"
@@ -80,18 +80,12 @@
"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": 3,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -200,7 +194,7 @@
]
},
{
"name": "stdin",
"name": "stdout",
"output_type": "stream",
"text": [
"Do you want to go to Step 3? (yes/no): yes\n"
@@ -17,7 +17,11 @@
"\n",
"In LangGraph you can add breakpoints before / after a node is executed. But oftentimes it may be helpful to **dynamically** interrupt the graph from inside a given node based on some condition. When doing so, it may also be helpful to include information about **why** that interrupt was raised.\n",
"\n",
"This guide shows how you can dynamically interrupt the graph using `NodeInterupt` -- a special exception that can be raised from inside a node. Let's see it in action!"
"This guide shows how you can dynamically interrupt the graph using `NodeInterupt` -- a special exception that can be raised from inside a node. Let's see it in action!\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages"
]
},
{
@@ -27,7 +31,21 @@
"metadata": {},
"outputs": [],
"source": [
"!pip install -U langgraph"
"%%capture --no-stderr\n",
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"id": "d9f9574b",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -35,7 +53,7 @@
"id": "e9aa244f-1dd9-450e-9526-b1a28b30f84f",
"metadata": {},
"source": [
"### Define the graph"
"## Define the graph"
]
},
{
@@ -111,7 +129,7 @@
"id": "ad5521e1-0e58-42c5-9282-ff96f24ee6f6",
"metadata": {},
"source": [
"### Run the graph with dynamic interrupt"
"## Run the graph with dynamic interrupt"
]
},
{
@@ -288,7 +306,7 @@
"id": "a5862dea-2af2-48cb-9889-979b6c6af6aa",
"metadata": {},
"source": [
"### Update the graph state"
"## Update the graph state"
]
},
{
@@ -80,18 +80,12 @@
"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": 3,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -6,7 +6,7 @@
"id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
"metadata": {},
"source": [
"# Review Tool Calls\n",
"# How to Review Tool Calls\n",
"\n",
"Human-in-the-loop (HIL) interactions are crucial for [agentic systems](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#human-in-the-loop). A common pattern is to add some human in the loop step after certain tool calls. These tool calls often lead to either a function call or saving of some information. Examples include:\n",
"\n",
@@ -77,18 +77,12 @@
"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": 3,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+6 -12
View File
@@ -84,18 +84,12 @@
"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": 3,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -50,18 +50,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ANTHROPIC_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -81,18 +73,12 @@
"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": 3,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+37 -1
View File
@@ -9,7 +9,43 @@
"\n",
"By default, `StateGraph` takes in a single schema and all nodes are expected to communicate with that schema. However, it is also possible to define explicit input and output schemas for a graph. This is helpful if you want to draw a distinction between input and output keys.\n",
"\n",
"In this notebook we'll walk through an example of this. At a high level, in order to do this you simply have to pass in `input=..., output=...` when defining the graph. Let's see an example below!"
"In this notebook we'll walk through an example of this. At a high level, in order to do this you simply have to pass in `input=..., output=...` when defining the graph. Let's see an example below!\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "678286f2",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"id": "16aad512",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "72689b3d",
"metadata": {},
"source": [
"## Define and use the graph"
]
},
{
+6 -13
View File
@@ -68,19 +68,12 @@
"id": "a98c72cf-33f9-4a37-9634-6c93a7c28815",
"metadata": {},
"source": [
"(Encouraged) [LangSmith](https://smith.langchain.com/) makes it a lot easier to see what's going on \"under the hood.\""
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "13cba9af-0572-41df-92f8-d6f56d5b5322",
"metadata": {},
"outputs": [],
"source": [
"_set_env(\"LANGSMITH_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"LangGraph Tutorial\""
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+446 -20
View File
@@ -24,7 +24,7 @@
"id": "db28668b-5491-4c93-a961-bd339f09202c",
"metadata": {},
"source": [
"## 0. Prerequisites\n",
"## Setup\n",
"\n",
"Install `langgraph` (for the framework), `langchain_openai` (for the LLM), and `langchain` + `tavily-python` (for the search engine).\n",
"\n",
@@ -37,7 +37,11 @@
"id": "dcc9159b-cc8c-426d-9670-3e8ada06723f",
"metadata": {},
"outputs": [],
"source": ["%%capture --no-stderr\n%pip install -U --quiet langchain langgraph langchain_openai\n%pip install -U --quiet tavily-python"]
"source": [
"%%capture --no-stderr\n",
"%pip install -U --quiet langchain langgraph langchain_openai\n",
"%pip install -U --quiet tavily-python"
]
},
{
"cell_type": "code",
@@ -45,7 +49,33 @@
"id": "a177ecc9-0c96-460f-9b39-9c1ce54754f1",
"metadata": {},
"outputs": [],
"source": ["from __future__ import annotations # noqa: F404\n\nimport getpass\nimport os\n\n\ndef _set_if_undefined(var: str) -> None:\n if os.environ.get(var):\n return\n os.environ[var] = getpass.getpass(var)\n\n\n# Optional: Configure tracing to visualize and debug the agent\n_set_if_undefined(\"LANGCHAIN_API_KEY\")\nos.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_PROJECT\"] = \"LATS\"\n\n_set_if_undefined(\"OPENAI_API_KEY\")\n_set_if_undefined(\"TAVILY_API_KEY\")"]
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_if_undefined(var: str) -> None:\n",
" if os.environ.get(var):\n",
" return\n",
" os.environ[var] = getpass.getpass(var)\n",
"\n",
"\n",
"_set_if_undefined(\"OPENAI_API_KEY\")\n",
"_set_if_undefined(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "8b3cac91",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
@@ -69,7 +99,134 @@
"id": "54c6f319-3966-4f66-aa7b-50e249189111",
"metadata": {},
"outputs": [],
"source": ["import math\nfrom collections import deque\nfrom typing import Optional\n\nfrom langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage\n\n\nclass Node:\n def __init__(\n self,\n messages: list[BaseMessage],\n reflection: Reflection,\n parent: Optional[Node] = None,\n ):\n self.messages = messages\n self.parent = parent\n self.children = []\n self.value = 0\n self.visits = 0\n self.reflection = reflection\n self.depth = parent.depth + 1 if parent is not None else 1\n self._is_solved = reflection.found_solution if reflection else False\n if self._is_solved:\n self._mark_tree_as_solved()\n self.backpropagate(reflection.normalized_score)\n\n def __repr__(self) -> str:\n return (\n f\"<Node value={self.value}, visits={self.visits},\"\n f\" solution={self.messages} reflection={self.reflection}/>\"\n )\n\n @property\n def is_solved(self):\n \"\"\"If any solutions exist, we can end the search.\"\"\"\n return self._is_solved\n\n @property\n def is_terminal(self):\n return not self.children\n\n @property\n def best_child(self):\n \"\"\"Select the child with the highest UCT to search next.\"\"\"\n if not self.children:\n return None\n all_nodes = self._get_all_children()\n return max(all_nodes, key=lambda child: child.upper_confidence_bound())\n\n @property\n def best_child_score(self):\n \"\"\"Return the child with the highest value.\"\"\"\n if not self.children:\n return None\n return max(self.children, key=lambda child: int(child.is_solved) * child.value)\n\n @property\n def height(self) -> int:\n \"\"\"Check for how far we've rolled out the tree.\"\"\"\n if self.children:\n return 1 + max([child.height for child in self.children])\n return 1\n\n def upper_confidence_bound(self, exploration_weight=1.0):\n \"\"\"Return the UCT score. This helps balance exploration vs. exploitation of a branch.\"\"\"\n if self.parent is None:\n raise ValueError(\"Cannot obtain UCT from root node\")\n if self.visits == 0:\n return self.value\n # Encourages exploitation of high-value trajectories\n average_reward = self.value / self.visits\n # Encourages exploration of less-visited trajectories\n exploration_term = math.sqrt(math.log(self.parent.visits) / self.visits)\n return average_reward + exploration_weight * exploration_term\n\n def backpropagate(self, reward: float):\n \"\"\"Update the score of this node and its parents.\"\"\"\n node = self\n while node:\n node.visits += 1\n node.value = (node.value * (node.visits - 1) + reward) / node.visits\n node = node.parent\n\n def get_messages(self, include_reflections: bool = True):\n if include_reflections:\n return self.messages + [self.reflection.as_message()]\n return self.messages\n\n def get_trajectory(self, include_reflections: bool = True) -> list[BaseMessage]:\n \"\"\"Get messages representing this search branch.\"\"\"\n messages = []\n node = self\n while node:\n messages.extend(\n node.get_messages(include_reflections=include_reflections)[::-1]\n )\n node = node.parent\n # Reverse the final back-tracked trajectory to return in the correct order\n return messages[::-1] # root solution, reflection, child 1, ...\n\n def _get_all_children(self):\n all_nodes = []\n nodes = deque()\n nodes.append(self)\n while nodes:\n node = nodes.popleft()\n all_nodes.extend(node.children)\n for n in node.children:\n nodes.append(n)\n return all_nodes\n\n def get_best_solution(self):\n \"\"\"Return the best solution from within the current sub-tree.\"\"\"\n all_nodes = [self] + self._get_all_children()\n best_node = max(\n all_nodes,\n # We filter out all non-terminal, non-solution trajectories\n key=lambda node: int(node.is_terminal and node.is_solved) * node.value,\n )\n return best_node\n\n def _mark_tree_as_solved(self):\n parent = self.parent\n while parent:\n parent._is_solved = True\n parent = parent.parent"]
"source": [
"import math\n",
"from collections import deque\n",
"from typing import Optional\n",
"\n",
"from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage\n",
"\n",
"\n",
"class Node:\n",
" def __init__(\n",
" self,\n",
" messages: list[BaseMessage],\n",
" reflection: Reflection,\n",
" parent: Optional[Node] = None,\n",
" ):\n",
" self.messages = messages\n",
" self.parent = parent\n",
" self.children = []\n",
" self.value = 0\n",
" self.visits = 0\n",
" self.reflection = reflection\n",
" self.depth = parent.depth + 1 if parent is not None else 1\n",
" self._is_solved = reflection.found_solution if reflection else False\n",
" if self._is_solved:\n",
" self._mark_tree_as_solved()\n",
" self.backpropagate(reflection.normalized_score)\n",
"\n",
" def __repr__(self) -> str:\n",
" return (\n",
" f\"<Node value={self.value}, visits={self.visits},\"\n",
" f\" solution={self.messages} reflection={self.reflection}/>\"\n",
" )\n",
"\n",
" @property\n",
" def is_solved(self):\n",
" \"\"\"If any solutions exist, we can end the search.\"\"\"\n",
" return self._is_solved\n",
"\n",
" @property\n",
" def is_terminal(self):\n",
" return not self.children\n",
"\n",
" @property\n",
" def best_child(self):\n",
" \"\"\"Select the child with the highest UCT to search next.\"\"\"\n",
" if not self.children:\n",
" return None\n",
" all_nodes = self._get_all_children()\n",
" return max(all_nodes, key=lambda child: child.upper_confidence_bound())\n",
"\n",
" @property\n",
" def best_child_score(self):\n",
" \"\"\"Return the child with the highest value.\"\"\"\n",
" if not self.children:\n",
" return None\n",
" return max(self.children, key=lambda child: int(child.is_solved) * child.value)\n",
"\n",
" @property\n",
" def height(self) -> int:\n",
" \"\"\"Check for how far we've rolled out the tree.\"\"\"\n",
" if self.children:\n",
" return 1 + max([child.height for child in self.children])\n",
" return 1\n",
"\n",
" def upper_confidence_bound(self, exploration_weight=1.0):\n",
" \"\"\"Return the UCT score. This helps balance exploration vs. exploitation of a branch.\"\"\"\n",
" if self.parent is None:\n",
" raise ValueError(\"Cannot obtain UCT from root node\")\n",
" if self.visits == 0:\n",
" return self.value\n",
" # Encourages exploitation of high-value trajectories\n",
" average_reward = self.value / self.visits\n",
" # Encourages exploration of less-visited trajectories\n",
" exploration_term = math.sqrt(math.log(self.parent.visits) / self.visits)\n",
" return average_reward + exploration_weight * exploration_term\n",
"\n",
" def backpropagate(self, reward: float):\n",
" \"\"\"Update the score of this node and its parents.\"\"\"\n",
" node = self\n",
" while node:\n",
" node.visits += 1\n",
" node.value = (node.value * (node.visits - 1) + reward) / node.visits\n",
" node = node.parent\n",
"\n",
" def get_messages(self, include_reflections: bool = True):\n",
" if include_reflections:\n",
" return self.messages + [self.reflection.as_message()]\n",
" return self.messages\n",
"\n",
" def get_trajectory(self, include_reflections: bool = True) -> list[BaseMessage]:\n",
" \"\"\"Get messages representing this search branch.\"\"\"\n",
" messages = []\n",
" node = self\n",
" while node:\n",
" messages.extend(\n",
" node.get_messages(include_reflections=include_reflections)[::-1]\n",
" )\n",
" node = node.parent\n",
" # Reverse the final back-tracked trajectory to return in the correct order\n",
" return messages[::-1] # root solution, reflection, child 1, ...\n",
"\n",
" def _get_all_children(self):\n",
" all_nodes = []\n",
" nodes = deque()\n",
" nodes.append(self)\n",
" while nodes:\n",
" node = nodes.popleft()\n",
" all_nodes.extend(node.children)\n",
" for n in node.children:\n",
" nodes.append(n)\n",
" return all_nodes\n",
"\n",
" def get_best_solution(self):\n",
" \"\"\"Return the best solution from within the current sub-tree.\"\"\"\n",
" all_nodes = [self] + self._get_all_children()\n",
" best_node = max(\n",
" all_nodes,\n",
" # We filter out all non-terminal, non-solution trajectories\n",
" key=lambda node: int(node.is_terminal and node.is_solved) * node.value,\n",
" )\n",
" return best_node\n",
"\n",
" def _mark_tree_as_solved(self):\n",
" parent = self.parent\n",
" while parent:\n",
" parent._is_solved = True\n",
" parent = parent.parent"
]
},
{
"cell_type": "markdown",
@@ -87,7 +244,16 @@
"id": "e10c94ba-9daa-4899-97ce-4f28428c2c38",
"metadata": {},
"outputs": [],
"source": ["from typing_extensions import TypedDict\n\n\nclass TreeState(TypedDict):\n # The full tree\n root: Node\n # The original input\n input: str"]
"source": [
"from typing_extensions import TypedDict\n",
"\n",
"\n",
"class TreeState(TypedDict):\n",
" # The full tree\n",
" root: Node\n",
" # The original input\n",
" input: str"
]
},
{
"cell_type": "markdown",
@@ -110,7 +276,11 @@
"id": "48738896-42ac-47eb-b482-0d4d4dd86c87",
"metadata": {},
"outputs": [],
"source": ["from langchain_openai import ChatOpenAI\n\nllm = ChatOpenAI(model=\"gpt-4o\")"]
"source": [
"from langchain_openai import ChatOpenAI\n",
"\n",
"llm = ChatOpenAI(model=\"gpt-4o\")"
]
},
{
"cell_type": "markdown",
@@ -128,7 +298,17 @@
"id": "55c2aff3-f454-43da-8f45-1a3d46523cd5",
"metadata": {},
"outputs": [],
"source": ["from langchain_community.tools.tavily_search import TavilySearchResults\nfrom langchain_community.utilities.tavily_search import TavilySearchAPIWrapper\n\nfrom langgraph.prebuilt.tool_executor import ToolExecutor, ToolInvocation\n\nsearch = TavilySearchAPIWrapper()\ntavily_tool = TavilySearchResults(api_wrapper=search, max_results=5)\ntools = [tavily_tool]\ntool_executor = ToolExecutor(tools=tools)"]
"source": [
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
"from langchain_community.utilities.tavily_search import TavilySearchAPIWrapper\n",
"\n",
"from langgraph.prebuilt.tool_executor import ToolExecutor, ToolInvocation\n",
"\n",
"search = TavilySearchAPIWrapper()\n",
"tavily_tool = TavilySearchResults(api_wrapper=search, max_results=5)\n",
"tools = [tavily_tool]\n",
"tool_executor = ToolExecutor(tools=tools)"
]
},
{
"cell_type": "markdown",
@@ -147,7 +327,68 @@
"id": "ddfd1750-c265-4b29-b505-83b1c5e2d30e",
"metadata": {},
"outputs": [],
"source": ["from langchain_core.output_parsers.openai_tools import (\n JsonOutputToolsParser,\n PydanticToolsParser,\n)\nfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\nfrom langchain_core.pydantic_v1 import BaseModel, Field\nfrom langchain_core.runnables import chain as as_runnable\n\n\nclass Reflection(BaseModel):\n reflections: str = Field(\n description=\"The critique and reflections on the sufficiency, superfluency,\"\n \" and general quality of the response\"\n )\n score: int = Field(\n description=\"Score from 0-10 on the quality of the candidate response.\",\n gte=0,\n lte=10,\n )\n found_solution: bool = Field(\n description=\"Whether the response has fully solved the question or task.\"\n )\n\n def as_message(self):\n return HumanMessage(\n content=f\"Reasoning: {self.reflections}\\nScore: {self.score}\"\n )\n\n @property\n def normalized_score(self) -> float:\n return self.score / 10.0\n\n\nprompt = ChatPromptTemplate.from_messages(\n [\n (\n \"system\",\n \"Reflect and grade the assistant response to the user question below.\",\n ),\n (\"user\", \"{input}\"),\n MessagesPlaceholder(variable_name=\"candidate\"),\n ]\n)\n\nreflection_llm_chain = (\n prompt\n | llm.bind_tools(tools=[Reflection], tool_choice=\"Reflection\").with_config(\n run_name=\"Reflection\"\n )\n | PydanticToolsParser(tools=[Reflection])\n)\n\n\n@as_runnable\ndef reflection_chain(inputs) -> Reflection:\n tool_choices = reflection_llm_chain.invoke(inputs)\n reflection = tool_choices[0]\n if not isinstance(inputs[\"candidate\"][-1], AIMessage):\n reflection.found_solution = False\n return reflection"]
"source": [
"from langchain_core.output_parsers.openai_tools import (\n",
" JsonOutputToolsParser,\n",
" PydanticToolsParser,\n",
")\n",
"from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
"from langchain_core.runnables import chain as as_runnable\n",
"\n",
"\n",
"class Reflection(BaseModel):\n",
" reflections: str = Field(\n",
" description=\"The critique and reflections on the sufficiency, superfluency,\"\n",
" \" and general quality of the response\"\n",
" )\n",
" score: int = Field(\n",
" description=\"Score from 0-10 on the quality of the candidate response.\",\n",
" gte=0,\n",
" lte=10,\n",
" )\n",
" found_solution: bool = Field(\n",
" description=\"Whether the response has fully solved the question or task.\"\n",
" )\n",
"\n",
" def as_message(self):\n",
" return HumanMessage(\n",
" content=f\"Reasoning: {self.reflections}\\nScore: {self.score}\"\n",
" )\n",
"\n",
" @property\n",
" def normalized_score(self) -> float:\n",
" return self.score / 10.0\n",
"\n",
"\n",
"prompt = ChatPromptTemplate.from_messages(\n",
" [\n",
" (\n",
" \"system\",\n",
" \"Reflect and grade the assistant response to the user question below.\",\n",
" ),\n",
" (\"user\", \"{input}\"),\n",
" MessagesPlaceholder(variable_name=\"candidate\"),\n",
" ]\n",
")\n",
"\n",
"reflection_llm_chain = (\n",
" prompt\n",
" | llm.bind_tools(tools=[Reflection], tool_choice=\"Reflection\").with_config(\n",
" run_name=\"Reflection\"\n",
" )\n",
" | PydanticToolsParser(tools=[Reflection])\n",
")\n",
"\n",
"\n",
"@as_runnable\n",
"def reflection_chain(inputs) -> Reflection:\n",
" tool_choices = reflection_llm_chain.invoke(inputs)\n",
" reflection = tool_choices[0]\n",
" if not isinstance(inputs[\"candidate\"][-1], AIMessage):\n",
" reflection.found_solution = False\n",
" return reflection"
]
},
{
"cell_type": "markdown",
@@ -165,7 +406,29 @@
"id": "72fc5363-f0f3-4362-8499-14eb583bd75b",
"metadata": {},
"outputs": [],
"source": ["from langchain_core.prompt_values import ChatPromptValue\nfrom langchain_core.runnables import RunnableConfig\n\nprompt_template = ChatPromptTemplate.from_messages(\n [\n (\n \"system\",\n \"You are an AI assistant.\",\n ),\n (\"user\", \"{input}\"),\n MessagesPlaceholder(variable_name=\"messages\", optional=True),\n ]\n)\n\n\ninitial_answer_chain = prompt_template | llm.bind_tools(tools=tools).with_config(\n run_name=\"GenerateInitialCandidate\"\n)\n\n\nparser = JsonOutputToolsParser(return_id=True)"]
"source": [
"from langchain_core.prompt_values import ChatPromptValue\n",
"from langchain_core.runnables import RunnableConfig\n",
"\n",
"prompt_template = ChatPromptTemplate.from_messages(\n",
" [\n",
" (\n",
" \"system\",\n",
" \"You are an AI assistant.\",\n",
" ),\n",
" (\"user\", \"{input}\"),\n",
" MessagesPlaceholder(variable_name=\"messages\", optional=True),\n",
" ]\n",
")\n",
"\n",
"\n",
"initial_answer_chain = prompt_template | llm.bind_tools(tools=tools).with_config(\n",
" run_name=\"GenerateInitialCandidate\"\n",
")\n",
"\n",
"\n",
"parser = JsonOutputToolsParser(return_id=True)"
]
},
{
"cell_type": "code",
@@ -184,7 +447,12 @@
"output_type": "execute_result"
}
],
"source": ["initial_response = initial_answer_chain.invoke(\n {\"input\": \"Write a research report on lithium pollution.\"}\n)\ninitial_response"]
"source": [
"initial_response = initial_answer_chain.invoke(\n",
" {\"input\": \"Write a research report on lithium pollution.\"}\n",
")\n",
"initial_response"
]
},
{
"cell_type": "markdown",
@@ -202,7 +470,31 @@
"id": "5b6b173c-78f5-4ae1-80b3-28c80e68f5c5",
"metadata": {},
"outputs": [],
"source": ["import json\n\n\n# Define the node we will add to the graph\ndef generate_initial_response(state: TreeState) -> dict:\n \"\"\"Generate the initial candidate response.\"\"\"\n res = initial_answer_chain.invoke({\"input\": state[\"input\"]})\n parsed = parser.invoke(res)\n tool_responses = tool_executor.batch(\n [ToolInvocation(tool=r[\"type\"], tool_input=r[\"args\"]) for r in parsed]\n )\n output_messages = [res] + [\n ToolMessage(content=json.dumps(resp), tool_call_id=tool_call[\"id\"])\n for resp, tool_call in zip(tool_responses, parsed)\n ]\n reflection = reflection_chain.invoke(\n {\"input\": state[\"input\"], \"candidate\": output_messages}\n )\n root = Node(output_messages, reflection=reflection)\n return {\n **state,\n \"root\": root,\n }"]
"source": [
"import json\n",
"\n",
"\n",
"# Define the node we will add to the graph\n",
"def generate_initial_response(state: TreeState) -> dict:\n",
" \"\"\"Generate the initial candidate response.\"\"\"\n",
" res = initial_answer_chain.invoke({\"input\": state[\"input\"]})\n",
" parsed = parser.invoke(res)\n",
" tool_responses = tool_executor.batch(\n",
" [ToolInvocation(tool=r[\"type\"], tool_input=r[\"args\"]) for r in parsed]\n",
" )\n",
" output_messages = [res] + [\n",
" ToolMessage(content=json.dumps(resp), tool_call_id=tool_call[\"id\"])\n",
" for resp, tool_call in zip(tool_responses, parsed)\n",
" ]\n",
" reflection = reflection_chain.invoke(\n",
" {\"input\": state[\"input\"], \"candidate\": output_messages}\n",
" )\n",
" root = Node(output_messages, reflection=reflection)\n",
" return {\n",
" **state,\n",
" \"root\": root,\n",
" }"
]
},
{
"cell_type": "markdown",
@@ -220,7 +512,26 @@
"id": "550bff9a-86aa-43ad-ad98-506e97c122d2",
"metadata": {},
"outputs": [],
"source": ["# This generates N candidate values\n# for a single input to sample actions from the environment\n\n\ndef generate_candidates(messages: ChatPromptValue, config: RunnableConfig):\n n = config[\"configurable\"].get(\"N\", 5)\n bound_kwargs = llm.bind_tools(tools=tools).kwargs\n chat_result = llm.generate(\n [messages.to_messages()],\n n=n,\n callbacks=config[\"callbacks\"],\n run_name=\"GenerateCandidates\",\n **bound_kwargs,\n )\n return [gen.message for gen in chat_result.generations[0]]\n\n\nexpansion_chain = prompt_template | generate_candidates"]
"source": [
"# This generates N candidate values\n",
"# for a single input to sample actions from the environment\n",
"\n",
"\n",
"def generate_candidates(messages: ChatPromptValue, config: RunnableConfig):\n",
" n = config[\"configurable\"].get(\"N\", 5)\n",
" bound_kwargs = llm.bind_tools(tools=tools).kwargs\n",
" chat_result = llm.generate(\n",
" [messages.to_messages()],\n",
" n=n,\n",
" callbacks=config[\"callbacks\"],\n",
" run_name=\"GenerateCandidates\",\n",
" **bound_kwargs,\n",
" )\n",
" return [gen.message for gen in chat_result.generations[0]]\n",
"\n",
"\n",
"expansion_chain = prompt_template | generate_candidates"
]
},
{
"cell_type": "code",
@@ -243,7 +554,10 @@
"output_type": "execute_result"
}
],
"source": ["res = expansion_chain.invoke({\"input\": \"Write a research report on lithium pollution.\"})\nres"]
"source": [
"res = expansion_chain.invoke({\"input\": \"Write a research report on lithium pollution.\"})\n",
"res"
]
},
{
"cell_type": "markdown",
@@ -262,7 +576,55 @@
"id": "d32af859-53e8-46be-8182-7d522be31f54",
"metadata": {},
"outputs": [],
"source": ["from collections import defaultdict\n\n\ndef expand(state: TreeState, config: RunnableConfig) -> dict:\n \"\"\"Starting from the \"best\" node in the tree, generate N candidates for the next step.\"\"\"\n root = state[\"root\"]\n best_candidate: Node = root.best_child if root.children else root\n messages = best_candidate.get_trajectory()\n # Generate N candidates from the single child candidate\n new_candidates = expansion_chain.invoke(\n {\"input\": state[\"input\"], \"messages\": messages}, config\n )\n parsed = parser.batch(new_candidates)\n flattened = [\n (i, tool_call)\n for i, tool_calls in enumerate(parsed)\n for tool_call in tool_calls\n ]\n tool_responses = tool_executor.batch(\n [\n ToolInvocation(tool=tool_call[\"type\"], tool_input=tool_call[\"args\"])\n for _, tool_call in flattened\n ]\n )\n collected_responses = defaultdict(list)\n for (i, tool_call), resp in zip(flattened, tool_responses):\n collected_responses[i].append(\n ToolMessage(content=json.dumps(resp), tool_call_id=tool_call[\"id\"])\n )\n output_messages = []\n for i, candidate in enumerate(new_candidates):\n output_messages.append([candidate] + collected_responses[i])\n\n # Reflect on each candidate\n # For tasks with external validation, you'd add that here.\n reflections = reflection_chain.batch(\n [{\"input\": state[\"input\"], \"candidate\": msges} for msges in output_messages],\n config,\n )\n # Grow tree\n child_nodes = [\n Node(cand, parent=best_candidate, reflection=reflection)\n for cand, reflection in zip(output_messages, reflections)\n ]\n best_candidate.children.extend(child_nodes)\n # We have already extended the tree directly, so we just return the state\n return state"]
"source": [
"from collections import defaultdict\n",
"\n",
"\n",
"def expand(state: TreeState, config: RunnableConfig) -> dict:\n",
" \"\"\"Starting from the \"best\" node in the tree, generate N candidates for the next step.\"\"\"\n",
" root = state[\"root\"]\n",
" best_candidate: Node = root.best_child if root.children else root\n",
" messages = best_candidate.get_trajectory()\n",
" # Generate N candidates from the single child candidate\n",
" new_candidates = expansion_chain.invoke(\n",
" {\"input\": state[\"input\"], \"messages\": messages}, config\n",
" )\n",
" parsed = parser.batch(new_candidates)\n",
" flattened = [\n",
" (i, tool_call)\n",
" for i, tool_calls in enumerate(parsed)\n",
" for tool_call in tool_calls\n",
" ]\n",
" tool_responses = tool_executor.batch(\n",
" [\n",
" ToolInvocation(tool=tool_call[\"type\"], tool_input=tool_call[\"args\"])\n",
" for _, tool_call in flattened\n",
" ]\n",
" )\n",
" collected_responses = defaultdict(list)\n",
" for (i, tool_call), resp in zip(flattened, tool_responses):\n",
" collected_responses[i].append(\n",
" ToolMessage(content=json.dumps(resp), tool_call_id=tool_call[\"id\"])\n",
" )\n",
" output_messages = []\n",
" for i, candidate in enumerate(new_candidates):\n",
" output_messages.append([candidate] + collected_responses[i])\n",
"\n",
" # Reflect on each candidate\n",
" # For tasks with external validation, you'd add that here.\n",
" reflections = reflection_chain.batch(\n",
" [{\"input\": state[\"input\"], \"candidate\": msges} for msges in output_messages],\n",
" config,\n",
" )\n",
" # Grow tree\n",
" child_nodes = [\n",
" Node(cand, parent=best_candidate, reflection=reflection)\n",
" for cand, reflection in zip(output_messages, reflections)\n",
" ]\n",
" best_candidate.children.extend(child_nodes)\n",
" # We have already extended the tree directly, so we just return the state\n",
" return state"
]
},
{
"cell_type": "markdown",
@@ -280,7 +642,41 @@
"id": "8aec0f20-f978-4df0-8900-e3a1f0544f6d",
"metadata": {},
"outputs": [],
"source": ["from typing import Literal\n\nfrom langgraph.graph import END, StateGraph, START\n\n\ndef should_loop(state: TreeState) -> Literal[\"expand\", \"__end__\"]:\n \"\"\"Determine whether to continue the tree search.\"\"\"\n root = state[\"root\"]\n if root.is_solved:\n return END\n if root.height > 5:\n return END\n return \"expand\"\n\n\nbuilder = StateGraph(TreeState)\nbuilder.add_node(\"start\", generate_initial_response)\nbuilder.add_node(\"expand\", expand)\nbuilder.add_edge(START, \"start\")\n\n\nbuilder.add_conditional_edges(\n \"start\",\n # Either expand/rollout or finish\n should_loop,\n)\nbuilder.add_conditional_edges(\n \"expand\",\n # Either continue to rollout or finish\n should_loop,\n)\n\ngraph = builder.compile()"]
"source": [
"from typing import Literal\n",
"\n",
"from langgraph.graph import END, StateGraph, START\n",
"\n",
"\n",
"def should_loop(state: TreeState) -> Literal[\"expand\", \"__end__\"]:\n",
" \"\"\"Determine whether to continue the tree search.\"\"\"\n",
" root = state[\"root\"]\n",
" if root.is_solved:\n",
" return END\n",
" if root.height > 5:\n",
" return END\n",
" return \"expand\"\n",
"\n",
"\n",
"builder = StateGraph(TreeState)\n",
"builder.add_node(\"start\", generate_initial_response)\n",
"builder.add_node(\"expand\", expand)\n",
"builder.add_edge(START, \"start\")\n",
"\n",
"\n",
"builder.add_conditional_edges(\n",
" \"start\",\n",
" # Either expand/rollout or finish\n",
" should_loop,\n",
")\n",
"builder.add_conditional_edges(\n",
" \"expand\",\n",
" # Either continue to rollout or finish\n",
" should_loop,\n",
")\n",
"\n",
"graph = builder.compile()"
]
},
{
"cell_type": "code",
@@ -300,7 +696,11 @@
"output_type": "execute_result"
}
],
"source": ["from IPython.display import Image\n\nImage(graph.get_graph().draw_mermaid_png())"]
"source": [
"from IPython.display import Image\n",
"\n",
"Image(graph.get_graph().draw_mermaid_png())"
]
},
{
"cell_type": "markdown",
@@ -329,7 +729,16 @@
]
}
],
"source": ["question = \"Generate a table with the average size and weight, as well as the oldest recorded instance for each of the top 5 most common birds.\"\nlast_step = None\nfor step in graph.stream({\"input\": question}):\n last_step = step\n step_name, step_state = next(iter(step.items()))\n print(step_name)\n print(\"rolled out: \", step_state[\"root\"].height)\n print(\"---\")"]
"source": [
"question = \"Generate a table with the average size and weight, as well as the oldest recorded instance for each of the top 5 most common birds.\"\n",
"last_step = None\n",
"for step in graph.stream({\"input\": question}):\n",
" last_step = step\n",
" step_name, step_state = next(iter(step.items()))\n",
" print(step_name)\n",
" print(\"rolled out: \", step_state[\"root\"].height)\n",
" print(\"---\")"
]
},
{
"cell_type": "code",
@@ -383,7 +792,11 @@
]
}
],
"source": ["solution_node = last_step[\"expand\"][\"root\"].get_best_solution()\nbest_trajectory = solution_node.get_trajectory(include_reflections=False)\nprint(best_trajectory[-1].content)"]
"source": [
"solution_node = last_step[\"expand\"][\"root\"].get_best_solution()\n",
"best_trajectory = solution_node.get_trajectory(include_reflections=False)\n",
"print(best_trajectory[-1].content)"
]
},
{
"cell_type": "code",
@@ -407,7 +820,16 @@
]
}
],
"source": ["question = \"Write out magnus carlson series of moves in his game against Alireza Firouzja and propose an alternate strategy\"\nlast_step = None\nfor step in graph.stream({\"input\": question}):\n last_step = step\n step_name, step_state = next(iter(step.items()))\n print(step_name)\n print(\"rolled out: \", step_state[\"root\"].height)\n print(\"---\")"]
"source": [
"question = \"Write out magnus carlson series of moves in his game against Alireza Firouzja and propose an alternate strategy\"\n",
"last_step = None\n",
"for step in graph.stream({\"input\": question}):\n",
" last_step = step\n",
" step_name, step_state = next(iter(step.items()))\n",
" print(step_name)\n",
" print(\"rolled out: \", step_state[\"root\"].height)\n",
" print(\"---\")"
]
},
{
"cell_type": "code",
@@ -470,7 +892,11 @@
]
}
],
"source": ["solution_node = last_step[\"expand\"][\"root\"].get_best_solution()\nbest_trajectory = solution_node.get_trajectory(include_reflections=False)\nprint(best_trajectory[-1].content)"]
"source": [
"solution_node = last_step[\"expand\"][\"root\"].get_best_solution()\n",
"best_trajectory = solution_node.get_trajectory(include_reflections=False)\n",
"print(best_trajectory[-1].content)"
]
},
{
"cell_type": "markdown",
+28 -16
View File
@@ -23,7 +23,9 @@
"This notebook walks through each component and shows how to wire them together using LangGraph. The end result will leave a trace [like the following](https://smith.langchain.com/public/218c2677-c719-4147-b0e9-7bc3b5bb2623/r).\n",
"\n",
"\n",
"**First,** install the dependencies, and set up LangSmith for tracing to more easily debug and observe the agent."
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -33,7 +35,8 @@
"metadata": {},
"outputs": [],
"source": [
"# %pip install -U --quiet langchain_openai langsmith langgraph langchain numexpr"
"%%capture --no-stderr\n",
"%pip install -U --quiet langchain_openai langsmith langgraph langchain numexpr"
]
},
{
@@ -52,19 +55,28 @@
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"# Optional: Debug + trace calls using LangSmith\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"True\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"LLMCompiler\"\n",
"_get_pass(\"LANGCHAIN_API_KEY\")\n",
"_get_pass(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "d499dad8",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "a61b48ee-8c6f-4863-913a-676f659287de",
"metadata": {},
"source": [
"## Part 1: Tools\n",
"## Define Tools\n",
"\n",
"We'll first define the tools for the agent to use in our demo. We'll give it the class search engine + calculator combo.\n",
"\n",
@@ -126,7 +138,7 @@
"id": "1abdedbd-d81b-4ee9-b46f-f29439ed1350",
"metadata": {},
"source": [
"# Part 2: Planner\n",
"## Planner\n",
"\n",
"\n",
"Largely adapted from [the original source code](https://github.com/SqueezeAILab/LLMCompiler/blob/main/src/llm_compiler/output_parser.py), the planner accepts the input question and generates a task list to execute.\n",
@@ -312,7 +324,7 @@
"id": "5d0e795f-61ff-4553-9823-23e7624ca180",
"metadata": {},
"source": [
"## 3. Task Fetching Unit\n",
"## Task Fetching Unit\n",
"\n",
"This component schedules the tasks. It receives a stream of tools of the following format:\n",
"\n",
@@ -534,7 +546,7 @@
"id": "9efa15ae-817a-48c6-86ed-16bc112fedc5",
"metadata": {},
"source": [
"#### Example Plan\n",
"### Example Plan\n",
"\n",
"We still haven't introduced any cycles in our computation graph, so this is all easily expressed in LCEL."
]
@@ -577,7 +589,7 @@
"id": "563d5311-55f0-4ca1-afbd-01fd970cf3e3",
"metadata": {},
"source": [
"## 4. \"Joiner\" \n",
"## Joiner\n",
"\n",
"So now we have the planning and initial execution done. We need a component to process these outputs and either:\n",
"\n",
@@ -706,7 +718,7 @@
"id": "b099e5ee-2c23-47d9-9387-0f64e02627d3",
"metadata": {},
"source": [
"## 5. Compose using LangGraph\n",
"## Compose using LangGraph\n",
"\n",
"We'll define the agent as a stateful graph, with the main nodes being:\n",
"\n",
@@ -767,7 +779,7 @@
"id": "9f8c9849-8531-463d-a0ef-dcc3d9888b2d",
"metadata": {},
"source": [
"#### Simple question\n",
"### Simple question\n",
"\n",
"Let's ask a simple question of the agent."
]
@@ -821,7 +833,7 @@
"id": "33c65ef5-b4b2-4ab2-8c78-a551da7819b9",
"metadata": {},
"source": [
"#### Multi-hop question\n",
"### Multi-hop question\n",
"\n",
"This question requires that the agent perform multiple searches."
]
@@ -884,7 +896,7 @@
"id": "1b859bc7-1a85-4d35-b57b-f67c87282403",
"metadata": {},
"source": [
"#### Multi-step math"
"### Multi-step math"
]
},
{
@@ -939,7 +951,7 @@
"id": "f9487866",
"metadata": {},
"source": [
"#### Complex Replanning Example\n",
"### Complex Replanning Example\n",
"\n",
"This question is likely to prompt the Replan functionality, but it may need to be run multiple times to see this in action."
]
+48 -1
View File
@@ -9,7 +9,54 @@
"\n",
"The subset of available tools to call is generally at the discretion of the model (although many providers also enable the user to [specify or constrain the choice of tool](https://python.langchain.com/v0.2/docs/how_to/tool_choice/)). As the number of available tools grows, you may want to limit the scope of the LLM's selection, to decrease token consumption and to help manage sources of error in LLM reasoning.\n",
"\n",
"Here we will demonstrate how to dynamically adjust the tools available to a model. Bottom line up front: like [RAG](https://python.langchain.com/v0.2/docs/concepts/#retrieval) and similar methods, we prefix the model invocation by retrieving over available tools. Although we demonstrate one implementation that searches over tool descriptions, the details of the tool selection can be customized as needed."
"Here we will demonstrate how to dynamically adjust the tools available to a model. Bottom line up front: like [RAG](https://python.langchain.com/v0.2/docs/concepts/#retrieval) and similar methods, we prefix the model invocation by retrieving over available tools. Although we demonstrate one implementation that searches over tool descriptions, the details of the tool selection can be customized as needed.\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9b6c62bd",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install --quiet -U langgraph langchain_openai"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "360d7ff6",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "25f9f6a0",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+41 -10
View File
@@ -23,6 +23,16 @@
"![Screenshot 2024-07-12 at 9.45.40 AM.png](attachment:a108ffc8-6136-4cd7-a6f9-579e41a5a786.png)"
]
},
{
"cell_type": "markdown",
"id": "66c58b5f",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
"cell_type": "code",
"execution_count": 1,
@@ -36,18 +46,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "dc292321",
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"ANTHROPIC_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import os\n",
"import getpass\n",
@@ -61,6 +63,27 @@
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "b87911bb",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "b4e782a0",
"metadata": {},
"source": [
"## Define the graph"
]
},
{
"cell_type": "code",
"execution_count": 3,
@@ -191,6 +214,14 @@
"Image(app.get_graph().draw_mermaid_png())"
]
},
{
"cell_type": "markdown",
"id": "4a0026d8",
"metadata": {},
"source": [
"## Use the graph"
]
},
{
"cell_type": "code",
"execution_count": 5,
@@ -70,18 +70,12 @@
"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": 3,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+6 -12
View File
@@ -77,18 +77,12 @@
"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": 3,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -68,18 +68,12 @@
"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": 3,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -364,9 +358,9 @@
],
"metadata": {
"kernelspec": {
"display_name": "langgraph-example-dev",
"display_name": "Python 3",
"language": "python",
"name": "langgraph-example-dev"
"name": "python3"
},
"language_info": {
"codemirror_mode": {
+49 -2
View File
@@ -10,7 +10,54 @@
"By default, state in a graph is scoped to that thread.\n",
"LangGraph also allows you to specify a \"scope\" for a given key/value pair that exists between threads. This can be useful for storing information that is shared between threads. For instance, you may want to store information about a user's preferences expressed in one thread, and then use that information in another thread.\n",
"\n",
"In this notebook we will go through an example of how to construct and use such a graph."
"In this notebook we will go through an example of how to construct and use such a graph.\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3457aadf",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langchain_openai langgraph"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aa2c64a7",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "51b6817d",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -26,7 +73,7 @@
"<div class=\"admonition note\">\n",
" <p class=\"admonition-title\">Typing shared state keys</p>\n",
" <p style=\"margin-top: 5px;\">\n",
" Shared state channels (keys) MUST be dictionaries (see `info` channel in the AgentState example below)\n",
" Shared state channels (keys) MUST be dictionaries (see <code>info</code> channel in the AgentState example below)\n",
" </p>\n",
"</div>"
]
+18 -8
View File
@@ -5,7 +5,7 @@
"id": "a3e3ebc4-57af-4fe4-bdd3-36aff67bf276",
"metadata": {},
"source": [
"## Agent Supervisor\n",
"# Agent Supervisor\n",
"\n",
"The [previous example](multi-agent-collaboration.ipynb) routed messages automatically based on the output of the initial researcher agent.\n",
"\n",
@@ -17,7 +17,9 @@
"\n",
"To simplify the code in each agent node, we will use the AgentExecutor class from LangChain. This and other \"advanced agent\" notebooks are designed to show how you can implement certain design patterns in LangGraph. If the pattern suits your needs, we recommend combining it with some of the other fundamental patterns described elsewhere in the docs for best performance.\n",
"\n",
"Before we build, let's configure our environment:"
"## Setup\n",
"\n",
"First, let's install required packages and set our API keys"
]
},
{
@@ -48,12 +50,20 @@
"\n",
"\n",
"_set_if_undefined(\"OPENAI_API_KEY\")\n",
"_set_if_undefined(\"LANGCHAIN_API_KEY\")\n",
"_set_if_undefined(\"TAVILY_API_KEY\")\n",
"\n",
"# Optional, add tracing in LangSmith\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Multi-agent Collaboration\""
"_set_if_undefined(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "be85e3ad",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -5,7 +5,7 @@
"id": "a3e3ebc4-57af-4fe4-bdd3-36aff67bf276",
"metadata": {},
"source": [
"## Hierarchical Agent Teams\n",
"# Hierarchical Agent Teams\n",
"\n",
"In our previous example ([Agent Supervisor](./agent_supervisor.ipynb)), we introduced the concept of a single supervisor node to route work between different worker nodes.\n",
"\n",
@@ -26,7 +26,9 @@
"3. Create and define each team (web research + doc writing)\n",
"4. Compose everything together.\n",
"\n",
"But before all of that, some setup:"
"## Setup\n",
"\n",
"First, let's install our required packages and set our API keys"
]
},
{
@@ -41,8 +43,8 @@
},
"outputs": [],
"source": [
"# %%capture --no-stderr\n",
"# %pip install -U langgraph langchain langchain_openai langchain_experimental"
"%% capture --no-stderr\n",
"%pip install -U langgraph langchain langchain_openai langchain_experimental"
]
},
{
@@ -67,13 +69,20 @@
"\n",
"\n",
"_set_if_undefined(\"OPENAI_API_KEY\")\n",
"_set_if_undefined(\"LANGCHAIN_API_KEY\")\n",
"_set_if_undefined(\"TAVILY_API_KEY\")\n",
"\n",
"# Optional, add tracing in LangSmith.\n",
"# This will help you visualize and debug the control flow\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Multi-agent Collaboration\""
"_set_if_undefined(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "04fdd0a3",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -17,7 +17,11 @@
"\n",
"![multi_agent diagram](./img/simple_multi_agent_diagram.png)\n",
"\n",
"Before we get started, a quick note: this and other multi-agent notebooks are designed to show _how_ you can implement certain design patterns in LangGraph. If the pattern suits your needs, we recommend combining it with some of the other fundamental patterns described elsewhere in the docs for best performance."
"Before we get started, a quick note: this and other multi-agent notebooks are designed to show _how_ you can implement certain design patterns in LangGraph. If the pattern suits your needs, we recommend combining it with some of the other fundamental patterns described elsewhere in the docs for best performance.\n",
"\n",
"## Setup\n",
"\n",
"First, let's install our required packages and set our API keys:"
]
},
{
@@ -48,12 +52,20 @@
"\n",
"\n",
"_set_if_undefined(\"OPENAI_API_KEY\")\n",
"_set_if_undefined(\"LANGCHAIN_API_KEY\")\n",
"_set_if_undefined(\"TAVILY_API_KEY\")\n",
"\n",
"# Optional, add tracing in LangSmith\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Multi-agent Collaboration\""
"_set_if_undefined(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "ab5cea6d",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+49
View File
@@ -8,6 +8,55 @@
"\n",
"There are many use cases where you may wish for your node to have a custom retry policy, for example if you are calling an API, querying a database, or calling an LLM, etc. \n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain_anthropic langchain_community"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to configure the retry policy, you have to pass the `retry` parameter to the `add_node` function. The `retry` parameter takes in a `RetryPolicy` named tuple object. Below we instantiate a `RetryPolicy` object with the default parameters:"
]
},
+17 -11
View File
@@ -26,7 +26,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -41,17 +43,9 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"ANTHROPIC_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -65,6 +59,18 @@
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
+6 -14
View File
@@ -63,20 +63,12 @@
"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:\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+37 -1
View File
@@ -17,7 +17,43 @@
"\n",
"We will have a separate node for each step. We will only have the `question` and `answer` on the overall state. However, we will need separate states for the `search_query` and the `documents` - we will pass these as private state keys.\n",
"\n",
"Let's look at an example!"
"Let's look at an example!\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "32d79ebd",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"id": "e30836ce",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "a0028ced",
"metadata": {},
"source": [
"## Define and use the graph"
]
},
{
+14 -18
View File
@@ -97,18 +97,12 @@
"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": 22,
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -116,7 +110,7 @@
"id": "4cf509bc",
"metadata": {},
"source": [
"## Set up the State\n",
"## Define graph state\n",
"\n",
"The state is the interface for all the nodes."
]
@@ -149,7 +143,7 @@
"id": "21ac643b-cb06-4724-a80c-2862ba4773f1",
"metadata": {},
"source": [
"## Set up the tools\n",
"## Define tools\n",
"\n",
"We will first define the tools we want to use.\n",
"For this simple example, we will use create a placeholder search engine.\n",
@@ -202,7 +196,7 @@
"id": "5497ed70-fce3-47f1-9cad-46f912bad6a5",
"metadata": {},
"source": [
"## Set up the model\n",
"## Define the model\n",
"\n",
"Now we need to load the [chat model](https://python.langchain.com/v0.2/docs/concepts/#chat-models) to power our agent.\n",
"For the design below, it must satisfy two criteria:\n",
@@ -258,7 +252,7 @@
"id": "e03c5094-9297-4d19-a04e-3eedc75cefb4",
"metadata": {},
"source": [
"## Define the graph \n",
"## Define nodes and edges \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",
@@ -313,6 +307,8 @@
"id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b",
"metadata": {},
"source": [
"## Compile the graph\n",
"\n",
"We can now put it all together and define the graph!"
]
},
@@ -355,7 +351,7 @@
"id": "bc9c8536-f90b-44fa-958d-5df016c66d8f",
"metadata": {},
"source": [
"**Persistence**\n",
"### Persistence\n",
"\n",
"To add in persistence, we pass in a checkpoint when compiling the graph"
]
@@ -430,7 +426,7 @@
"id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159",
"metadata": {},
"source": [
"## Interacting with the Agent\n",
"## Use the graph\n",
"\n",
"We can now interact with the agent and see that it remembers previous messages!\n"
]
+18 -11
View File
@@ -19,7 +19,9 @@
"id": "456fa19c-93a5-4750-a410-f2d810b964ad",
"metadata": {},
"source": [
"## Setup environment"
"## Setup\n",
"\n",
"First let's install the required packages and set our API keys"
]
},
{
@@ -35,18 +37,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "eca9aafb-a155-407a-8036-682a2f1297d7",
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -60,6 +54,19 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "3080e508",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "ecb23436-f238-4f8c-a2b7-67c7956121e2",
+19 -12
View File
@@ -23,7 +23,9 @@
"id": "456fa19c-93a5-4750-a410-f2d810b964ad",
"metadata": {},
"source": [
"## Setup environment"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -39,18 +41,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "eca9aafb-a155-407a-8036-682a2f1297d7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -64,12 +58,25 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "b394e26c",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "e26b3204-cca2-414c-800e-7e09032445ae",
"metadata": {},
"source": [
"## Setup model and tools for the graph"
"## Define model and tools for the graph"
]
},
{
+19 -12
View File
@@ -19,7 +19,9 @@
"id": "456fa19c-93a5-4750-a410-f2d810b964ad",
"metadata": {},
"source": [
"## Setup environment"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -30,23 +32,15 @@
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U redis langgraph"
"%pip install -U redis langgraph langchain_openai"
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "eca9aafb-a155-407a-8036-682a2f1297d7",
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -60,6 +54,19 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "49c80b63",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "ecb23436-f238-4f8c-a2b7-67c7956121e2",
@@ -83,19 +83,12 @@
"id": "be2d7981-3737-4134-8bef-d00d18d4e91d",
"metadata": {},
"source": [
"Optionally, we can set API key for LangSmith tracing, which will give us best-in-class observability."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "01f460d1-f26f-47d1-ae76-de74d5d851de",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Plan-and-execute\""
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+33 -27
View File
@@ -37,7 +37,9 @@
"id": "a85501ca-eb89-4795-aeab-cdab050ead6b",
"metadata": {},
"source": [
"# Environment "
"## Setup\n",
"\n",
"First, let's install our required packages and set our API keys"
]
},
{
@@ -58,35 +60,31 @@
"metadata": {},
"outputs": [],
"source": [
"### LLMs\n",
"import getpass\n",
"import os\n",
"\n",
"os.environ[\"OPENAI_API_KEY\"] = \"<your-api-key>\"\n",
"os.environ[\"COHERE_API_KEY\"] = \"<your-api-key>\"\n",
"os.environ[\"TAVILY_API_KEY\"] = \"<your-api-key>\""
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"_set_env(\"COHERE_API_KEY\")\n",
"_set_env(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "018c9e9f-8199-4f33-b5b4-7adfb66d6219",
"id": "47e04b18",
"metadata": {},
"source": [
"### Tracing\n",
"\n",
"* Optionally, use [LangSmith](https://docs.smith.langchain.com/) for tracing (shown at bottom) by setting: "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "08edba00-988a-478b-96fc-ae0199cbef49",
"metadata": {},
"outputs": [],
"source": [
"### Tracing (optional)\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -94,7 +92,7 @@
"id": "9ac1c2cd-81fb-40eb-8ba1-e9197800cba6",
"metadata": {},
"source": [
"## Index"
"## Create Index"
]
},
{
@@ -462,11 +460,11 @@
"id": "efbbff0e-8843-45bb-b2ff-137bef707ef4",
"metadata": {},
"source": [
"# Graph \n",
"## Construct the Graph \n",
"\n",
"Capture the flow in as a graph.\n",
"\n",
"## Graph state"
"### Define Graph State"
]
},
{
@@ -501,7 +499,7 @@
"id": "7e2d6c0d-42e8-4399-9751-e315be16607a",
"metadata": {},
"source": [
"## Graph Flow "
"### Define Graph Flow "
]
},
{
@@ -721,7 +719,7 @@
"id": "3ab01f36-5628-49ab-bfd3-84bb6f1a1b0f",
"metadata": {},
"source": [
"## Build Graph"
"### Compile Graph"
]
},
{
@@ -776,6 +774,14 @@
"app = workflow.compile()"
]
},
{
"cell_type": "markdown",
"id": "85bce541",
"metadata": {},
"source": [
"## Use Graph"
]
},
{
"cell_type": "code",
"execution_count": 17,
+37 -26
View File
@@ -35,7 +35,9 @@
"id": "8cece98f-a3ed-417e-8b6a-1754e8f9c42a",
"metadata": {},
"source": [
"# Environment "
"## Setup\n",
"\n",
"First, let's install our required packages and set our API keys"
]
},
{
@@ -49,6 +51,39 @@
"%pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph tavily-python nomic[local]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2369652a",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"TAVILY_API_KEY\")\n",
"_set_env(\"NOMIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "aea269f6",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "6a5d4a26-249b-4551-aa13-6c373429618e",
@@ -84,36 +119,12 @@
"local_llm = \"mistral\""
]
},
{
"cell_type": "markdown",
"id": "5c104495-a8a4-4517-b6a6-9a2cbe1e82f2",
"metadata": {},
"source": [
"### Tracing\n",
"\n",
"Optionally, use [LangSmith](https://docs.smith.langchain.com/) for tracing (shown at bottom)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f6cb8dce-f580-421d-a05f-9fc71de2b023",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
]
},
{
"cell_type": "markdown",
"id": "04718a0c-7a48-4243-97a2-940a0239cc12",
"metadata": {},
"source": [
"## Index"
"## Create Index"
]
},
{
+20 -7
View File
@@ -11,7 +11,11 @@
"\n",
"To implement a retrieval agent, we simple need to give an LLM access to a retriever tool.\n",
"\n",
"We can incorporate this into [LangGraph](https://langchain-ai.github.io/langgraph/)."
"We can incorporate this into [LangGraph](https://langchain-ai.github.io/langgraph/).\n",
"\n",
"## Setup\n",
"\n",
"First, let's download the required packages and set our API keys:"
]
},
{
@@ -41,11 +45,20 @@
" os.environ[key] = getpass.getpass(f\"{key}:\")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"\n",
"# (Optional) For tracing\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "3d07e8d4",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -124,7 +137,7 @@
"id": "fe6e8f78-1ef7-42ad-b2bf-835ed5850553",
"metadata": {},
"source": [
"## Agent state\n",
"## Agent State\n",
" \n",
"We will define a graph.\n",
"\n",
+32 -51
View File
@@ -38,7 +38,9 @@
"id": "4931ac25-99f9-4f04-b3d1-4683f7853667",
"metadata": {},
"source": [
"# Environment "
"## Setup\n",
"\n",
"First, let's download our required packages and set our API keys"
]
},
{
@@ -51,14 +53,6 @@
"! pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph tavily-python"
]
},
{
"cell_type": "markdown",
"id": "c9ca92ae-c84f-423e-bab5-2a8bd06bd4cf",
"metadata": {},
"source": [
"### LLMs"
]
},
{
"cell_type": "code",
"execution_count": null,
@@ -66,51 +60,30 @@
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"os.environ[\"OPENAI_API_KEY\"] = \"<your-api-key>\""
]
},
{
"cell_type": "markdown",
"id": "db34bf9b-fa1e-45d3-a83e-4f5eaf897bad",
"metadata": {},
"source": [
"### Search\n",
" \n",
"We'll use [Tavily Search](https://python.langchain.com/docs/integrations/tools/tavily_search) for web search."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c3ac6e65-2d4e-48dd-9fff-40047373332d",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"TAVILY_API_KEY\"] = \"<your-api-key>\""
]
},
{
"cell_type": "markdown",
"id": "20d2644a-043e-419c-9ff8-45118b8e9f17",
"metadata": {},
"source": [
"### Tracing\n",
"\n",
"Optionally, use [LangSmith](https://docs.smith.langchain.com/) for tracing (shown at bottom) by setting"
"def _set_env(key: str):\n",
" if key not in os.environ:\n",
" os.environ[key] = getpass.getpass(f\"{key}:\")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"_set_env(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e205f57e-5218-478b-ad8e-1723bdb0d45e",
"cell_type": "markdown",
"id": "3adde047",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -118,7 +91,7 @@
"id": "a21f32d2-92ce-4995-b309-99347bafe3be",
"metadata": {},
"source": [
"## Index\n",
"## Create Index\n",
" \n",
"Let's index 3 blog posts."
]
@@ -326,11 +299,11 @@
"id": "87194a1b-535a-4593-ab95-5736fae176d1",
"metadata": {},
"source": [
"# Graph \n",
"## Create Graph \n",
"\n",
"Capture the flow in as a graph.\n",
"Now let's create our graph that will use CRAG\n",
"\n",
"## Graph state"
"### Define Graph State"
]
},
{
@@ -523,7 +496,7 @@
"id": "fa076e90-7132-4fcf-8507-db5990314c4f",
"metadata": {},
"source": [
"## Build Graph\n",
"### Compile Graph\n",
"\n",
"The just follows the flow we outlined in the figure above."
]
@@ -565,6 +538,14 @@
"app = workflow.compile()"
]
},
{
"cell_type": "markdown",
"id": "27ba16a8",
"metadata": {},
"source": [
"## Use the graph"
]
},
{
"cell_type": "code",
"execution_count": 42,
+23 -27
View File
@@ -36,7 +36,7 @@
"id": "6ba4302f-09d9-4d2a-a18d-a6fd23704850",
"metadata": {},
"source": [
"### Environment\n",
"## Setup\n",
"\n",
"We'll use [Ollama](https://ollama.ai/) to access a local LLM:\n",
"\n",
@@ -47,7 +47,8 @@
"\n",
"We'll use a vectorstore with [Nomic local embeddings](https://blog.nomic.ai/posts/nomic-embed-text-v1) or, optionally, OpenAI embeddings.\n",
"\n",
"We'll use [LangSmith](https://docs.smith.langchain.com/) for tracing and evaluation."
"\n",
"Let's install our required packages and set our API keys:"
]
},
{
@@ -68,35 +69,30 @@
"metadata": {},
"outputs": [],
"source": [
"# Search\n",
"import getpass\n",
"import os\n",
"\n",
"os.environ[\"TAVILY_API_KEY\"] = \"xxx\""
"\n",
"def _set_env(key: str):\n",
" if key not in os.environ:\n",
" os.environ[key] = getpass.getpass(f\"{key}:\")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")\n",
"_set_env(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0be68860-dded-481e-9fc7-a5042bf92c04",
"cell_type": "markdown",
"id": "98f863ea",
"metadata": {},
"outputs": [],
"source": [
"# Embedding (optional)\n",
"os.environ[\"OPENAI_API_KEY\"] = \"xxx\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7248ab88-2b97-41eb-8dbb-4ea65525ed9a",
"metadata": {},
"outputs": [],
"source": [
"# Tracing and testing (optional)\n",
"os.environ[\"LANGCHAIN_API_KEY\"] = \"xxx\"\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"corrective-rag-agent-testing\""
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -126,7 +122,7 @@
"id": "6e2b6eed-3b3f-44b5-a34a-4ade1e94caf0",
"metadata": {},
"source": [
"### Index\n",
"## Create Index\n",
"\n",
"Let's index 3 blog posts."
]
@@ -194,7 +190,7 @@
"id": "fe7fd10a-f64a-48de-a116-6d5890def1af",
"metadata": {},
"source": [
"### Tools"
"## Define Tools"
]
},
{
@@ -318,7 +314,7 @@
"id": "a3421cf0-9067-43fe-8681-0d3189d15dd3",
"metadata": {},
"source": [
"### Graph \n",
"## Create the Graph \n",
"\n",
"Here we'll explicitly define the majority of the control flow, only using an LLM to define a single branch point following grading."
]
+19 -27
View File
@@ -50,7 +50,9 @@
"id": "72f3ee57-68ab-4040-bd36-4014e2a23d96",
"metadata": {},
"source": [
"# Environment "
"## Setup\n",
"\n",
"First let's install our required packages and set our API keys"
]
},
{
@@ -63,46 +65,36 @@
"! pip install -U langchain_community tiktoken langchain-openai langchainhub chromadb langchain langgraph"
]
},
{
"cell_type": "markdown",
"id": "15569b93-3c68-4aac-838c-37112d33987a",
"metadata": {},
"source": [
"### LLMs"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f18b63c7-d0d3-41c1-ae6b-5a0f1b8ccf0f",
"id": "de4ee2a5",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"os.environ[\"OPENAI_API_KEY\"] = \"<your-api-key>\""
"\n",
"def _set_env(key: str):\n",
" if key not in os.environ:\n",
" os.environ[key] = getpass.getpass(f\"{key}:\")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "532d91fb-381e-4e11-b3b1-254321351773",
"id": "25d16369",
"metadata": {},
"source": [
"### Tracing\n",
"\n",
"Optionally, use [LangSmith](https://docs.smith.langchain.com/) for tracing (shown at bottom)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ccc3dae5-1df6-48ca-af8a-50f0e6128876",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+37 -27
View File
@@ -10,7 +10,7 @@
"id": "848ba742-7443-4123-8115-061da9823309",
"metadata": {},
"source": [
"# Self RAG using local LLMs\n",
"# Self-RAG using local LLMs\n",
"\n",
"Self-RAG is a strategy for RAG that incorporates self-reflection / self-grading on retrieved documents and generations. \n",
"\n",
@@ -50,7 +50,9 @@
"id": "9ed0a85a-a33b-40a6-99fa-2444bf57a6cc",
"metadata": {},
"source": [
"# Environment "
"## Setup\n",
"\n",
"First let's install our required packages and set our API keys"
]
},
{
@@ -64,6 +66,38 @@
"%pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph nomic[local]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "71c540ca",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(key: str):\n",
" if key not in os.environ:\n",
" os.environ[key] = getpass.getpass(f\"{key}:\")\n",
"\n",
"\n",
"_set_env(\"NOMIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "05e8cf60",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "ccc6b6bd-a2fa-4a43-83b7-704b8b6fb855",
@@ -99,36 +133,12 @@
"local_llm = \"mistral\""
]
},
{
"cell_type": "markdown",
"id": "6306aa61-2e6a-47b6-b306-4987d0636f97",
"metadata": {},
"source": [
"### Tracing\n",
"\n",
"Optionally, use [LangSmith](https://docs.smith.langchain.com/) for tracing (shown at bottom)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2208f342-8163-4af3-8dc0-aa70f5e06143",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_ENDPOINT\"] = \"https://api.smith.langchain.com\"\n",
"os.environ[\"LANGCHAIN_API_KEY\"] = \"<your-api-key>\""
]
},
{
"cell_type": "markdown",
"id": "ba68a46d-b617-4fdc-9113-fabdcf736feb",
"metadata": {},
"source": [
"## Index\n",
"## Create Index\n",
"\n",
"Let's index 3 blog posts."
]
+50 -1
View File
@@ -49,7 +49,56 @@
"\n",
"## Setup\n",
"\n",
"For our setup we need to define how we want to structure our output, define our graph state, and also our tools and the models we are going to use.\n",
"First, let's install the required packages and set our API keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain_anthropic"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define model, tools, and graph state\n",
"\n",
"Now we can define how we want to structure our output, define our graph state, and also our tools and the models we are going to use.\n",
"\n",
"To use structured output, we will use the `with_structured_output` method from LangChain, which you can read more about [here](https://python.langchain.com/v0.2/docs/how_to/structured_output/).\n",
"\n",
+35 -2
View File
@@ -8,7 +8,38 @@
"\n",
"You can set the graph recursion limit when invoking or streaming the graph. The recursion limit sets the number of supersteps that the graph is allowed to execute before it raises an error. Read more about the concept of recursion limits [here](https://langchain-ai.github.io/langgraph/concepts/low_level/#recursion-limit). Let's see an example of this in a simple graph with parallel branches to better understand exactly how the recursion limit works.\n",
"\n",
"## Define our graph"
"## Setup\n",
"\n",
"First, let's install the required packages"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define the graph"
]
},
{
@@ -79,7 +110,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"As we can see, our graph will execute nodes `b` and `c` in parallel (i.e. in a single super-step), which means that if we run this graph it should take exactly 3 steps. We can set the recursion limit to 3 first to check that it raises an error (the recursion limit is inclusive, so if the limit is 3 the graph will raise an error when it reaches step 3) as expected: "
"As we can see, our graph will execute nodes `b` and `c` in parallel (i.e. in a single super-step), which means that if we run this graph it should take exactly 3 steps. We can set the recursion limit to 3 first to check that it raises an error (the recursion limit is inclusive, so if the limit is 3 the graph will raise an error when it reaches step 3) as expected: \n",
"\n",
"## Use the graph"
]
},
{
+18 -19
View File
@@ -21,9 +21,9 @@
"id": "3ef94e7e-c9a5-4eee-a865-acf411b5c235",
"metadata": {},
"source": [
"#### Prerequisites\n",
"## Setup\n",
"\n",
"We will be using a basic agent with a search tool here."
"First, let's install our required packages and set our API keys"
]
},
{
@@ -39,19 +39,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "3368f330-cad6-4d35-a291-68fbf4389d98",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"LANGCHAIN_API_KEY ········\n",
"FIREWORKS_API_KEY ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -62,15 +53,23 @@
" return\n",
" os.environ[var] = getpass.getpass(var)\n",
"\n",
"\n",
"# Optional: Configure tracing to visualize and debug the agent\n",
"_set_if_undefined(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Reflection\"\n",
"\n",
"_set_if_undefined(\"TAVILY_API_KEY\")\n",
"_set_if_undefined(\"FIREWORKS_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "9182b7d5",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "f27bcc4a-aaa5-46bd-8163-3e0e90cb66e6",
+17 -8
View File
@@ -27,7 +27,7 @@
"id": "906edf48-7c81-48b8-8250-fdc34043d01b",
"metadata": {},
"source": [
"## 0. Prerequisites\n",
"## Setup\n",
"\n",
"Install `langgraph` (for the framework), `langchain_openai` (for the LLM), and `langchain` + `tavily-python` (for the search engine).\n",
"\n",
@@ -61,16 +61,25 @@
" return\n",
" os.environ[var] = getpass.getpass(var)\n",
"\n",
"\n",
"# Optional: Configure tracing to visualize and debug the agent\n",
"_set_if_undefined(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Reflexion\"\n",
"\n",
"_set_if_undefined(\"ANTHROPIC_API_KEY\")\n",
"_set_if_undefined(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "8a1b13a6",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> \n",
"\n",
"### Define our LLM"
]
},
{
"cell_type": "code",
"execution_count": 2,
@@ -92,7 +101,7 @@
"id": "af543598-52d0-4ec3-a05f-d2954ff793ee",
"metadata": {},
"source": [
"## 1. Actor (with reflection)\n",
"## Actor (with reflection)\n",
"\n",
"The main component of Reflexion is the \"actor\", which is an agent that reflects on its response and re-executes to improve based on self-critique. It's main sub-components include:\n",
"1. Tools/tool execution\n",
+198 -23
View File
@@ -34,11 +34,11 @@
"\n",
"In this example, each module is represented by a LangGraph node. The end result will leave a trace that looks [like this one](https://smith.langchain.com/public/39dbdcf8-fbcc-4479-8e28-15377ca5e653/r). Let's get started!\n",
"\n",
"## 0. Prerequisites\n",
"## Setup\n",
"\n",
"For this example, we will provide the agent with a Tavily search engine tool. You can get an API key [here](https://app.tavily.com/sign-in) or replace with a free tool option (e.g., [duck duck go search](https://python.langchain.com/v0.2/docs/integrations/tools/ddg/)).\n",
"\n",
"To see the full langsmith trace, you can s"
"Let's install the required packages and set our API keys"
]
},
{
@@ -47,7 +47,10 @@
"id": "7f52bded-9d23-4826-8bfc-20b0d3a51182",
"metadata": {},
"outputs": [],
"source": ["# %pip install -U langgraph langchain_community langchain_openai tavily-python"]
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain_community langchain_openai tavily-python"
]
},
{
"cell_type": "code",
@@ -55,14 +58,41 @@
"id": "4215f9fb-71ff-4d88-8484-f73174db5592",
"metadata": {},
"outputs": [],
"source": ["import getpass\nimport os\n\n\ndef _set_if_undefined(var: str):\n if not os.environ.get(var):\n os.environ[var] = getpass.getpass(f\"{var}=\")\n\n\nos.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_PROJECT\"] = \"ReWOO\"\n_set_if_undefined(\"TAVILY_API_KEY\")\n_set_if_undefined(\"LANGCHAIN_API_KEY\")\n_set_if_undefined(\"OPENAI_API_KEY\")"]
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_if_undefined(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}=\")\n",
"\n",
"\n",
"_set_if_undefined(\"TAVILY_API_KEY\")\n",
"_set_if_undefined(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "2eba7932",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "55239a14-a14d-4117-adb5-07199e1e5e16",
"metadata": {},
"source": [
"**Graph State**: In LangGraph, every node updates a shared graph state. The state is the input to any node whenever it is invoked.\n",
"## Define graph state\n",
"\n",
"In LangGraph, every node updates a shared graph state. The state is the input to any node whenever it is invoked.\n",
"\n",
"Below, we will define a state dict to contain the task, plan, steps, and other variables."
]
@@ -73,14 +103,24 @@
"id": "9a92c875-c20b-4b7e-9d88-61c62382f8e2",
"metadata": {},
"outputs": [],
"source": ["from typing import List, TypedDict\n\n\nclass ReWOO(TypedDict):\n task: str\n plan_string: str\n steps: List\n results: dict\n result: str"]
"source": [
"from typing import List, TypedDict\n",
"\n",
"\n",
"class ReWOO(TypedDict):\n",
" task: str\n",
" plan_string: str\n",
" steps: List\n",
" results: dict\n",
" result: str"
]
},
{
"cell_type": "markdown",
"id": "997f9181-41c0-4c44-937d-94bd3946a929",
"metadata": {},
"source": [
"## 1. Planner\n",
"## Planner\n",
"\n",
"The planner prompts an LLM to generate a plan in the form of a task list. The arguments to each task are strings that may contain special variables (`#E{0-9}+`) that are used for variable substitution from other task results.\n",
"\n",
@@ -100,7 +140,11 @@
"id": "c8836921-c89e-42b6-8c71-27aeaeac5368",
"metadata": {},
"outputs": [],
"source": ["from langchain_openai import ChatOpenAI\n\nmodel = ChatOpenAI(temperature=0)"]
"source": [
"from langchain_openai import ChatOpenAI\n",
"\n",
"model = ChatOpenAI(temperature=0)"
]
},
{
"cell_type": "code",
@@ -108,7 +152,32 @@
"id": "7e7faa92-30a1-4942-b3c7-acd3a7bfccbc",
"metadata": {},
"outputs": [],
"source": ["prompt = \"\"\"For the following task, make plans that can solve the problem step by step. For each plan, indicate \\\nwhich external tool together with tool input to retrieve evidence. You can store the evidence into a \\\nvariable #E that can be called by later tools. (Plan, #E1, Plan, #E2, Plan, ...)\n\nTools can be one of the following:\n(1) Google[input]: Worker that searches results from Google. Useful when you need to find short\nand succinct answers about a specific topic. The input should be a search query.\n(2) LLM[input]: A pretrained LLM like yourself. Useful when you need to act with general\nworld knowledge and common sense. Prioritize it when you are confident in solving the problem\nyourself. Input can be any instruction.\n\nFor example,\nTask: Thomas, Toby, and Rebecca worked a total of 157 hours in one week. Thomas worked x\nhours. Toby worked 10 hours less than twice what Thomas worked, and Rebecca worked 8 hours\nless than Toby. How many hours did Rebecca work?\nPlan: Given Thomas worked x hours, translate the problem into algebraic expressions and solve\nwith Wolfram Alpha. #E1 = WolframAlpha[Solve x + (2x 10) + ((2x 10) 8) = 157]\nPlan: Find out the number of hours Thomas worked. #E2 = LLM[What is x, given #E1]\nPlan: Calculate the number of hours Rebecca worked. #E3 = Calculator[(2 #E2 10) 8]\n\nBegin! \nDescribe your plans with rich details. Each Plan should be followed by only one #E.\n\nTask: {task}\"\"\""]
"source": [
"prompt = \"\"\"For the following task, make plans that can solve the problem step by step. For each plan, indicate \\\n",
"which external tool together with tool input to retrieve evidence. You can store the evidence into a \\\n",
"variable #E that can be called by later tools. (Plan, #E1, Plan, #E2, Plan, ...)\n",
"\n",
"Tools can be one of the following:\n",
"(1) Google[input]: Worker that searches results from Google. Useful when you need to find short\n",
"and succinct answers about a specific topic. The input should be a search query.\n",
"(2) LLM[input]: A pretrained LLM like yourself. Useful when you need to act with general\n",
"world knowledge and common sense. Prioritize it when you are confident in solving the problem\n",
"yourself. Input can be any instruction.\n",
"\n",
"For example,\n",
"Task: Thomas, Toby, and Rebecca worked a total of 157 hours in one week. Thomas worked x\n",
"hours. Toby worked 10 hours less than twice what Thomas worked, and Rebecca worked 8 hours\n",
"less than Toby. How many hours did Rebecca work?\n",
"Plan: Given Thomas worked x hours, translate the problem into algebraic expressions and solve\n",
"with Wolfram Alpha. #E1 = WolframAlpha[Solve x + (2x 10) + ((2x 10) 8) = 157]\n",
"Plan: Find out the number of hours Thomas worked. #E2 = LLM[What is x, given #E1]\n",
"Plan: Calculate the number of hours Rebecca worked. #E3 = Calculator[(2 #E2 10) 8]\n",
"\n",
"Begin! \n",
"Describe your plans with rich details. Each Plan should be followed by only one #E.\n",
"\n",
"Task: {task}\"\"\""
]
},
{
"cell_type": "code",
@@ -116,7 +185,9 @@
"id": "72b4ab0f-7215-4f4b-9407-0ebad8b13b92",
"metadata": {},
"outputs": [],
"source": ["task = \"what is the hometown of the 2024 australian open winner\""]
"source": [
"task = \"what is the hometown of the 2024 australian open winner\""
]
},
{
"cell_type": "code",
@@ -124,7 +195,9 @@
"id": "56ecb45b-ea76-4303-a4f3-51406fe8312a",
"metadata": {},
"outputs": [],
"source": ["result = model.invoke(prompt.format(task=task))"]
"source": [
"result = model.invoke(prompt.format(task=task))"
]
},
{
"cell_type": "code",
@@ -150,7 +223,9 @@
]
}
],
"source": ["print(result.content)"]
"source": [
"print(result.content)"
]
},
{
"cell_type": "markdown",
@@ -169,14 +244,31 @@
"id": "f9f042b6-90d8-430f-abf3-04ad2bb047c7",
"metadata": {},
"outputs": [],
"source": ["import re\n\nfrom langchain_core.prompts import ChatPromptTemplate\n\n# Regex to match expressions of the form E#... = ...[...]\nregex_pattern = r\"Plan:\\s*(.+)\\s*(#E\\d+)\\s*=\\s*(\\w+)\\s*\\[([^\\]]+)\\]\"\nprompt_template = ChatPromptTemplate.from_messages([(\"user\", prompt)])\nplanner = prompt_template | model\n\n\ndef get_plan(state: ReWOO):\n task = state[\"task\"]\n result = planner.invoke({\"task\": task})\n # Find all matches in the sample text\n matches = re.findall(regex_pattern, result.content)\n return {\"steps\": matches, \"plan_string\": result.content}"]
"source": [
"import re\n",
"\n",
"from langchain_core.prompts import ChatPromptTemplate\n",
"\n",
"# Regex to match expressions of the form E#... = ...[...]\n",
"regex_pattern = r\"Plan:\\s*(.+)\\s*(#E\\d+)\\s*=\\s*(\\w+)\\s*\\[([^\\]]+)\\]\"\n",
"prompt_template = ChatPromptTemplate.from_messages([(\"user\", prompt)])\n",
"planner = prompt_template | model\n",
"\n",
"\n",
"def get_plan(state: ReWOO):\n",
" task = state[\"task\"]\n",
" result = planner.invoke({\"task\": task})\n",
" # Find all matches in the sample text\n",
" matches = re.findall(regex_pattern, result.content)\n",
" return {\"steps\": matches, \"plan_string\": result.content}"
]
},
{
"cell_type": "markdown",
"id": "0d97942f-27d3-4761-b6cc-6614dbb90c77",
"metadata": {},
"source": [
"## 2. Executor\n",
"## Executor\n",
"\n",
"The executor receives the plan and executes the tools in sequence.\n",
"\n",
@@ -189,7 +281,11 @@
"id": "3412cfc4-6796-4295-aea4-7eeb304e10bd",
"metadata": {},
"outputs": [],
"source": ["from langchain_community.tools.tavily_search import TavilySearchResults\n\nsearch = TavilySearchResults()"]
"source": [
"from langchain_community.tools.tavily_search import TavilySearchResults\n",
"\n",
"search = TavilySearchResults()"
]
},
{
"cell_type": "code",
@@ -197,14 +293,39 @@
"id": "aa96fbac-28bc-4afe-ae35-ddb3383d1147",
"metadata": {},
"outputs": [],
"source": ["def _get_current_task(state: ReWOO):\n if state[\"results\"] is None:\n return 1\n if len(state[\"results\"]) == len(state[\"steps\"]):\n return None\n else:\n return len(state[\"results\"]) + 1\n\n\ndef tool_execution(state: ReWOO):\n \"\"\"Worker node that executes the tools of a given plan.\"\"\"\n _step = _get_current_task(state)\n _, step_name, tool, tool_input = state[\"steps\"][_step - 1]\n _results = state[\"results\"] or {}\n for k, v in _results.items():\n tool_input = tool_input.replace(k, v)\n if tool == \"Google\":\n result = search.invoke(tool_input)\n elif tool == \"LLM\":\n result = model.invoke(tool_input)\n else:\n raise ValueError\n _results[step_name] = str(result)\n return {\"results\": _results}"]
"source": [
"def _get_current_task(state: ReWOO):\n",
" if state[\"results\"] is None:\n",
" return 1\n",
" if len(state[\"results\"]) == len(state[\"steps\"]):\n",
" return None\n",
" else:\n",
" return len(state[\"results\"]) + 1\n",
"\n",
"\n",
"def tool_execution(state: ReWOO):\n",
" \"\"\"Worker node that executes the tools of a given plan.\"\"\"\n",
" _step = _get_current_task(state)\n",
" _, step_name, tool, tool_input = state[\"steps\"][_step - 1]\n",
" _results = state[\"results\"] or {}\n",
" for k, v in _results.items():\n",
" tool_input = tool_input.replace(k, v)\n",
" if tool == \"Google\":\n",
" result = search.invoke(tool_input)\n",
" elif tool == \"LLM\":\n",
" result = model.invoke(tool_input)\n",
" else:\n",
" raise ValueError\n",
" _results[step_name] = str(result)\n",
" return {\"results\": _results}"
]
},
{
"cell_type": "markdown",
"id": "28e20b31-d721-470d-94d2-db0c177fae75",
"metadata": {},
"source": [
"## 3. Solver\n",
"## Solver\n",
"\n",
"The solver receives the full plan and generates the final response based on the responses of the tool calls from the worker."
]
@@ -215,14 +336,39 @@
"id": "0a4d9851-8590-42be-8c53-9969ebff85f4",
"metadata": {},
"outputs": [],
"source": ["solve_prompt = \"\"\"Solve the following task or problem. To solve the problem, we have made step-by-step Plan and \\\nretrieved corresponding Evidence to each Plan. Use them with caution since long evidence might \\\ncontain irrelevant information.\n\n{plan}\n\nNow solve the question or task according to provided Evidence above. Respond with the answer\ndirectly with no extra words.\n\nTask: {task}\nResponse:\"\"\"\n\n\ndef solve(state: ReWOO):\n plan = \"\"\n for _plan, step_name, tool, tool_input in state[\"steps\"]:\n _results = state[\"results\"] or {}\n for k, v in _results.items():\n tool_input = tool_input.replace(k, v)\n step_name = step_name.replace(k, v)\n plan += f\"Plan: {_plan}\\n{step_name} = {tool}[{tool_input}]\"\n prompt = solve_prompt.format(plan=plan, task=state[\"task\"])\n result = model.invoke(prompt)\n return {\"result\": result.content}"]
"source": [
"solve_prompt = \"\"\"Solve the following task or problem. To solve the problem, we have made step-by-step Plan and \\\n",
"retrieved corresponding Evidence to each Plan. Use them with caution since long evidence might \\\n",
"contain irrelevant information.\n",
"\n",
"{plan}\n",
"\n",
"Now solve the question or task according to provided Evidence above. Respond with the answer\n",
"directly with no extra words.\n",
"\n",
"Task: {task}\n",
"Response:\"\"\"\n",
"\n",
"\n",
"def solve(state: ReWOO):\n",
" plan = \"\"\n",
" for _plan, step_name, tool, tool_input in state[\"steps\"]:\n",
" _results = state[\"results\"] or {}\n",
" for k, v in _results.items():\n",
" tool_input = tool_input.replace(k, v)\n",
" step_name = step_name.replace(k, v)\n",
" plan += f\"Plan: {_plan}\\n{step_name} = {tool}[{tool_input}]\"\n",
" prompt = solve_prompt.format(plan=plan, task=state[\"task\"])\n",
" result = model.invoke(prompt)\n",
" return {\"result\": result.content}"
]
},
{
"cell_type": "markdown",
"id": "8ce26c3f-6ced-4a91-a9f2-d0bc235e4010",
"metadata": {},
"source": [
"## 4. Define Graph\n",
"## Define Graph\n",
"\n",
"Our graph defines the workflow. Each of the planner, tool executor, and solver modules are added as nodes."
]
@@ -233,7 +379,16 @@
"id": "73b235d7-fa83-4e84-9f2e-2908f16deb26",
"metadata": {},
"outputs": [],
"source": ["def _route(state):\n _step = _get_current_task(state)\n if _step is None:\n # We have executed all tasks\n return \"solve\"\n else:\n # We are still executing tasks, loop back to the \"tool\" node\n return \"tool\""]
"source": [
"def _route(state):\n",
" _step = _get_current_task(state)\n",
" if _step is None:\n",
" # We have executed all tasks\n",
" return \"solve\"\n",
" else:\n",
" # We are still executing tasks, loop back to the \"tool\" node\n",
" return \"tool\""
]
},
{
"cell_type": "code",
@@ -241,7 +396,20 @@
"id": "cf173aa1-ce31-4dca-8111-30c91e209652",
"metadata": {},
"outputs": [],
"source": ["from langgraph.graph import END, StateGraph, START\n\ngraph = StateGraph(ReWOO)\ngraph.add_node(\"plan\", get_plan)\ngraph.add_node(\"tool\", tool_execution)\ngraph.add_node(\"solve\", solve)\ngraph.add_edge(\"plan\", \"tool\")\ngraph.add_edge(\"solve\", END)\ngraph.add_conditional_edges(\"tool\", _route)\ngraph.add_edge(START, \"plan\")\n\napp = graph.compile()"]
"source": [
"from langgraph.graph import END, StateGraph, START\n",
"\n",
"graph = StateGraph(ReWOO)\n",
"graph.add_node(\"plan\", get_plan)\n",
"graph.add_node(\"tool\", tool_execution)\n",
"graph.add_node(\"solve\", solve)\n",
"graph.add_edge(\"plan\", \"tool\")\n",
"graph.add_edge(\"solve\", END)\n",
"graph.add_conditional_edges(\"tool\", _route)\n",
"graph.add_edge(START, \"plan\")\n",
"\n",
"app = graph.compile()"
]
},
{
"cell_type": "code",
@@ -270,7 +438,11 @@
]
}
],
"source": ["for s in app.stream({\"task\": task}):\n print(s)\n print(\"---\")"]
"source": [
"for s in app.stream({\"task\": task}):\n",
" print(s)\n",
" print(\"---\")"
]
},
{
"cell_type": "code",
@@ -286,7 +458,10 @@
]
}
],
"source": ["# Print out the final result\nprint(s[END][\"result\"])"]
"source": [
"# Print out the final result\n",
"print(s[END][\"result\"])"
]
},
{
"cell_type": "markdown",
@@ -12,6 +12,60 @@
"Based on [this implementation from @catid](https://github.com/catid/self-discover/tree/main?tab=readme-ov-file)\n",
"\n",
"\n",
"## Setup\n",
"\n",
"First, let's install our required packages and set our API keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2811c3da",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U --quiet langchain langgraph langchain_openai"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5e66899a",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_if_undefined(var: str) -> None:\n",
" if os.environ.get(var):\n",
" return\n",
" os.environ[var] = getpass.getpass(var)\n",
"\n",
"\n",
"_set_if_undefined(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "35dce921",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "35b1729e",
"metadata": {},
"source": [
"## Define the prompts"
]
},
+6 -12
View File
@@ -70,18 +70,12 @@
"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",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+6 -12
View File
@@ -69,18 +69,12 @@
"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",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+18 -7
View File
@@ -39,7 +39,9 @@
"M: Max number of conversation turns in step (Step 3)\n",
"\n",
"\n",
"## Prerequisites"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -82,18 +84,27 @@
" os.environ[var] = getpass.getpass(var + \":\")\n",
"\n",
"\n",
"# Set for tracing\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"STORM\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")\n",
"_set_env(\"OPENAI_API_KEY\")"
"_set_env(\"OPENAI_API_KEY\")\n",
"_set_env(\"TAVILY_API_KEY\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Select LLMs\n",
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Select LLMs\n",
"\n",
"We will have a faster LLM do most of the work, but a slower, long-context model to distill the conversations and write the final report."
]
+28 -19
View File
@@ -21,15 +21,9 @@
"id": "7c2f84f1-0751-4779-97d4-5cbb286093b7",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "markdown",
"id": "323db423-b644-40bd-9c2d-976a53f602f7",
"metadata": {},
"source": [
"We'll be using a simple ReAct agent for this guide."
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -45,18 +39,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "f7f9f24a-e3d0-422b-8924-47950b2facd6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -70,6 +56,29 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "4e48aa9e",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "cc82c21f",
"metadata": {},
"source": [
"## Define the graph\n",
"\n",
"We'll be using a simple ReAct agent for this guide."
]
},
{
"cell_type": "code",
"execution_count": 3,
+28 -19
View File
@@ -26,15 +26,9 @@
"id": "7c2f84f1-0751-4779-97d4-5cbb286093b7",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "markdown",
"id": "323db423-b644-40bd-9c2d-976a53f602f7",
"metadata": {},
"source": [
"We'll be using a simple ReAct agent for this guide."
"## Setup\n",
"\n",
"First, let's install the required package and set our API keys"
]
},
{
@@ -50,18 +44,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "f7f9f24a-e3d0-422b-8924-47950b2facd6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -75,6 +61,29 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "cc6c48fe",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "2e7777f9",
"metadata": {},
"source": [
"## Define the graph\n",
"\n",
"We'll be using a simple ReAct agent for this guide."
]
},
{
"cell_type": "code",
"execution_count": 3,
+28 -19
View File
@@ -26,15 +26,9 @@
"id": "7c2f84f1-0751-4779-97d4-5cbb286093b7",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "markdown",
"id": "323db423-b644-40bd-9c2d-976a53f602f7",
"metadata": {},
"source": [
"We'll be using a simple ReAct agent for this guide."
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -50,18 +44,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "f7f9f24a-e3d0-422b-8924-47950b2facd6",
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -75,6 +61,29 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "eaaab1fc",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "7939a3c5",
"metadata": {},
"source": [
"## Define the graph\n",
"\n",
"We'll be using a simple ReAct agent for this guide."
]
},
{
"cell_type": "code",
"execution_count": 3,
+45 -1
View File
@@ -11,7 +11,35 @@
"\n",
"We do so using a [RunnableGenerator](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.RunnableGenerator.html#langchain-core-runnables-base-runnablegenerator) (which your function will automatically behave as if wrapped as a [RunnableLambda](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.RunnableLambda.html#langchain_core.runnables.base.RunnableLambda)).\n",
"\n",
"Below is a simple toy example."
"Below is a simple toy example.\n",
"\n",
"## Setup\n",
"\n",
"First, let's install our required packages"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e1a20f31",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"id": "12297071",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
@@ -29,6 +57,14 @@
"</div>"
]
},
{
"cell_type": "markdown",
"id": "07d5779e",
"metadata": {},
"source": [
"## Define the graph"
]
},
{
"cell_type": "code",
"execution_count": 1,
@@ -83,6 +119,14 @@
"app = workflow.compile()"
]
},
{
"cell_type": "markdown",
"id": "2af9e94e",
"metadata": {},
"source": [
"## Stream arbitrarily nested content"
]
},
{
"cell_type": "code",
"execution_count": 2,
@@ -21,7 +21,9 @@
"id": "a37f60af-43ea-4aa6-847a-df8cc47065f5",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -30,30 +32,49 @@
"id": "47f79af8-58d8-4a48-8d9a-88823d88701f",
"metadata": {},
"outputs": [],
"source": ["%%capture --no-stderr\n%pip install -U langgraph openai"]
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph openai"
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "0cf6b41d-7fcb-40b6-9a72-229cdd00a094",
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"source": ["import getpass\nimport os\n\n\ndef _set_env(var: str):\n if not os.environ.get(var):\n os.environ[var] = getpass.getpass(f\"{var}: \")\n\n\n_set_env(\"OPENAI_API_KEY\")"]
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "d8df7b58",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "e3d02ebb-c2e1-4ef7-b187-810d55139317",
"metadata": {},
"source": [
"## Define model, tools and graph"
"## Define the graph"
]
},
{
@@ -70,7 +91,94 @@
"id": "d59234f9-173e-469d-a725-c13e0979663e",
"metadata": {},
"outputs": [],
"source": ["from openai import AsyncOpenAI\nfrom langchain_core.language_models.chat_models import ChatGenerationChunk\nfrom langchain_core.messages import AIMessageChunk\nfrom langchain_core.runnables.config import (\n ensure_config,\n get_callback_manager_for_config,\n)\n\nopenai_client = AsyncOpenAI()\n# define tool schema for openai tool calling\n\ntool = {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_items\",\n \"description\": \"Use this tool to look up which items are in the given place.\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\"place\": {\"type\": \"string\"}},\n \"required\": [\"place\"],\n },\n },\n}\n\n\nasync def call_model(state, config=None):\n config = ensure_config(config | {\"tags\": [\"agent_llm\"]})\n callback_manager = get_callback_manager_for_config(config)\n messages = state[\"messages\"]\n\n llm_run_manager = callback_manager.on_chat_model_start({}, [messages])[0]\n response = await openai_client.chat.completions.create(\n messages=messages, model=\"gpt-3.5-turbo\", tools=[tool], stream=True\n )\n\n response_content = \"\"\n role = None\n\n tool_call_id = None\n tool_call_function_name = None\n tool_call_function_arguments = \"\"\n async for chunk in response:\n delta = chunk.choices[0].delta\n if delta.role is not None:\n role = delta.role\n\n if delta.content:\n response_content += delta.content\n llm_run_manager.on_llm_new_token(delta.content)\n\n if delta.tool_calls:\n # note: for simplicity we're only handling a single tool call here\n if delta.tool_calls[0].function.name is not None:\n tool_call_function_name = delta.tool_calls[0].function.name\n tool_call_id = delta.tool_calls[0].id\n\n # note: we're wrapping the tools calls in ChatGenerationChunk so that the events from .astream_events in the graph can render tool calls correctly\n tool_call_chunk = ChatGenerationChunk(\n message=AIMessageChunk(\n content=\"\",\n additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]},\n )\n )\n llm_run_manager.on_llm_new_token(\"\", chunk=tool_call_chunk)\n tool_call_function_arguments += delta.tool_calls[0].function.arguments\n\n if tool_call_function_name is not None:\n tool_calls = [\n {\n \"id\": tool_call_id,\n \"function\": {\n \"name\": tool_call_function_name,\n \"arguments\": tool_call_function_arguments,\n },\n \"type\": \"function\",\n }\n ]\n else:\n tool_calls = None\n\n response_message = {\n \"role\": role,\n \"content\": response_content,\n \"tool_calls\": tool_calls,\n }\n return {\"messages\": [response_message]}"]
"source": [
"from openai import AsyncOpenAI\n",
"from langchain_core.language_models.chat_models import ChatGenerationChunk\n",
"from langchain_core.messages import AIMessageChunk\n",
"from langchain_core.runnables.config import (\n",
" ensure_config,\n",
" get_callback_manager_for_config,\n",
")\n",
"\n",
"openai_client = AsyncOpenAI()\n",
"# define tool schema for openai tool calling\n",
"\n",
"tool = {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"get_items\",\n",
" \"description\": \"Use this tool to look up which items are in the given place.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\"place\": {\"type\": \"string\"}},\n",
" \"required\": [\"place\"],\n",
" },\n",
" },\n",
"}\n",
"\n",
"\n",
"async def call_model(state, config=None):\n",
" config = ensure_config(config | {\"tags\": [\"agent_llm\"]})\n",
" callback_manager = get_callback_manager_for_config(config)\n",
" messages = state[\"messages\"]\n",
"\n",
" llm_run_manager = callback_manager.on_chat_model_start({}, [messages])[0]\n",
" response = await openai_client.chat.completions.create(\n",
" messages=messages, model=\"gpt-3.5-turbo\", tools=[tool], stream=True\n",
" )\n",
"\n",
" response_content = \"\"\n",
" role = None\n",
"\n",
" tool_call_id = None\n",
" tool_call_function_name = None\n",
" tool_call_function_arguments = \"\"\n",
" async for chunk in response:\n",
" delta = chunk.choices[0].delta\n",
" if delta.role is not None:\n",
" role = delta.role\n",
"\n",
" if delta.content:\n",
" response_content += delta.content\n",
" llm_run_manager.on_llm_new_token(delta.content)\n",
"\n",
" if delta.tool_calls:\n",
" # note: for simplicity we're only handling a single tool call here\n",
" if delta.tool_calls[0].function.name is not None:\n",
" tool_call_function_name = delta.tool_calls[0].function.name\n",
" tool_call_id = delta.tool_calls[0].id\n",
"\n",
" # note: we're wrapping the tools calls in ChatGenerationChunk so that the events from .astream_events in the graph can render tool calls correctly\n",
" tool_call_chunk = ChatGenerationChunk(\n",
" message=AIMessageChunk(\n",
" content=\"\",\n",
" additional_kwargs={\"tool_calls\": [delta.tool_calls[0].dict()]},\n",
" )\n",
" )\n",
" llm_run_manager.on_llm_new_token(\"\", chunk=tool_call_chunk)\n",
" tool_call_function_arguments += delta.tool_calls[0].function.arguments\n",
"\n",
" if tool_call_function_name is not None:\n",
" tool_calls = [\n",
" {\n",
" \"id\": tool_call_id,\n",
" \"function\": {\n",
" \"name\": tool_call_function_name,\n",
" \"arguments\": tool_call_function_arguments,\n",
" },\n",
" \"type\": \"function\",\n",
" }\n",
" ]\n",
" else:\n",
" tool_calls = None\n",
"\n",
" response_message = {\n",
" \"role\": role,\n",
" \"content\": response_content,\n",
" \"tool_calls\": tool_calls,\n",
" }\n",
" return {\"messages\": [response_message]}"
]
},
{
"cell_type": "markdown",
@@ -86,7 +194,62 @@
"id": "b90941d8-afe4-42ec-9262-9c3b87c3b1ec",
"metadata": {},
"outputs": [],
"source": ["import json\nfrom langchain_core.callbacks import adispatch_custom_event\n\n\nasync def get_items(place: str) -> str:\n \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n\n # this can be replaced with any actual streaming logic that you might have\n def stream(place: str):\n if \"bed\" in place: # For under the bed\n yield from [\"socks\", \"shoes\", \"dust bunnies\"]\n elif \"shelf\" in place: # For 'shelf'\n yield from [\"books\", \"penciles\", \"pictures\"]\n else: # if the agent decides to ask about a different place\n yield \"cat snacks\"\n\n tokens = []\n for token in stream(place):\n await adispatch_custom_event(\n # this will allow you to filter events by name\n \"tool_call_token_stream\",\n {\n \"function_name\": \"get_items\",\n \"arguments\": {\"place\": place},\n \"tool_output_token\": token,\n },\n # this will allow you to filter events by tags\n config={\"tags\": [\"tool_call\"]},\n )\n tokens.append(token)\n\n return \", \".join(tokens)\n\n\n# define mapping to look up functions when running tools\nfunction_name_to_function = {\"get_items\": get_items}\n\n\nasync def call_tools(state):\n messages = state[\"messages\"]\n\n tool_call = messages[-1][\"tool_calls\"][0]\n function_name = tool_call[\"function\"][\"name\"]\n function_arguments = tool_call[\"function\"][\"arguments\"]\n arguments = json.loads(function_arguments)\n\n function_response = await function_name_to_function[function_name](**arguments)\n tool_message = {\n \"tool_call_id\": tool_call[\"id\"],\n \"role\": \"tool\",\n \"name\": function_name,\n \"content\": function_response,\n }\n return {\"messages\": [tool_message]}"]
"source": [
"import json\n",
"from langchain_core.callbacks import adispatch_custom_event\n",
"\n",
"\n",
"async def get_items(place: str) -> str:\n",
" \"\"\"Use this tool to look up which items are in the given place.\"\"\"\n",
"\n",
" # this can be replaced with any actual streaming logic that you might have\n",
" def stream(place: str):\n",
" if \"bed\" in place: # For under the bed\n",
" yield from [\"socks\", \"shoes\", \"dust bunnies\"]\n",
" elif \"shelf\" in place: # For 'shelf'\n",
" yield from [\"books\", \"penciles\", \"pictures\"]\n",
" else: # if the agent decides to ask about a different place\n",
" yield \"cat snacks\"\n",
"\n",
" tokens = []\n",
" for token in stream(place):\n",
" await adispatch_custom_event(\n",
" # this will allow you to filter events by name\n",
" \"tool_call_token_stream\",\n",
" {\n",
" \"function_name\": \"get_items\",\n",
" \"arguments\": {\"place\": place},\n",
" \"tool_output_token\": token,\n",
" },\n",
" # this will allow you to filter events by tags\n",
" config={\"tags\": [\"tool_call\"]},\n",
" )\n",
" tokens.append(token)\n",
"\n",
" return \", \".join(tokens)\n",
"\n",
"\n",
"# define mapping to look up functions when running tools\n",
"function_name_to_function = {\"get_items\": get_items}\n",
"\n",
"\n",
"async def call_tools(state):\n",
" messages = state[\"messages\"]\n",
"\n",
" tool_call = messages[-1][\"tool_calls\"][0]\n",
" function_name = tool_call[\"function\"][\"name\"]\n",
" function_arguments = tool_call[\"function\"][\"arguments\"]\n",
" arguments = json.loads(function_arguments)\n",
"\n",
" function_response = await function_name_to_function[function_name](**arguments)\n",
" tool_message = {\n",
" \"tool_call_id\": tool_call[\"id\"],\n",
" \"role\": \"tool\",\n",
" \"name\": function_name,\n",
" \"content\": function_response,\n",
" }\n",
" return {\"messages\": [tool_message]}"
]
},
{
"cell_type": "markdown",
@@ -102,7 +265,33 @@
"id": "228260be-1f9a-4195-80e0-9604f8a5dba6",
"metadata": {},
"outputs": [],
"source": ["import operator\nfrom typing import Annotated, TypedDict, Literal\n\nfrom langgraph.graph import StateGraph, END, START\n\n\nclass State(TypedDict):\n messages: Annotated[list, operator.add]\n\n\ndef should_continue(state) -> Literal[\"tools\", END]:\n messages = state[\"messages\"]\n last_message = messages[-1]\n if last_message[\"tool_calls\"]:\n return \"tools\"\n return END\n\n\nworkflow = StateGraph(State)\nworkflow.add_edge(START, \"model\")\nworkflow.add_node(\"model\", call_model) # i.e. our \"agent\"\nworkflow.add_node(\"tools\", call_tools)\nworkflow.add_conditional_edges(\"model\", should_continue)\nworkflow.add_edge(\"tools\", \"model\")\ngraph = workflow.compile()"]
"source": [
"import operator\n",
"from typing import Annotated, TypedDict, Literal\n",
"\n",
"from langgraph.graph import StateGraph, END, START\n",
"\n",
"\n",
"class State(TypedDict):\n",
" messages: Annotated[list, operator.add]\n",
"\n",
"\n",
"def should_continue(state) -> Literal[\"tools\", END]:\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" if last_message[\"tool_calls\"]:\n",
" return \"tools\"\n",
" return END\n",
"\n",
"\n",
"workflow = StateGraph(State)\n",
"workflow.add_edge(START, \"model\")\n",
"workflow.add_node(\"model\", call_model) # i.e. our \"agent\"\n",
"workflow.add_node(\"tools\", call_tools)\n",
"workflow.add_conditional_edges(\"model\", should_continue)\n",
"workflow.add_edge(\"tools\", \"model\")\n",
"graph = workflow.compile()"
]
},
{
"cell_type": "markdown",
@@ -136,7 +325,14 @@
]
}
],
"source": ["async for event in graph.astream_events(\n {\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"\n):\n tags = event.get(\"tags\", [])\n if event[\"event\"] == \"on_custom_event\" and \"tool_call\" in tags:\n print(\"Tool token\", event[\"data\"][\"tool_output_token\"])"]
"source": [
"async for event in graph.astream_events(\n",
" {\"messages\": [{\"role\": \"user\", \"content\": \"what's in the bedroom\"}]}, version=\"v2\"\n",
"):\n",
" tags = event.get(\"tags\", [])\n",
" if event[\"event\"] == \"on_custom_event\" and \"tool_call\" in tags:\n",
" print(\"Tool token\", event[\"data\"][\"tool_output_token\"])"
]
}
],
"metadata": {
@@ -21,7 +21,9 @@
"id": "a37f60af-43ea-4aa6-847a-df8cc47065f5",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -37,18 +39,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "0cf6b41d-7fcb-40b6-9a72-229cdd00a094",
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -62,12 +56,25 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "767cd76a",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "e3d02ebb-c2e1-4ef7-b187-810d55139317",
"metadata": {},
"source": [
"## Define graph and tools"
"## Define the graph"
]
},
{
+16 -11
View File
@@ -17,7 +17,7 @@
"\n",
"## Setup\n",
"\n",
"First let's install our required packages and set our environment variables."
"First let's install our required packages and set our API keys"
]
},
{
@@ -33,18 +33,10 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "c87e4a47-4099-4d1a-907c-a99fa857165a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -58,6 +50,19 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "eb79e50b",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "17f994ca-28e7-4379-a1c9-8c1682773b5f",
+13 -1
View File
@@ -10,7 +10,7 @@
"\n",
"## Setup\n",
"\n",
"First let's download the required packages and set our OpenAI API key since we will need that to run the models "
"First let's install the required packages and set our API keys"
]
},
{
@@ -41,6 +41,18 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
@@ -21,7 +21,9 @@
"id": "a37f60af-43ea-4aa6-847a-df8cc47065f5",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -37,18 +39,10 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"id": "0cf6b41d-7fcb-40b6-9a72-229cdd00a094",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPENAI_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -62,6 +56,19 @@
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "1c5bc618",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "e3d02ebb-c2e1-4ef7-b187-810d55139317",
+6 -12
View File
@@ -80,18 +80,12 @@
"id": "cc088bbd",
"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": "907bf5e8",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+17 -1
View File
@@ -9,7 +9,11 @@
"\n",
"It's possible that your subgraph state is completely independent from the parent graph state, i.e. there are no overlapping channels (keys) between the two. For example, you might have a supervisor agent that needs to produce a report with a help of multiple ReAct agents. ReAct agent subgraphs might keep track of a list of messages whereas the supervisor only needs user input and final report in its state, and doesn't need to keep track of messages.\n",
"\n",
"In such cases you need to transform the inputs to the subgraph before calling it and then transform its outputs before returning. This guide shows how to do that."
"In such cases you need to transform the inputs to the subgraph before calling it and then transform its outputs before returning. This guide shows how to do that.\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages"
]
},
{
@@ -22,6 +26,18 @@
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"attachments": {},
"cell_type": "markdown",
+21
View File
@@ -16,6 +16,15 @@
"![Screenshot 2024-07-11 at 1.01.28 PM.png](attachment:71516aef-9c00-4730-a676-a54e90cb6472.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setup\n",
"\n",
"First, let's install the required packages"
]
},
{
"cell_type": "code",
"execution_count": 1,
@@ -26,6 +35,18 @@
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"attachments": {
"9145adc1-ce9d-4a22-8183-e13796d4a388.png": {
+6 -11
View File
@@ -55,17 +55,12 @@
"cell_type": "markdown",
"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": null,
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+17 -11
View File
@@ -34,7 +34,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -49,17 +51,9 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"ANTHROPIC_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -73,6 +67,18 @@
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
+17 -11
View File
@@ -15,7 +15,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Setup"
"## Setup\n",
"\n",
"First, let's install the required packages and set our API keys"
]
},
{
@@ -30,17 +32,9 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"ANTHROPIC_API_KEY: ········\n"
]
}
],
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
@@ -54,6 +48,18 @@
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
+29 -10
View File
@@ -39,9 +39,20 @@
}
},
"source": [
"## Set up environment\n",
"## Setup\n",
"\n",
"We'll set up our environment variables for OpenAI, and optionally, to enable tracing with [LangSmith](https://smith.langchain.com)."
"First let's install our required packages and set our API keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4a4be247",
"metadata": {},
"outputs": [],
"source": [
"%capture --no-stderr\n",
"%pip install -U langgraph langchain_openai langchain_community"
]
},
{
@@ -60,21 +71,29 @@
},
"outputs": [],
"source": [
"import getpass\n",
"import os\n",
"\n",
"os.environ[\"OPENAI_API_KEY\"] = \"sk-...\"\n",
"os.environ[\"LANGSMITH_API_KEY\"] = \"lsv2_pt_...\"\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\""
"\n",
"def _set_env(key: str):\n",
" if key not in os.environ:\n",
" os.environ[key] = getpass.getpass(f\"{key}:\")\n",
"\n",
"\n",
"_set_env(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "04d73c39-1cc9-4b94-a454-b0a4f604713c",
"cell_type": "markdown",
"id": "80559636",
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"LANGCHAIN_PROJECT\"] = \"sql-agent\""
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+610 -29
View File
@@ -29,7 +29,9 @@
"4. **Update** the taxonomy on each subsequent minibatch via a ritique and revise prompt\n",
"5. **Review** the final taxonomy, scoring its quality and generating a final value using a final sample.\n",
"\n",
"## Prerequisites\n"
"## Setup\n",
"\n",
"First, let's install our required packages and set our API keys\n"
]
},
{
@@ -38,7 +40,12 @@
"id": "abd95235-4da5-4d6a-985f-78b2572ad626",
"metadata": {},
"outputs": [],
"source": ["%%capture --no-stderr\n%pip install -U langgraph langchain_anthropic langsmith\n# For the embedding-based classifier use in phase 2\n%pip install -U sklearn langchain_openai"]
"source": [
"%%capture --no-stderr\n",
"%pip install -U langgraph langchain_anthropic langsmith\n",
"# For the embedding-based classifier use in phase 2\n",
"%pip install -U sklearn langchain_openai"
]
},
{
"cell_type": "code",
@@ -46,14 +53,41 @@
"id": "d98b62e4-d327-4442-8482-65529500a8a7",
"metadata": {},
"outputs": [],
"source": ["import os\nfrom getpass import getpass\n\nif \"ANTHROPIC_API_KEY\" not in os.environ:\n os.environ[\"ANTHROPIC_API_KEY\"] = getpass(\"Enter your ANTHROPIC_API_KEY: \")\n\n# (Optional) Enable tracing\nos.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\nos.environ[\"LANGCHAIN_PROJECT\"] = \"tnt-llm\"\n\nif \"LANGCHAIN_API_KEY\" not in os.environ:\n os.environ[\"LANGCHAIN_API_KEY\"] = getpass(\"Enter your LANGCHAIN_API_KEY: \")"]
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if os.environ.get(var):\n",
" return\n",
" os.environ[var] = getpass.getpass(var + \":\")\n",
"\n",
"\n",
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "a21bbd3f",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "04a133e8-f94d-4ae4-8ee1-6bee09dad4fd",
"metadata": {},
"source": [
"#### Graph State\n",
"## Define the graph\n",
"\n",
"### Graph State\n",
"\n",
"Since each node of a StateGraph accepts the state (and returns an updated state), we'll define that at the outset.\n",
"\n",
@@ -66,13 +100,39 @@
"id": "580d82b5-b60c-47a4-9c8b-e28be22ca0e3",
"metadata": {},
"outputs": [],
"source": ["import logging\nimport operator\nfrom typing import Annotated, List, Optional, TypedDict\n\nlogging.basicConfig(level=logging.WARNING)\nlogger = logging.getLogger(\"tnt-llm\")\n\n\nclass Doc(TypedDict):\n id: str\n content: str\n summary: Optional[str]\n explanation: Optional[str]\n category: Optional[str]\n\n\nclass TaxonomyGenerationState(TypedDict):\n # The raw docs; we inject summaries within them in the first step\n documents: List[Doc]\n # Indices to be concise\n minibatches: List[List[int]]\n # Candidate Taxonomies (full trajectory)\n clusters: Annotated[List[List[dict]], operator.add]"]
"source": [
"import logging\n",
"import operator\n",
"from typing import Annotated, List, Optional, TypedDict\n",
"\n",
"logging.basicConfig(level=logging.WARNING)\n",
"logger = logging.getLogger(\"tnt-llm\")\n",
"\n",
"\n",
"class Doc(TypedDict):\n",
" id: str\n",
" content: str\n",
" summary: Optional[str]\n",
" explanation: Optional[str]\n",
" category: Optional[str]\n",
"\n",
"\n",
"class TaxonomyGenerationState(TypedDict):\n",
" # The raw docs; we inject summaries within them in the first step\n",
" documents: List[Doc]\n",
" # Indices to be concise\n",
" minibatches: List[List[int]]\n",
" # Candidate Taxonomies (full trajectory)\n",
" clusters: Annotated[List[List[dict]], operator.add]"
]
},
{
"cell_type": "markdown",
"id": "8e13d0b3-03a5-4584-98e4-b06cbb446e35",
"metadata": {},
"source": [
"### Define nodes\n",
"\n",
"#### 1. Summarize Docs\n",
"\n",
"Chat logs can get quite long. Our taxonomy generation step needs to see large, diverse minibatches to be able to adequately capture the distribution of categories. To ensure they can all fit efficiently into the context window, we first summarize each chat log. Downstream steps will use these summaries instead of the raw doc content.\n"
@@ -84,7 +144,77 @@
"id": "ff02c2a1-18b5-4848-96bb-27ff00978570",
"metadata": {},
"outputs": [],
"source": ["import re\n\nfrom langchain import hub\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_core.output_parsers import StrOutputParser\nfrom langchain_core.runnables import RunnableConfig, RunnableLambda, RunnablePassthrough\n\nsummary_prompt = hub.pull(\"wfh/tnt-llm-summary-generation\").partial(\n summary_length=20, explanation_length=30\n)\n\n\ndef parse_summary(xml_string: str) -> dict:\n summary_pattern = r\"<summary>(.*?)</summary>\"\n explanation_pattern = r\"<explanation>(.*?)</explanation>\"\n\n summary_match = re.search(summary_pattern, xml_string, re.DOTALL)\n explanation_match = re.search(explanation_pattern, xml_string, re.DOTALL)\n\n summary = summary_match.group(1).strip() if summary_match else \"\"\n explanation = explanation_match.group(1).strip() if explanation_match else \"\"\n\n return {\"summary\": summary, \"explanation\": explanation}\n\n\nsummary_llm_chain = (\n summary_prompt\n | ChatAnthropic(model=\"claude-3-haiku-20240307\")\n | StrOutputParser()\n # Customize the tracing name for easier organization\n).with_config(run_name=\"GenerateSummary\")\nsummary_chain = summary_llm_chain | parse_summary\n\n\n# Now combine as a \"map\" operation in a map-reduce chain\n# Input: state\n# Output: state U summaries\n# Processes docs in parallel\ndef get_content(state: TaxonomyGenerationState):\n docs = state[\"documents\"]\n return [{\"content\": doc[\"content\"]} for doc in docs]\n\n\nmap_step = RunnablePassthrough.assign(\n summaries=get_content\n # This effectively creates a \"map\" operation\n # Note you can make this more robust by handling individual errors\n | RunnableLambda(func=summary_chain.batch, afunc=summary_chain.abatch)\n)\n\n\ndef reduce_summaries(combined: dict) -> TaxonomyGenerationState:\n summaries = combined[\"summaries\"]\n documents = combined[\"documents\"]\n return {\n \"documents\": [\n {\n \"id\": doc[\"id\"],\n \"content\": doc[\"content\"],\n \"summary\": summ_info[\"summary\"],\n \"explanation\": summ_info[\"explanation\"],\n }\n for doc, summ_info in zip(documents, summaries)\n ]\n }\n\n\n# This is actually the node itself!\nmap_reduce_chain = map_step | reduce_summaries"]
"source": [
"import re\n",
"\n",
"from langchain import hub\n",
"from langchain_anthropic import ChatAnthropic\n",
"from langchain_core.output_parsers import StrOutputParser\n",
"from langchain_core.runnables import RunnableConfig, RunnableLambda, RunnablePassthrough\n",
"\n",
"summary_prompt = hub.pull(\"wfh/tnt-llm-summary-generation\").partial(\n",
" summary_length=20, explanation_length=30\n",
")\n",
"\n",
"\n",
"def parse_summary(xml_string: str) -> dict:\n",
" summary_pattern = r\"<summary>(.*?)</summary>\"\n",
" explanation_pattern = r\"<explanation>(.*?)</explanation>\"\n",
"\n",
" summary_match = re.search(summary_pattern, xml_string, re.DOTALL)\n",
" explanation_match = re.search(explanation_pattern, xml_string, re.DOTALL)\n",
"\n",
" summary = summary_match.group(1).strip() if summary_match else \"\"\n",
" explanation = explanation_match.group(1).strip() if explanation_match else \"\"\n",
"\n",
" return {\"summary\": summary, \"explanation\": explanation}\n",
"\n",
"\n",
"summary_llm_chain = (\n",
" summary_prompt\n",
" | ChatAnthropic(model=\"claude-3-haiku-20240307\")\n",
" | StrOutputParser()\n",
" # Customize the tracing name for easier organization\n",
").with_config(run_name=\"GenerateSummary\")\n",
"summary_chain = summary_llm_chain | parse_summary\n",
"\n",
"\n",
"# Now combine as a \"map\" operation in a map-reduce chain\n",
"# Input: state\n",
"# Output: state U summaries\n",
"# Processes docs in parallel\n",
"def get_content(state: TaxonomyGenerationState):\n",
" docs = state[\"documents\"]\n",
" return [{\"content\": doc[\"content\"]} for doc in docs]\n",
"\n",
"\n",
"map_step = RunnablePassthrough.assign(\n",
" summaries=get_content\n",
" # This effectively creates a \"map\" operation\n",
" # Note you can make this more robust by handling individual errors\n",
" | RunnableLambda(func=summary_chain.batch, afunc=summary_chain.abatch)\n",
")\n",
"\n",
"\n",
"def reduce_summaries(combined: dict) -> TaxonomyGenerationState:\n",
" summaries = combined[\"summaries\"]\n",
" documents = combined[\"documents\"]\n",
" return {\n",
" \"documents\": [\n",
" {\n",
" \"id\": doc[\"id\"],\n",
" \"content\": doc[\"content\"],\n",
" \"summary\": summ_info[\"summary\"],\n",
" \"explanation\": summ_info[\"explanation\"],\n",
" }\n",
" for doc, summ_info in zip(documents, summaries)\n",
" ]\n",
" }\n",
"\n",
"\n",
"# This is actually the node itself!\n",
"map_reduce_chain = map_step | reduce_summaries"
]
},
{
"cell_type": "markdown",
@@ -102,7 +232,36 @@
"id": "3e0139c3-b5ba-42b9-9367-33533d66eb58",
"metadata": {},
"outputs": [],
"source": ["import random\n\n\ndef get_minibatches(state: TaxonomyGenerationState, config: RunnableConfig):\n batch_size = config[\"configurable\"].get(\"batch_size\", 200)\n original = state[\"documents\"]\n indices = list(range(len(original)))\n random.shuffle(indices)\n if len(indices) < batch_size:\n # Don't pad needlessly if we can't fill a single batch\n return [indices]\n\n num_full_batches = len(indices) // batch_size\n\n batches = [\n indices[i * batch_size : (i + 1) * batch_size] for i in range(num_full_batches)\n ]\n\n leftovers = len(indices) % batch_size\n if leftovers:\n last_batch = indices[num_full_batches * batch_size :]\n elements_to_add = batch_size - leftovers\n last_batch += random.sample(indices, elements_to_add)\n batches.append(last_batch)\n\n return {\n \"minibatches\": batches,\n }"]
"source": [
"import random\n",
"\n",
"\n",
"def get_minibatches(state: TaxonomyGenerationState, config: RunnableConfig):\n",
" batch_size = config[\"configurable\"].get(\"batch_size\", 200)\n",
" original = state[\"documents\"]\n",
" indices = list(range(len(original)))\n",
" random.shuffle(indices)\n",
" if len(indices) < batch_size:\n",
" # Don't pad needlessly if we can't fill a single batch\n",
" return [indices]\n",
"\n",
" num_full_batches = len(indices) // batch_size\n",
"\n",
" batches = [\n",
" indices[i * batch_size : (i + 1) * batch_size] for i in range(num_full_batches)\n",
" ]\n",
"\n",
" leftovers = len(indices) % batch_size\n",
" if leftovers:\n",
" last_batch = indices[num_full_batches * batch_size :]\n",
" elements_to_add = batch_size - leftovers\n",
" last_batch += random.sample(indices, elements_to_add)\n",
" batches.append(last_batch)\n",
"\n",
" return {\n",
" \"minibatches\": batches,\n",
" }"
]
},
{
"cell_type": "markdown",
@@ -120,7 +279,80 @@
"id": "224ed013-2963-489c-b734-315cad701d59",
"metadata": {},
"outputs": [],
"source": ["from typing import Dict\n\nfrom langchain_core.runnables import Runnable\n\n\ndef parse_taxa(output_text: str) -> Dict:\n \"\"\"Extract the taxonomy from the generated output.\"\"\"\n cluster_matches = re.findall(\n r\"\\s*<id>(.*?)</id>\\s*<name>(.*?)</name>\\s*<description>(.*?)</description>\\s*\",\n output_text,\n re.DOTALL,\n )\n clusters = [\n {\"id\": id.strip(), \"name\": name.strip(), \"description\": description.strip()}\n for id, name, description in cluster_matches\n ]\n # We don't parse the explanation since it isn't used downstream\n return {\"clusters\": clusters}\n\n\ndef format_docs(docs: List[Doc]) -> str:\n xml_table = \"<conversations>\\n\"\n for doc in docs:\n xml_table += f'<conv_summ id={doc[\"id\"]}>{doc[\"summary\"]}</conv_summ>\\n'\n xml_table += \"</conversations>\"\n return xml_table\n\n\ndef format_taxonomy(clusters):\n xml = \"<cluster_table>\\n\"\n for label in clusters:\n xml += \" <cluster>\\n\"\n xml += f' <id>{label[\"id\"]}</id>\\n'\n xml += f' <name>{label[\"name\"]}</name>\\n'\n xml += f' <description>{label[\"description\"]}</description>\\n'\n xml += \" </cluster>\\n\"\n xml += \"</cluster_table>\"\n return xml\n\n\ndef invoke_taxonomy_chain(\n chain: Runnable,\n state: TaxonomyGenerationState,\n config: RunnableConfig,\n mb_indices: List[int],\n) -> TaxonomyGenerationState:\n configurable = config[\"configurable\"]\n docs = state[\"documents\"]\n minibatch = [docs[idx] for idx in mb_indices]\n data_table_xml = format_docs(minibatch)\n\n previous_taxonomy = state[\"clusters\"][-1] if state[\"clusters\"] else []\n cluster_table_xml = format_taxonomy(previous_taxonomy)\n\n updated_taxonomy = chain.invoke(\n {\n \"data_xml\": data_table_xml,\n \"use_case\": configurable[\"use_case\"],\n \"cluster_table_xml\": cluster_table_xml,\n \"suggestion_length\": configurable.get(\"suggestion_length\", 30),\n \"cluster_name_length\": configurable.get(\"cluster_name_length\", 10),\n \"cluster_description_length\": configurable.get(\n \"cluster_description_length\", 30\n ),\n \"explanation_length\": configurable.get(\"explanation_length\", 20),\n \"max_num_clusters\": configurable.get(\"max_num_clusters\", 25),\n }\n )\n\n return {\n \"clusters\": [updated_taxonomy[\"clusters\"]],\n }"]
"source": [
"from typing import Dict\n",
"\n",
"from langchain_core.runnables import Runnable\n",
"\n",
"\n",
"def parse_taxa(output_text: str) -> Dict:\n",
" \"\"\"Extract the taxonomy from the generated output.\"\"\"\n",
" cluster_matches = re.findall(\n",
" r\"\\s*<id>(.*?)</id>\\s*<name>(.*?)</name>\\s*<description>(.*?)</description>\\s*\",\n",
" output_text,\n",
" re.DOTALL,\n",
" )\n",
" clusters = [\n",
" {\"id\": id.strip(), \"name\": name.strip(), \"description\": description.strip()}\n",
" for id, name, description in cluster_matches\n",
" ]\n",
" # We don't parse the explanation since it isn't used downstream\n",
" return {\"clusters\": clusters}\n",
"\n",
"\n",
"def format_docs(docs: List[Doc]) -> str:\n",
" xml_table = \"<conversations>\\n\"\n",
" for doc in docs:\n",
" xml_table += f'<conv_summ id={doc[\"id\"]}>{doc[\"summary\"]}</conv_summ>\\n'\n",
" xml_table += \"</conversations>\"\n",
" return xml_table\n",
"\n",
"\n",
"def format_taxonomy(clusters):\n",
" xml = \"<cluster_table>\\n\"\n",
" for label in clusters:\n",
" xml += \" <cluster>\\n\"\n",
" xml += f' <id>{label[\"id\"]}</id>\\n'\n",
" xml += f' <name>{label[\"name\"]}</name>\\n'\n",
" xml += f' <description>{label[\"description\"]}</description>\\n'\n",
" xml += \" </cluster>\\n\"\n",
" xml += \"</cluster_table>\"\n",
" return xml\n",
"\n",
"\n",
"def invoke_taxonomy_chain(\n",
" chain: Runnable,\n",
" state: TaxonomyGenerationState,\n",
" config: RunnableConfig,\n",
" mb_indices: List[int],\n",
") -> TaxonomyGenerationState:\n",
" configurable = config[\"configurable\"]\n",
" docs = state[\"documents\"]\n",
" minibatch = [docs[idx] for idx in mb_indices]\n",
" data_table_xml = format_docs(minibatch)\n",
"\n",
" previous_taxonomy = state[\"clusters\"][-1] if state[\"clusters\"] else []\n",
" cluster_table_xml = format_taxonomy(previous_taxonomy)\n",
"\n",
" updated_taxonomy = chain.invoke(\n",
" {\n",
" \"data_xml\": data_table_xml,\n",
" \"use_case\": configurable[\"use_case\"],\n",
" \"cluster_table_xml\": cluster_table_xml,\n",
" \"suggestion_length\": configurable.get(\"suggestion_length\", 30),\n",
" \"cluster_name_length\": configurable.get(\"cluster_name_length\", 10),\n",
" \"cluster_description_length\": configurable.get(\n",
" \"cluster_description_length\", 30\n",
" ),\n",
" \"explanation_length\": configurable.get(\"explanation_length\", 20),\n",
" \"max_num_clusters\": configurable.get(\"max_num_clusters\", 25),\n",
" }\n",
" )\n",
"\n",
" return {\n",
" \"clusters\": [updated_taxonomy[\"clusters\"]],\n",
" }"
]
},
{
"cell_type": "markdown",
@@ -136,7 +368,34 @@
"id": "553dff30-ce53-47d8-ab3c-d2f437b7d5f4",
"metadata": {},
"outputs": [],
"source": ["# We will share an LLM for each step of the generate -> update -> review cycle\n# You may want to consider using Opus or another more powerful model for this\ntaxonomy_generation_llm = ChatAnthropic(\n model=\"claude-3-haiku-20240307\", max_tokens_to_sample=2000\n)\n\n\n## Initial generation\ntaxonomy_generation_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-generation\").partial(\n use_case=\"Generate the taxonomy that can be used to label the user intent in the conversation.\",\n)\n\ntaxa_gen_llm_chain = (\n taxonomy_generation_prompt | taxonomy_generation_llm | StrOutputParser()\n).with_config(run_name=\"GenerateTaxonomy\")\n\n\ngenerate_taxonomy_chain = taxa_gen_llm_chain | parse_taxa\n\n\ndef generate_taxonomy(\n state: TaxonomyGenerationState, config: RunnableConfig\n) -> TaxonomyGenerationState:\n return invoke_taxonomy_chain(\n generate_taxonomy_chain, state, config, state[\"minibatches\"][0]\n )"]
"source": [
"# We will share an LLM for each step of the generate -> update -> review cycle\n",
"# You may want to consider using Opus or another more powerful model for this\n",
"taxonomy_generation_llm = ChatAnthropic(\n",
" model=\"claude-3-haiku-20240307\", max_tokens_to_sample=2000\n",
")\n",
"\n",
"\n",
"## Initial generation\n",
"taxonomy_generation_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-generation\").partial(\n",
" use_case=\"Generate the taxonomy that can be used to label the user intent in the conversation.\",\n",
")\n",
"\n",
"taxa_gen_llm_chain = (\n",
" taxonomy_generation_prompt | taxonomy_generation_llm | StrOutputParser()\n",
").with_config(run_name=\"GenerateTaxonomy\")\n",
"\n",
"\n",
"generate_taxonomy_chain = taxa_gen_llm_chain | parse_taxa\n",
"\n",
"\n",
"def generate_taxonomy(\n",
" state: TaxonomyGenerationState, config: RunnableConfig\n",
") -> TaxonomyGenerationState:\n",
" return invoke_taxonomy_chain(\n",
" generate_taxonomy_chain, state, config, state[\"minibatches\"][0]\n",
" )"
]
},
{
"cell_type": "markdown",
@@ -154,7 +413,25 @@
"id": "b8739b5b-ba8a-4c40-bd25-a3b06a19949d",
"metadata": {},
"outputs": [],
"source": ["taxonomy_update_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-update\")\n\ntaxa_update_llm_chain = (\n taxonomy_update_prompt | taxonomy_generation_llm | StrOutputParser()\n).with_config(run_name=\"UpdateTaxonomy\")\n\n\nupdate_taxonomy_chain = taxa_update_llm_chain | parse_taxa\n\n\ndef update_taxonomy(\n state: TaxonomyGenerationState, config: RunnableConfig\n) -> TaxonomyGenerationState:\n which_mb = len(state[\"clusters\"]) % len(state[\"minibatches\"])\n return invoke_taxonomy_chain(\n update_taxonomy_chain, state, config, state[\"minibatches\"][which_mb]\n )"]
"source": [
"taxonomy_update_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-update\")\n",
"\n",
"taxa_update_llm_chain = (\n",
" taxonomy_update_prompt | taxonomy_generation_llm | StrOutputParser()\n",
").with_config(run_name=\"UpdateTaxonomy\")\n",
"\n",
"\n",
"update_taxonomy_chain = taxa_update_llm_chain | parse_taxa\n",
"\n",
"\n",
"def update_taxonomy(\n",
" state: TaxonomyGenerationState, config: RunnableConfig\n",
") -> TaxonomyGenerationState:\n",
" which_mb = len(state[\"clusters\"]) % len(state[\"minibatches\"])\n",
" return invoke_taxonomy_chain(\n",
" update_taxonomy_chain, state, config, state[\"minibatches\"][which_mb]\n",
" )"
]
},
{
"cell_type": "markdown",
@@ -172,16 +449,37 @@
"id": "0039cf1c-54d5-4e9e-8dd6-a5cebfaec92d",
"metadata": {},
"outputs": [],
"source": ["taxonomy_review_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-review\")\n\ntaxa_review_llm_chain = (\n taxonomy_review_prompt | taxonomy_generation_llm | StrOutputParser()\n).with_config(run_name=\"ReviewTaxonomy\")\n\n\nreview_taxonomy_chain = taxa_review_llm_chain | parse_taxa\n\n\ndef review_taxonomy(\n state: TaxonomyGenerationState, config: RunnableConfig\n) -> TaxonomyGenerationState:\n batch_size = config[\"configurable\"].get(\"batch_size\", 200)\n original = state[\"documents\"]\n indices = list(range(len(original)))\n random.shuffle(indices)\n return invoke_taxonomy_chain(\n review_taxonomy_chain, state, config, indices[:batch_size]\n )"]
"source": [
"taxonomy_review_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-review\")\n",
"\n",
"taxa_review_llm_chain = (\n",
" taxonomy_review_prompt | taxonomy_generation_llm | StrOutputParser()\n",
").with_config(run_name=\"ReviewTaxonomy\")\n",
"\n",
"\n",
"review_taxonomy_chain = taxa_review_llm_chain | parse_taxa\n",
"\n",
"\n",
"def review_taxonomy(\n",
" state: TaxonomyGenerationState, config: RunnableConfig\n",
") -> TaxonomyGenerationState:\n",
" batch_size = config[\"configurable\"].get(\"batch_size\", 200)\n",
" original = state[\"documents\"]\n",
" indices = list(range(len(original)))\n",
" random.shuffle(indices)\n",
" return invoke_taxonomy_chain(\n",
" review_taxonomy_chain, state, config, indices[:batch_size]\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "ae1d8103-3ecb-458c-8269-1f81c1c6296b",
"metadata": {},
"source": [
"## Define the Graph\n",
"### Compile the Graph\n",
"\n",
"With all the functionality defined, we can define the graph!\n"
"With all the functionality defined, we can build the graph!\n"
]
},
{
@@ -190,7 +488,40 @@
"id": "f1f97ea4-53e5-4f55-8d73-b5b2234a47d9",
"metadata": {},
"outputs": [],
"source": ["from langgraph.graph import StateGraph, START, END\n\ngraph = StateGraph(TaxonomyGenerationState)\ngraph.add_node(\"summarize\", map_reduce_chain)\ngraph.add_node(\"get_minibatches\", get_minibatches)\ngraph.add_node(\"generate_taxonomy\", generate_taxonomy)\ngraph.add_node(\"update_taxonomy\", update_taxonomy)\ngraph.add_node(\"review_taxonomy\", review_taxonomy)\n\ngraph.add_edge(\"summarize\", \"get_minibatches\")\ngraph.add_edge(\"get_minibatches\", \"generate_taxonomy\")\ngraph.add_edge(\"generate_taxonomy\", \"update_taxonomy\")\n\n\ndef should_review(state: TaxonomyGenerationState) -> str:\n num_minibatches = len(state[\"minibatches\"])\n num_revisions = len(state[\"clusters\"])\n if num_revisions < num_minibatches:\n return \"update_taxonomy\"\n return \"review_taxonomy\"\n\n\ngraph.add_conditional_edges(\n \"update_taxonomy\",\n should_review,\n # Optional (but required for the diagram to be drawn correctly below)\n {\"update_taxonomy\": \"update_taxonomy\", \"review_taxonomy\": \"review_taxonomy\"},\n)\ngraph.add_edge(\"review_taxonomy\", END)\n\ngraph.add_edge(START, \"summarize\")\napp = graph.compile()"]
"source": [
"from langgraph.graph import StateGraph, START, END\n",
"\n",
"graph = StateGraph(TaxonomyGenerationState)\n",
"graph.add_node(\"summarize\", map_reduce_chain)\n",
"graph.add_node(\"get_minibatches\", get_minibatches)\n",
"graph.add_node(\"generate_taxonomy\", generate_taxonomy)\n",
"graph.add_node(\"update_taxonomy\", update_taxonomy)\n",
"graph.add_node(\"review_taxonomy\", review_taxonomy)\n",
"\n",
"graph.add_edge(\"summarize\", \"get_minibatches\")\n",
"graph.add_edge(\"get_minibatches\", \"generate_taxonomy\")\n",
"graph.add_edge(\"generate_taxonomy\", \"update_taxonomy\")\n",
"\n",
"\n",
"def should_review(state: TaxonomyGenerationState) -> str:\n",
" num_minibatches = len(state[\"minibatches\"])\n",
" num_revisions = len(state[\"clusters\"])\n",
" if num_revisions < num_minibatches:\n",
" return \"update_taxonomy\"\n",
" return \"review_taxonomy\"\n",
"\n",
"\n",
"graph.add_conditional_edges(\n",
" \"update_taxonomy\",\n",
" should_review,\n",
" # Optional (but required for the diagram to be drawn correctly below)\n",
" {\"update_taxonomy\": \"update_taxonomy\", \"review_taxonomy\": \"review_taxonomy\"},\n",
")\n",
"graph.add_edge(\"review_taxonomy\", END)\n",
"\n",
"graph.add_edge(START, \"summarize\")\n",
"app = graph.compile()"
]
},
{
"cell_type": "code",
@@ -210,14 +541,18 @@
"output_type": "execute_result"
}
],
"source": ["from IPython.display import Image\n\nImage(app.get_graph().draw_png())"]
"source": [
"from IPython.display import Image\n",
"\n",
"Image(app.get_graph().draw_png())"
]
},
{
"cell_type": "markdown",
"id": "e8bdebf5-f315-4e96-80a2-5dde66b327c1",
"metadata": {},
"source": [
"## Usage\n",
"## Use the graph\n",
"\n",
"The docs can contain **any** content, but we've found it works really well on chat bot logs, such as those captured by [LangSmith](https://smith.langchain.com).\n",
"\n",
@@ -232,7 +567,51 @@
"id": "bcc65649-157f-4848-9ef0-8a9932a98d85",
"metadata": {},
"outputs": [],
"source": ["from datetime import datetime, timedelta\n\nfrom langsmith import Client\n\nproject_name = \"YOUR PROJECT NAME\" # Update to your own project\nclient = Client()\n\npast_week = datetime.now() - timedelta(days=7)\nruns = list(\n client.list_runs(\n project_name=project_name,\n filter=\"eq(is_root, true)\",\n start_time=past_week,\n # We only need to return the inputs + outputs\n select=[\"inputs\", \"outputs\"],\n )\n)\n\n\n# Convert the langsmith traces to our graph's Doc object.\ndef run_to_doc(run) -> Doc:\n turns = []\n idx = 0\n for turn in run.inputs.get(\"chat_history\") or []:\n key, value = next(iter(turn.items()))\n turns.append(f\"<{key} idx={idx}>\\n{value}\\n</{key}>\")\n idx += 1\n turns.append(\n f\"\"\"\n<human idx={idx}>\n{run.inputs['question']}\n</human>\"\"\"\n )\n if run.outputs and run.outputs[\"output\"]:\n turns.append(\n f\"\"\"<ai idx={idx+1}>\n{run.outputs['output']}\n</ai>\"\"\"\n )\n return {\n \"id\": str(run.id),\n \"content\": (\"\\n\".join(turns)),\n }"]
"source": [
"from datetime import datetime, timedelta\n",
"\n",
"from langsmith import Client\n",
"\n",
"project_name = \"YOUR PROJECT NAME\" # Update to your own project\n",
"client = Client()\n",
"\n",
"past_week = datetime.now() - timedelta(days=7)\n",
"runs = list(\n",
" client.list_runs(\n",
" project_name=project_name,\n",
" filter=\"eq(is_root, true)\",\n",
" start_time=past_week,\n",
" # We only need to return the inputs + outputs\n",
" select=[\"inputs\", \"outputs\"],\n",
" )\n",
")\n",
"\n",
"\n",
"# Convert the langsmith traces to our graph's Doc object.\n",
"def run_to_doc(run) -> Doc:\n",
" turns = []\n",
" idx = 0\n",
" for turn in run.inputs.get(\"chat_history\") or []:\n",
" key, value = next(iter(turn.items()))\n",
" turns.append(f\"<{key} idx={idx}>\\n{value}\\n</{key}>\")\n",
" idx += 1\n",
" turns.append(\n",
" f\"\"\"\n",
"<human idx={idx}>\n",
"{run.inputs['question']}\n",
"</human>\"\"\"\n",
" )\n",
" if run.outputs and run.outputs[\"output\"]:\n",
" turns.append(\n",
" f\"\"\"<ai idx={idx+1}>\n",
"{run.outputs['output']}\n",
"</ai>\"\"\"\n",
" )\n",
" return {\n",
" \"id\": str(run.id),\n",
" \"content\": (\"\\n\".join(turns)),\n",
" }"
]
},
{
"cell_type": "markdown",
@@ -250,7 +629,15 @@
"id": "900906b5-9264-46a8-ba83-46307f8c25d0",
"metadata": {},
"outputs": [],
"source": ["from langchain.cache import InMemoryCache\nfrom langchain.globals import set_llm_cache\n\n# Optional. If you are running into errors or rate limits and want to avoid repeated computation,\n# you can set this while debugging\n\nset_llm_cache(InMemoryCache())"]
"source": [
"from langchain.cache import InMemoryCache\n",
"from langchain.globals import set_llm_cache\n",
"\n",
"# Optional. If you are running into errors or rate limits and want to avoid repeated computation,\n",
"# you can set this while debugging\n",
"\n",
"set_llm_cache(InMemoryCache())"
]
},
{
"cell_type": "code",
@@ -258,7 +645,39 @@
"id": "c2340177-f40c-407a-8e3e-cb06c2ef09ce",
"metadata": {},
"outputs": [],
"source": ["# We will randomly sample down to 1K docs to speed things up\ndocs = [run_to_doc(run) for run in runs if run.inputs]\ndocs = random.sample(docs, min(len(docs), 1000))\nuse_case = (\n \"Generate the taxonomy that can be used both to label the user intent\"\n \" as well as to identify any required documentation (references, how-tos, etc.)\"\n \" that would benefit the user.\"\n)\n\nstream = app.stream(\n {\"documents\": docs},\n {\n \"configurable\": {\n \"use_case\": use_case,\n # Optional:\n \"batch_size\": 400,\n \"suggestion_length\": 30,\n \"cluster_name_length\": 10,\n \"cluster_description_length\": 30,\n \"explanation_length\": 20,\n \"max_num_clusters\": 25,\n },\n # We batch summarize the docs. To avoid getting errors, we will limit the\n # degree of parallelism to permit.\n \"max_concurrency\": 2,\n },\n)\n\nfor step in stream:\n node, state = next(iter(step.items()))\n print(node, str(state)[:20] + \" ...\")"]
"source": [
"# We will randomly sample down to 1K docs to speed things up\n",
"docs = [run_to_doc(run) for run in runs if run.inputs]\n",
"docs = random.sample(docs, min(len(docs), 1000))\n",
"use_case = (\n",
" \"Generate the taxonomy that can be used both to label the user intent\"\n",
" \" as well as to identify any required documentation (references, how-tos, etc.)\"\n",
" \" that would benefit the user.\"\n",
")\n",
"\n",
"stream = app.stream(\n",
" {\"documents\": docs},\n",
" {\n",
" \"configurable\": {\n",
" \"use_case\": use_case,\n",
" # Optional:\n",
" \"batch_size\": 400,\n",
" \"suggestion_length\": 30,\n",
" \"cluster_name_length\": 10,\n",
" \"cluster_description_length\": 30,\n",
" \"explanation_length\": 20,\n",
" \"max_num_clusters\": 25,\n",
" },\n",
" # We batch summarize the docs. To avoid getting errors, we will limit the\n",
" # degree of parallelism to permit.\n",
" \"max_concurrency\": 2,\n",
" },\n",
")\n",
"\n",
"for step in stream:\n",
" node, state = next(iter(step.items()))\n",
" print(node, str(state)[:20] + \" ...\")"
]
},
{
"cell_type": "markdown",
@@ -318,7 +737,31 @@
"output_type": "execute_result"
}
],
"source": ["from IPython.display import Markdown\n\n\ndef format_taxonomy_md(clusters):\n md = \"## Final Taxonomy\\n\\n\"\n md += \"| ID | Name | Description |\\n\"\n md += \"|----|------|-------------|\\n\"\n\n # Fill the table with cluster data\n for label in clusters:\n id = label[\"id\"]\n name = label[\"name\"].replace(\n \"|\", \"\\\\|\"\n ) # Escape any pipe characters within the content\n description = label[\"description\"].replace(\n \"|\", \"\\\\|\"\n ) # Escape any pipe characters\n md += f\"| {id} | {name} | {description} |\\n\"\n\n return md\n\n\nMarkdown(format_taxonomy_md(step[\"__end__\"][\"clusters\"][-1]))"]
"source": [
"from IPython.display import Markdown\n",
"\n",
"\n",
"def format_taxonomy_md(clusters):\n",
" md = \"## Final Taxonomy\\n\\n\"\n",
" md += \"| ID | Name | Description |\\n\"\n",
" md += \"|----|------|-------------|\\n\"\n",
"\n",
" # Fill the table with cluster data\n",
" for label in clusters:\n",
" id = label[\"id\"]\n",
" name = label[\"name\"].replace(\n",
" \"|\", \"\\\\|\"\n",
" ) # Escape any pipe characters within the content\n",
" description = label[\"description\"].replace(\n",
" \"|\", \"\\\\|\"\n",
" ) # Escape any pipe characters\n",
" md += f\"| {id} | {name} | {description} |\\n\"\n",
"\n",
" return md\n",
"\n",
"\n",
"Markdown(format_taxonomy_md(step[\"__end__\"][\"clusters\"][-1]))"
]
},
{
"cell_type": "markdown",
@@ -348,7 +791,32 @@
"id": "8aa8a6f5-f53a-41e5-b09d-c6e8476e5471",
"metadata": {},
"outputs": [],
"source": ["labeling_prompt = hub.pull(\"wfh/tnt-llm-classify\")\n\nlabeling_llm = ChatAnthropic(model=\"claude-3-haiku-20240307\", max_tokens_to_sample=2000)\nlabeling_llm_chain = (labeling_prompt | labeling_llm | StrOutputParser()).with_config(\n run_name=\"ClassifyDocs\"\n)\n\n\ndef parse_labels(output_text: str) -> Dict:\n \"\"\"Parse the generated labels from the predictions.\"\"\"\n category_matches = re.findall(\n r\"\\s*<category>(.*?)</category>.*\",\n output_text,\n re.DOTALL,\n )\n categories = [{\"category\": category.strip()} for category in category_matches]\n if len(categories) > 1:\n logger.warning(f\"Multiple selected categories: {categories}\")\n label = categories[0]\n stripped = re.sub(r\"^\\d+\\.\\s*\", \"\", label[\"category\"]).strip()\n return {\"category\": stripped}\n\n\nlabeling_chain = labeling_llm_chain | parse_labels"]
"source": [
"labeling_prompt = hub.pull(\"wfh/tnt-llm-classify\")\n",
"\n",
"labeling_llm = ChatAnthropic(model=\"claude-3-haiku-20240307\", max_tokens_to_sample=2000)\n",
"labeling_llm_chain = (labeling_prompt | labeling_llm | StrOutputParser()).with_config(\n",
" run_name=\"ClassifyDocs\"\n",
")\n",
"\n",
"\n",
"def parse_labels(output_text: str) -> Dict:\n",
" \"\"\"Parse the generated labels from the predictions.\"\"\"\n",
" category_matches = re.findall(\n",
" r\"\\s*<category>(.*?)</category>.*\",\n",
" output_text,\n",
" re.DOTALL,\n",
" )\n",
" categories = [{\"category\": category.strip()} for category in category_matches]\n",
" if len(categories) > 1:\n",
" logger.warning(f\"Multiple selected categories: {categories}\")\n",
" label = categories[0]\n",
" stripped = re.sub(r\"^\\d+\\.\\s*\", \"\", label[\"category\"]).strip()\n",
" return {\"category\": stripped}\n",
"\n",
"\n",
"labeling_chain = labeling_llm_chain | parse_labels"
]
},
{
"cell_type": "code",
@@ -356,7 +824,23 @@
"id": "59c06eea-ecbf-43af-a292-71816ccd92b8",
"metadata": {},
"outputs": [],
"source": ["final_taxonomy = step[\"__end__\"][\"clusters\"][-1]\nxml_taxonomy = format_taxonomy(final_taxonomy)\nresults = labeling_chain.batch(\n [\n {\n \"content\": doc[\"content\"],\n \"taxonomy\": xml_taxonomy,\n }\n for doc in docs\n ],\n {\"max_concurrency\": 5},\n return_exceptions=True,\n)\n# Update the docs to include the categories\nupdated_docs = [{**doc, **category} for doc, category in zip(docs, results)]"]
"source": [
"final_taxonomy = step[\"__end__\"][\"clusters\"][-1]\n",
"xml_taxonomy = format_taxonomy(final_taxonomy)\n",
"results = labeling_chain.batch(\n",
" [\n",
" {\n",
" \"content\": doc[\"content\"],\n",
" \"taxonomy\": xml_taxonomy,\n",
" }\n",
" for doc in docs\n",
" ],\n",
" {\"max_concurrency\": 5},\n",
" return_exceptions=True,\n",
")\n",
"# Update the docs to include the categories\n",
"updated_docs = [{**doc, **category} for doc, category in zip(docs, results)]"
]
},
{
"cell_type": "code",
@@ -364,7 +848,10 @@
"id": "0ef9be82-278e-4501-8af9-70409ce15cc2",
"metadata": {},
"outputs": [],
"source": ["if \"OPENAI_API_KEY\" not in os.environ:\n os.environ[\"OPENAI_API_KEY\"] = getpass(\"Enter your OPENAI_API_KEY: \")"]
"source": [
"if \"OPENAI_API_KEY\" not in os.environ:\n",
" os.environ[\"OPENAI_API_KEY\"] = getpass(\"Enter your OPENAI_API_KEY: \")"
]
},
{
"cell_type": "code",
@@ -372,7 +859,14 @@
"id": "c21f787e-2dcb-49c2-9cc1-5284a1732fbc",
"metadata": {},
"outputs": [],
"source": ["from langchain_openai import OpenAIEmbeddings\n\n# Consider using other embedding models here too!\nencoder = OpenAIEmbeddings(model=\"text-embedding-3-large\")\nvectors = encoder.embed_documents([doc[\"content\"] for doc in docs])\nembedded_docs = [{**doc, \"embedding\": v} for doc, v in zip(updated_docs, vectors)]"]
"source": [
"from langchain_openai import OpenAIEmbeddings\n",
"\n",
"# Consider using other embedding models here too!\n",
"encoder = OpenAIEmbeddings(model=\"text-embedding-3-large\")\n",
"vectors = encoder.embed_documents([doc[\"content\"] for doc in docs])\n",
"embedded_docs = [{**doc, \"embedding\": v} for doc, v in zip(updated_docs, vectors)]"
]
},
{
"cell_type": "markdown",
@@ -401,7 +895,51 @@
]
}
],
"source": ["import numpy as np\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.metrics import accuracy_score, f1_score\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils import class_weight\n\n# Create a dictionary mapping category names to their indices in the taxonomy\ncategory_to_index = {d[\"name\"]: i for i, d in enumerate(final_taxonomy)}\ncategory_to_index[\"Other\"] = len(category_to_index)\n# Convert category strings to numeric labels\nlabels = [\n category_to_index.get(d[\"category\"], category_to_index[\"Other\"])\n for d in embedded_docs\n]\n\nlabel_vectors = [d[\"embedding\"] for d in embedded_docs]\n\nX_train, X_test, y_train, y_test = train_test_split(\n label_vectors, labels, test_size=0.2, random_state=42\n)\n\n# Calculate class weights\nclass_weights = class_weight.compute_class_weight(\n class_weight=\"balanced\", classes=np.unique(y_train), y=y_train\n)\nclass_weight_dict = dict(enumerate(class_weights))\n\n# Weight the classes to partially handle imbalanced data\nmodel = LogisticRegression(class_weight=class_weight_dict)\nmodel.fit(X_train, y_train)\n\ntrain_preds = model.predict(X_train)\ntest_preds = model.predict(X_test)\n\ntrain_acc = accuracy_score(y_train, train_preds)\ntest_acc = accuracy_score(y_test, test_preds)\ntrain_f1 = f1_score(y_train, train_preds, average=\"weighted\")\ntest_f1 = f1_score(y_test, test_preds, average=\"weighted\")\n\nprint(f\"Train Accuracy: {train_acc:.3f}\")\nprint(f\"Test Accuracy: {test_acc:.3f}\")\nprint(f\"Train F1 Score: {train_f1:.3f}\")\nprint(f\"Test F1 Score: {test_f1:.3f}\")"]
"source": [
"import numpy as np\n",
"from sklearn.linear_model import LogisticRegression\n",
"from sklearn.metrics import accuracy_score, f1_score\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.utils import class_weight\n",
"\n",
"# Create a dictionary mapping category names to their indices in the taxonomy\n",
"category_to_index = {d[\"name\"]: i for i, d in enumerate(final_taxonomy)}\n",
"category_to_index[\"Other\"] = len(category_to_index)\n",
"# Convert category strings to numeric labels\n",
"labels = [\n",
" category_to_index.get(d[\"category\"], category_to_index[\"Other\"])\n",
" for d in embedded_docs\n",
"]\n",
"\n",
"label_vectors = [d[\"embedding\"] for d in embedded_docs]\n",
"\n",
"X_train, X_test, y_train, y_test = train_test_split(\n",
" label_vectors, labels, test_size=0.2, random_state=42\n",
")\n",
"\n",
"# Calculate class weights\n",
"class_weights = class_weight.compute_class_weight(\n",
" class_weight=\"balanced\", classes=np.unique(y_train), y=y_train\n",
")\n",
"class_weight_dict = dict(enumerate(class_weights))\n",
"\n",
"# Weight the classes to partially handle imbalanced data\n",
"model = LogisticRegression(class_weight=class_weight_dict)\n",
"model.fit(X_train, y_train)\n",
"\n",
"train_preds = model.predict(X_train)\n",
"test_preds = model.predict(X_test)\n",
"\n",
"train_acc = accuracy_score(y_train, train_preds)\n",
"test_acc = accuracy_score(y_test, test_preds)\n",
"train_f1 = f1_score(y_train, train_preds, average=\"weighted\")\n",
"test_f1 = f1_score(y_test, test_preds, average=\"weighted\")\n",
"\n",
"print(f\"Train Accuracy: {train_acc:.3f}\")\n",
"print(f\"Test Accuracy: {test_acc:.3f}\")\n",
"print(f\"Train F1 Score: {train_f1:.3f}\")\n",
"print(f\"Test F1 Score: {test_f1:.3f}\")"
]
},
{
"cell_type": "markdown",
@@ -419,7 +957,15 @@
"id": "c27cbb6b-4d0f-476a-bef3-31ed307ce45f",
"metadata": {},
"outputs": [],
"source": ["from joblib import dump as jl_dump\n\ncategories = list(category_to_index)\n\n# Save the model and categories to a file\nwith open(\"model.joblib\", \"wb\") as file:\n jl_dump((model, categories), file)"]
"source": [
"from joblib import dump as jl_dump\n",
"\n",
"categories = list(category_to_index)\n",
"\n",
"# Save the model and categories to a file\n",
"with open(\"model.joblib\", \"wb\") as file:\n",
" jl_dump((model, categories), file)"
]
},
{
"cell_type": "markdown",
@@ -437,7 +983,24 @@
"id": "28f0b88a-b308-4208-b482-6c157357dfc6",
"metadata": {},
"outputs": [],
"source": ["from joblib import load as jl_load\nfrom langchain_openai import OpenAIEmbeddings\n\nloaded_model, loaded_categories = jl_load(\"model.joblib\")\nencoder = OpenAIEmbeddings(model=\"text-embedding-3-large\")\n\n\ndef get_category_name(predictions):\n return [loaded_categories[pred] for pred in predictions]\n\n\nclassifier = (\n RunnableLambda(encoder.embed_documents, encoder.aembed_documents)\n | loaded_model.predict\n | get_category_name\n)"]
"source": [
"from joblib import load as jl_load\n",
"from langchain_openai import OpenAIEmbeddings\n",
"\n",
"loaded_model, loaded_categories = jl_load(\"model.joblib\")\n",
"encoder = OpenAIEmbeddings(model=\"text-embedding-3-large\")\n",
"\n",
"\n",
"def get_category_name(predictions):\n",
" return [loaded_categories[pred] for pred in predictions]\n",
"\n",
"\n",
"classifier = (\n",
" RunnableLambda(encoder.embed_documents, encoder.aembed_documents)\n",
" | loaded_model.predict\n",
" | get_category_name\n",
")"
]
},
{
"cell_type": "markdown",
@@ -455,7 +1018,22 @@
"id": "6cdb9d8a-2aa1-4f48-8b23-f311fdf36416",
"metadata": {},
"outputs": [],
"source": ["client = Client()\n\npast_5_min = datetime.now() - timedelta(minutes=5)\nruns = list(\n client.list_runs(\n project_name=project_name,\n filter=\"eq(is_root, true)\",\n start_time=past_5_min,\n # We only need to return the inputs + outputs\n select=[\"inputs\", \"outputs\"],\n limit=100,\n )\n)\ndocs = [run_to_doc(r) for r in runs]"]
"source": [
"client = Client()\n",
"\n",
"past_5_min = datetime.now() - timedelta(minutes=5)\n",
"runs = list(\n",
" client.list_runs(\n",
" project_name=project_name,\n",
" filter=\"eq(is_root, true)\",\n",
" start_time=past_5_min,\n",
" # We only need to return the inputs + outputs\n",
" select=[\"inputs\", \"outputs\"],\n",
" limit=100,\n",
" )\n",
")\n",
"docs = [run_to_doc(r) for r in runs]"
]
},
{
"cell_type": "code",
@@ -478,7 +1056,10 @@
]
}
],
"source": ["classes = classifier.invoke([doc[\"content\"] for doc in docs])\nprint(classes[:2])"]
"source": [
"classes = classifier.invoke([doc[\"content\"] for doc in docs])\n",
"print(classes[:2])"
]
},
{
"cell_type": "markdown",
+15 -5
View File
@@ -34,7 +34,7 @@
"\n",
"For this tutorial, we will need to install some dependencies, fetch the Olympiad dataset, and define a utility function to help run the candidate solutions to see if they pass the test cases.\n",
"\n",
"First, install the requirements."
"First, let's install the required packages and set our API keys"
]
},
{
@@ -64,10 +64,20 @@
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_get_env(\"ANTHROPIC_API_KEY\")\n",
"# Recommended\n",
"_get_env(\"LANGCHAIN_API_KEY\")\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\""
"_get_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "10284e28",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
+18 -1
View File
@@ -7,7 +7,11 @@
"source": [
"# How to visualize your graph\n",
"\n",
"This notebook walks through how to visualize the graphs you create. This works with ANY [Graph](https://langchain-ai.github.io/langgraph/reference/graphs/)."
"This notebook walks through how to visualize the graphs you create. This works with ANY [Graph](https://langchain-ai.github.io/langgraph/reference/graphs/).\n",
"\n",
"## Setup\n",
"\n",
"First, let's install the required packages"
]
},
{
@@ -21,6 +25,19 @@
"%pip install -U langgraph"
]
},
{
"cell_type": "markdown",
"id": "c45e18aa",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "e130cf70-a30e-47d7-8fd5-464f1a92e374",
+23 -14
View File
@@ -5,7 +5,7 @@
"id": "f1a7d688-561c-4175-acfc-a6537f6dd042",
"metadata": {},
"source": [
"## Web Voyager\n",
"# Web Voyager\n",
"\n",
"[WebVoyager](https://arxiv.org/abs/2401.13919) by He, et. al., is a vision-enabled web-browsing agent capable of controlling the mouse and keyboard.\n",
"\n",
@@ -20,11 +20,9 @@
"<img src=\"./img/web-voyager.excalidraw.png\" width=\"50%\">\n",
"\n",
"\n",
"## Configure environment\n",
"## Setup\n",
"\n",
"We will first set up LangSmith tracing. Though optional, this lets us inspect and debug agent's trajectory for a given input.\n",
"\n",
"You can sign up at [smith.langchain.com](https://smith.langchain.com/) to get an API key."
"First, let's install our required packages:"
]
},
{
@@ -45,7 +43,6 @@
"metadata": {},
"outputs": [],
"source": [
"# Optional: add tracing to visualize the agent trajectories\n",
"import os\n",
"from getpass import getpass\n",
"\n",
@@ -55,12 +52,22 @@
" os.environ[env_var] = getpass(f\"{env_var}=\")\n",
"\n",
"\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_PROJECT\"] = \"Web-Voyager\"\n",
"_getpass(\"LANGCHAIN_API_KEY\")\n",
"_getpass(\"OPENAI_API_KEY\")"
]
},
{
"cell_type": "markdown",
"id": "8251cc1a",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Set up <a href=\"https://smith.langchain.com\">LangSmith</a> for LangGraph development</p>\n",
" <p style=\"padding-top: 5px;\">\n",
" Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started <a href=\"https://docs.smith.langchain.com\">here</a>. \n",
" </p>\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "15d2e932-e1ce-4f2e-93e9-c8caf44b2afc",
@@ -100,7 +107,9 @@
"id": "a0ee0f97-eb4e-4a13-b4f4-fc6439eec6a6",
"metadata": {},
"source": [
"## Define Graph State\n",
"## Define graph\n",
"\n",
"### Define graph state\n",
"\n",
"The state provides the inputs to each node in the graph.\n",
"\n",
@@ -151,7 +160,7 @@
"id": "8016a06a-3a90-46a4-85d3-510b83dfcef4",
"metadata": {},
"source": [
"## Define tools\n",
"### Define tools\n",
"\n",
"The agent has 6 simple tools:\n",
"\n",
@@ -272,7 +281,7 @@
"id": "ed4d4d9f-9971-477c-b391-1a73dee34573",
"metadata": {},
"source": [
"## Define Agent\n",
"### Define Agent\n",
"\n",
"The agent is driven by a multi-modal model and decides the action to take for each step. It is composed of a few runnable objects:\n",
"\n",
@@ -409,7 +418,7 @@
"id": "7802b9fe-e75b-4779-b45d-003c218dba48",
"metadata": {},
"source": [
"## Define graph\n",
"## Compile the graph\n",
"\n",
"We've created most of the important logic. We have one more function to define that will help us update the graph state after a tool is called."
]
@@ -511,7 +520,7 @@
"id": "1d11071f-f7ad-434d-99b7-14ebbbc92506",
"metadata": {},
"source": [
"## Run agent\n",
"## Use the graph\n",
"\n",
"Now that we've created the whole agent executor, we can run it on a few questions! We'll start our browser at \"google.com\" and then let it control the rest.\n",
"\n",