add links from concepts guide (#825)

This commit is contained in:
Harrison Chase
2024-06-25 22:07:06 -07:00
committed by GitHub
parent 720ea986d3
commit fcdf7a8ced
3 changed files with 50 additions and 25 deletions
+16 -2
View File
@@ -18,6 +18,8 @@ Memory is a key concept to agentic applications. Memory is important because end
LangGraph is perfectly suited to give you full control over the memory of your application. With user defined [`State`](#state) you can specify the exact schema of the memory you want to retain. With [checkpointers](#checkpointer) you can store checkpoints of previous interactions and resume from there in follow up interactions.
See [this guide](/langgraph/how-tos/persistence/) for how to add memory to your graph.
## Human-in-the-loop
Agentic systems often require some human-in-the-loop (or "on-the-loop") interaction patterns. This is because agentic systems are still not super reliable, so having a human involved is required for any sensitive tasks/actions. These are all easily enabled in LangGraph, largely due to [checkpointers](#checkpointer). The reason a checkpointer is necessary is that a lot of these interaction patterns involve running a graph up until a certain point, waiting for some sort of human feedback, and then continuing. When you want to "continue" you will need to access the state of the graph previous to getting interrupted, and checkpointers are a built in, highly convenient way to do that.
@@ -28,6 +30,8 @@ There are a few common human-in-the-loop interaction patterns we see emerging.
A basic one is to have the agent wait for approval before executing certain tools. This may be all tools, or just a subset of tools. This is generally recommend for more sensitive actions (like writing to a database). This can easily be done in LangGraph by setting a [breakpoint](#breakpoints) before specific nodes.
See [this guide](/langgraph/how-tos/human_in_the_loop/breakpoints) for how do this in LangGraph.
### Wait for input
A similar one is to have the agent wait for human input. This can be done by:
@@ -38,21 +42,29 @@ A similar one is to have the agent wait for human input. This can be done by:
4. Update the state with that user input, acting as that node
5. Resume execution
See [this guide](/langgraph/how-tos/human_in_the_loop/wait-user-input) for how do this in LangGraph.
### Edit agent actions
This is a more advanced interaction pattern. In this interaction pattern the human can actually edit some of the agent's previous decisions. This can be done either during the flow (after a [breakpoint](#breakpoints), part of the [approval](#approval) flow) or after the fact (as part of [time-travel](#time-travel))
See [this guide](/langgraph/how-tos/human_in_the_loop/edit-graph-state) for how do this in LangGraph.
### Time travel
This is a pretty advanced interaction pattern. In this interaction pattern, the human can look back at the list of previous checkpoints, find one they like, optionally [edit it](#edit-agent-actions), and then resume execution from there.
See [this guide](/langgraph/how-tos/human_in_the_loop/time-travel) for how to do this in LangGraph.
## Map-Reduce
A common pattern in agents is to generate a list of objects, do some work on each of those objects, and then combine the results. This is very similar to the common [map-reduce](https://en.wikipedia.org/wiki/MapReduce) operation. This can be tricky for a few reasons. First, it can be tough to define a structured graph ahead of time because the length of the list of objects may be unknown. Second, in order to do this map-reduce you need multiple versions of the state to exist... but the graph shares a common shared state, so how can this be?
LangGraph supports this via the [Send](#send) api. This can be used to allow a conditional edge to Send multiple different states to multiple nodes. The state it sends can be different from the state of the core graph.
See a how-to guide for this [here](https://langchain-ai.github.io/langgraph/how-tos/map-reduce/)
See a how-to guide for this [here](/langgraph/how-tos/map-reduce)
## Multi-agent
@@ -76,7 +88,7 @@ This "reflection" step often uses an LLM, but doesn't have to. A good example of
One of the most common agent architectures is what is commonly called the ReAct agent architecture. In this architecture, an LLM is called repeatedly in a while-loop. At each step the agent decides which tools to call, and what the inputs to those tools should be. Those tools are then executed, and the outputs are fed back into the LLM as observations. The while-loop terminates when the agent decides it is not worth calling any more tools.
One of the few high level, pre-built agents we have in LangGraph - you can use it with [`create_react_agent`](https://langchain-ai.github.io/langgraph/reference/prebuilt/#create_react_agent)
One of the few high level, pre-built agents we have in LangGraph - you can use it with [`create_react_agent`](/langgraph/reference/prebuilt#create_react_agent)
This is named after and based on the [ReAct](https://arxiv.org/abs/2210.03629) paper. However, there are several differences between this paper and our implementation:
@@ -85,3 +97,5 @@ This is named after and based on the [ReAct](https://arxiv.org/abs/2210.03629) p
- Third, the paper required all inputs to the tools to be a single string. This was largely due to LLMs not being super capable at the time, and only really being able to generate a single input. Our implementation allows for using tools that require multiple inputs.
- Forth, the paper only looks at calling a single tool at the time, largely due to limitations in LLMs performance at the time. Our implementation allows for calling multiple tools at a time.
- Finally, the paper asked the LLM to explicitly generate a "Thought" step before deciding which tools to call. This is the "Reasoning" part of "ReAct". Our implementation does not do this by default, largely because LLMs have gotten much better and that is not as necessary. Of course, if you wish to prompt it do so, you certainly can.
See [this guide](/langgraph/how-tos/human_in_the_loop/time-travel) for a full walkthrough of how to use the prebuilt ReAct agent.
+7 -7
View File
@@ -17,18 +17,18 @@ If these decisions are being made in a loop, then its even more agentic!
There are other concepts often associated with being agentic, but we would argue these are a by-product of the above definition:
- Tool calling: this is often how LLMs make decisions
- [Tool calling](/langgraph/concepts/agentic_concepts/#tool-calling): this is often how LLMs make decisions
- Action taking: often times, the LLMs' outputs are used as the input to an action
- Memory: reliable systems need to have knowledge of things that occurred
- Planning: planning steps (either explicit or implicit) are useful for ensuring that the LLM, when making decisions, makes them in the highest fidelity way.
- [Memory](/langgraph/concepts/agentic_concepts/#memory): reliable systems need to have knowledge of things that occurred
- [Planning](/langgraph/concepts/agentic_concepts/#planning): planning steps (either explicit or implicit) are useful for ensuring that the LLM, when making decisions, makes them in the highest fidelity way.
## Why LangGraph?
LangGraph has several core principles that we believe make it the most suitable framework for building agentic applications:
- Controllability
- Human-in-the-Loop
- Streaming First
- [Controllability](/langgraph/how-tos/#controllability)
- [Human-in-the-Loop](/langgraph/how-tos/#human-in-the-loop)
- [Streaming First](/langgraph/how-tos/#streaming)
**Controllability**
@@ -40,7 +40,7 @@ LangGraph comes with a built-in persistence layer as a first-class concept. This
**Streaming First**
LangGraph comes with first class support for streaming. Agentic applications often take a while to run, and so giving the user some idea of what is happening is important, and streaming is a great way to do that. LangGraph supports streaming of both events (like a tool call being taken) as well as of tokens that an LLM may emit.
LangGraph comes with first class support for streaming. Agentic applications often take a while to run, and so giving the user some idea of what is happening is important, and streaming is a great way to do that. LangGraph supports streaming of both events ([like a tool call being taken](/langgraph/how-tos/stream-updates/)) as well as of [tokens that an LLM may emit](/langgraph/how-tos/streaming-tokens/).
## Deployment
+27 -16
View File
@@ -4,11 +4,11 @@
At its core, LangGraph models agent workflows as graphs. You define the behavior of your agents using three key components:
1. `State`: A shared data structure that represents the current snapshot of your application. It can be any Python type, but is typically a `TypedDict` or Pydantic `BaseModel`.
1. [`State`](#state): A shared data structure that represents the current snapshot of your application. It can be any Python type, but is typically a `TypedDict` or Pydantic `BaseModel`.
2. `Nodes`: Python functions that encode the logic of your agents. They receive the current `State` as input, perform some computation or side-effect, and return an updated `State`.
2. [`Nodes`](#nodes): Python functions that encode the logic of your agents. They receive the current `State` as input, perform some computation or side-effect, and return an updated `State`.
3. `Edges`: Python functions that determine which `Node` to execute next based on the current `State`. They can be conditional branches or fixed transitions.
3. [`Edges`](#edges): Python functions that determine which `Node` to execute next based on the current `State`. They can be conditional branches or fixed transitions.
By composing `Nodes` and `Edges`, you can create complex, looping workflows that evolve the `State` over time. The real power, though, comes from how LangGraph manages that `State`. To emphasize: `Nodes` and `Edges` are nothing more than Python functions - they can contain an LLM or just good ol' Python code.
@@ -38,11 +38,11 @@ You **MUST** compile your graph before you can use it.
## State
The first thing you do when you define a graph is define the `State` of the graph. The `State` consists of the schema of graph as well as `reducer` functions which specify how to apply updates to the state. The schema of the `State` will be the input schema to all `Nodes` and `Edges` in the graph, and can be either a `TypedDict` or a `Pydantic` model. All `Nodes` will emit updates to the `State` which are then applied using the specified `reducer` function.
The first thing you do when you define a graph is define the `State` of the graph. The `State` consists of the [schema of the graph](#schema) as well as [`reducer` functions](#reducers) which specify how to apply updates to the state. The schema of the `State` will be the input schema to all `Nodes` and `Edges` in the graph, and can be either a `TypedDict` or a `Pydantic` model. All `Nodes` will emit updates to the `State` which are then applied using the specified `reducer` function.
### Schema
The main documented way to specify the schema of a graph is by using `TypedDict`. However, we also support [using a Pydantic BaseModel](https://langchain-ai.github.io/langgraph/how-tos/state-model/) as your graph state to add **default values** and additional data validation.
The main documented way to specify the schema of a graph is by using `TypedDict`. However, we also support [using a Pydantic BaseModel](/langgraph/how-tos/state-model/) as your graph state to add **default values** and additional data validation.
### Reducers
@@ -103,7 +103,7 @@ class State(MessagesState):
In LangGraph, nodes are typically python functions (sync or `async`) where the **first** positional argument is the [state](#state), and (optionally), the **second** positional argument is a "config", containing optional [configurable parameters](#configuration) (such as a `thread_id`).
Similar to `NetworkX`, you add these nodes to a graph using the [add_node](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.MessageGraph) method:
Similar to `NetworkX`, you add these nodes to a graph using the [add_node](/langgraph/reference/graphs#langgraph.graph.MessageGraph) method:
```python
from langchain_core.runnables import RunnableConfig
@@ -170,7 +170,7 @@ A node can have MULTIPLE outgoing edges. If a node has multiple out-going edges,
### Normal Edges
If you **always** want to go from node A to node B, you can use the [add_edge](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.StateGraph.add_edge) method directly.
If you **always** want to go from node A to node B, you can use the [add_edge](/langgraph/reference/graphs#langgraph.graph.StateGraph.add_edge) method directly.
```python
graph.add_edge("node_a", "node_b")
@@ -178,7 +178,7 @@ graph.add_edge("node_a", "node_b")
### Conditional Edges
If you want to **optionally** route to 1 or more edges (or optionally terminate), you can use the [add_conditional_edges](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.StateGraph.add_conditional_edges) method. This method accepts the name of a node and a "routing function" to call after that node is executed:
If you want to **optionally** route to 1 or more edges (or optionally terminate), you can use the [add_conditional_edges](/langgraph/reference/graphs#langgraph.graph.StateGraph.add_conditional_edges) method. This method accepts the name of a node and a "routing function" to call after that node is executed:
```python
graph.add_edge("node_a", routing_function)
@@ -196,7 +196,7 @@ graph.add_edge("node_a", routing_function, {True: "node_b", False: "node_c"})
### Entry Point
The entry point is first node to call when the graph starts.
The entry point is first node to call when the graph starts. You can use [`set_entry_point`](/langgraph/reference/graphs#langgraph.graph.StateGraph.set_entry_point) to specify this.
```python
graph.set_entry_point("node_a")
@@ -213,6 +213,7 @@ graph.add_edge(START, "node_a")
### Conditional Entry Point
The conditional entry point is used when you want to specify a function to call to determine which node(s) should be called first.
You can use [`set_conditional_entry_point`](/langgraph/reference/graphs#langgraph.graph.StateGraph.set_conditional_entry_point) to specify this.
```python
graph.set_conditional_entry_point(routing_function)
@@ -228,7 +229,7 @@ graph.set_conditional_entry_point(routing_function, {True: "node_b", False: "nod
By default, `Nodes` and `Edges` are defined ahead of time and operate on the same shared state. However, there can be cases where the exact edges are not known ahead of time and/or you may want different versions of `State` to exist at the same time. A common of example of this is with `map-reduce` design patterns. In this design pattern, a first node may generate a list of objects, and you may want to apply some other node to all those objects. The number of objects may be unknown ahead of time (meaning the number of edges may not be known) and the input `State` to the downstream `Node` should be different (one for each generated object).
To support this design pattern, LangGraph supports returning [`Send`](https://langchain-ai.github.io/langgraph/reference/graphs/#send) objects from conditional edges. `Send` takes two arguments: first is the name of the node, and second is the state to pass to that node.
To support this design pattern, LangGraph supports returning [`Send`](/langgraph/reference/graphs#send) objects from conditional edges. `Send` takes two arguments: first is the name of the node, and second is the state to pass to that node.
```python
def continue_to_jokes(state: OverallState):
@@ -239,13 +240,15 @@ graph.add_conditional_edges("node_a", continue_to_jokes)
## Checkpointer
One of the main benefits of LangGraph is that it comes backed by a persistence layer. This is accomplished via [checkpointers](https://langchain-ai.github.io/langgraph/reference/checkpoints/#basecheckpointsaver).
One of the main benefits of LangGraph is that it comes backed by a persistence layer. This is accomplished via [checkpointers](/langgraph/reference/checkpoints#basecheckpointsaver).
Checkpointers can be used to save a _checkpoint_ of the state of a graph after all steps of the graph. This allows for several things.
First, it allows for human-in-the-loop workflows, as it allows humans to inspect, interrupt, and approve steps. Checkpointers are needed for these workflows as the human has to be able to view the state of a graph at any point in time, and the graph has to be to resume execution after the human has made any updates to the state.
First, it allows for [human-in-the-loop workflows](agentic_concepts#human-in-the-loop), as it allows humans to inspect, interrupt, and approve steps. Checkpointers are needed for these workflows as the human has to be able to view the state of a graph at any point in time, and the graph has to be to resume execution after the human has made any updates to the state.
Second, it allows for "memory" between interactions. You can use checkpointers to create threads and save the state of a thread after a graph executes. In the case of repeated human interactions (like conversations) any follow up messages can be sent to that checkpoint, which will retain its memory of previous ones.
Second, it allows for ["memory"](agentic_concepts#memory) between interactions. You can use checkpointers to create threads and save the state of a thread after a graph executes. In the case of repeated human interactions (like conversations) any follow up messages can be sent to that checkpoint, which will retain its memory of previous ones.
See [this guide](/langgraph/how-tos/persistence) for how to add a checkpointer to your graph.
## Threads
@@ -263,6 +266,8 @@ config = {"configurable": {"thread_id": "a"}}
graph.invoke(inputs, config=config)
```
See [this guide](/langgraph/how-tos/persistence) for how to use threads.
## Checkpointer state
When you use a checkpointer with a graph, you can interact with the state of that graph.
@@ -365,6 +370,8 @@ def node_a(state, config):
...
```
See [this guide](/langgraph/how-tos/configuration) for a full breakdown on configuration
## Breakpoints
It can often be useful to set breakpoints before or after certain nodes execute. This can be used to wait for human approval before continuing. These can be set when you ["compile" a graph](#compiling-your-graph). You can set breakpoints either *before* a node executes (using `interrupt_before`) or after a node executes (using `interrupt_after`.)
@@ -381,17 +388,21 @@ graph.invoke(inputs, config=config)
graph.invoke(None, config=config)
```
See [this guide](/langgraph/how-tos/human_in_the_loop/breakpoints) for a full walkthrough of how to add breakpoints.
## Visualization
It's often nice to be able to visualize graphs, especially as they get more complex. LangGraph comes with several built-in ways to visualize graphs. See [this how-to guide](https://langchain-ai.github.io/langgraph/how-tos/visualization/) for more info.
See [this guide](/langgraph/how-tos/visualization) for how to visualize your graph.
## Streaming
LangGraph is built with first class support for streaming. There are several different streaming modes that LangGraph supports:
- `"values"`: This streams the full value of the state after each step of the graph.
- `"updates`: This streams the updates to the state after each step of the graph. If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are streamed separately.
- [`"values"`](/langgraph/how-tos/stream-values): This streams the full value of the state after each step of the graph.
- [`"updates`](/langgraph/how-tos/stream-updates): This streams the updates to the state after each step of the graph. If multiple updates are made in the same step (e.g. multiple nodes are run) then those updates are streamed separately.
- `"debug"`: This streams as much information as possible throughout the execution of the graph.
In addition, you can use the [`astream_events`](https://langchain-ai.github.io/langgraph/how-tos/streaming-tokens/) method to stream back events that happen _inside_ nodes. This is useful for streaming tokens of LLM calls.
In addition, you can use the [`astream_events`](https://langchain-ai.github.io/langgraph/how-tos/streaming-tokens/) method to stream back events that happen _inside_ nodes. This is useful for [streaming tokens of LLM calls](/langgraph/how-tos/streaming-tokens).