- BaseCache interface defines the base class for cache storage adapters
- FileCache implements BaseCache with filesystem-backed storage
- Provide default cache key implementation which hashes args with pickle
- Update PregelExecutableTask with cache_key property for tasks that
opt-in to caching
- Update PregelLoop, PregelRunner to get/set from cache as appropriate
TODO
- [x] Call match_cached_writes in async PregelRunner
- [ ] Implement RedisCache to use in LGP
- [x] Add more tests
Because we were propagating the task ID config key, the stream mode was
always overridden as "values", meaning the token, etc. callback handlers
were never added within the remote graphs.
---------
Signed-off-by: William Fu-Hinthorn <13333726+hinthornw@users.noreply.github.com>
I was having trouble following this section of the docs and I realized
it is because the incorrect tools is included in the tools list.
`update_user_info` was defined earlier in the file but never used.
Also moving over any logic from `langchain-core` to here as we slowly
drop `langchain-core` dependency.
Pydantic v1 is no longer undergoing active maintenance and v2 has been
out for almost 2 years, so it seems like an appropriate time to drop v1
scar tissue.
Manual pass to apply a few heuristics:
* Boost conceptual pages
* Deboost (is that a word?) index pages that list all content
* Prefer Agents pages if search query contains the word "agent"
* Add tags for a few selected pages
* Adding support for mapping interrupt ids -> resume values with the
`Command.resume_map` argument, like:
```py
resume_map = {
i.interrupt_id: f"human input for prompt {i.value}"
for i in parent_graph.get_state(thread_config).interrupts
}
parent_graph.invoke(Command(resume=resume_map), config=thread_config)
```
* Adds an `interrupts` attribute on `StateSnapshot` so that we can
access that directly rather than having to do
`get_state(thread_config).tasks` and then iterate over tasks to find
interrupts
* Deprecates undocumented feature where (if interrupting a graph from
the level of an interrupt), you could pass a dict mapping task ids ->
resume values. Now we recommend and endorse the `interrupt_id` approach
above.
I'll note, from an internal perspective, I would love if we didn't have
to pass around this map, but it seems like the best way right now to
make the necessary resume information necessary at different levels in a
graph with subgraphs.
Fix https://github.com/langchain-ai/langgraph/issues/4028
Slotted to be included in our v0.4.0 release early next week!
A few notes:
* We shouldn't be using `tool.poetry.dependencies`, that's deprecated -
waiting for a future PR to address this big change though.
* We should remove upper bounds for all deps unless strictly necessary.
We want to deprecate `TavilySearchResults` in langchain-community in
favor of `TavilySearch` in langchain-tavily.
Also update quickstart to use `init_chat_model`.
This PR does a few things:
1. Surfaces interrupts when `stream_mode='values'` (particularly
relevant for `invoke`, where this is the default behavior)
2. Adds an `interrupt_id` property to the `Interrupt` dataclass so that
interrupts can effectively be mapped to resumes
3. Minor docs updates to reflect the new pattern (no need for a special
section on interrupts with `invoke` and `ainvoke`)
* In a different PR (the one with the multiple resume values), as it's
more relevant there: add an `interrupts` property to `StateSnapshot` so
that `interrupts` can easily be iterated over if users are attempting to
map interrupts to resumes.
I **don't** recommend we release this until we have multi-resumes
working.
## Example
We have the following setup where we're sending multiple prompts to the
child graph, which uses `interrupt`:
```py
def child_graph(state):
human_input = interrupt(state["prompt"])
return {
"human_inputs": [human_input],
}
```
<img width="142" alt="Screenshot 2025-04-23 at 10 01 12 AM"
src="https://github.com/user-attachments/assets/c6238bf1-54ad-4e48-ab0b-60a0bfc18485"
/>
Old behavior:
```py
initial_input = {"prompts": ["a", "b"]}
print(parent_graph.invoke(input=initial_input,config=thread_config,stream_mode="values"))
#> {'prompts': ['a', 'b'], 'human_inputs': []}
print(parent_graph.invoke(Command(resume="hello 1"),config=thread_config,stream_mode="values"))
#> {'prompts': ['a', 'b'], 'human_inputs': ['hello 1']}
print(parent_graph.invoke(Command(resume="hello 2"),config=thread_config,stream_mode="values"))
#> {'prompts': ['a', 'b'], 'human_inputs': ['hello 1', 'hello 2']}
```
New behavior:
```py
initial_input = {"prompts": ["a", "b"]}
print(parent_graph.invoke(input=initial_input,config=thread_config,stream_mode="values"))
"""
{
"prompts": ["a", "b"],
"human_inputs": [],
"__interrupt__": [
Interrupt(
value="a",
resumable=True,
ns=["child_graph:38d43a18-a5e7-8ab2-ca83-9d80f6e9ca83"]
),
Interrupt(
value="b",
resumable=True,
ns=["child_graph:dad810e8-738e-9f90-41cd-30c0091eb79b"]
)
]
}
"""
print(parent_graph.invoke(Command(resume="hello 1"),config=thread_config,stream_mode="values"))
"""
{
"prompts": ["a", "b"],
"human_inputs": ["hello 1"],
"__interrupt__": [
Interrupt(
value="b",
resumable=True,
ns=["child_graph:dad810e8-738e-9f90-41cd-30c0091eb79b"]
)
]
}
"""
print(parent_graph.invoke(Command(resume="hello 2"),config=thread_config,stream_mode="values"))
#> {'prompts': ['a', 'b'], 'human_inputs': ['hello 1', 'hello 2']}
```
Using this argument, you can get more customization since you can do
`langgraph build` or directly `docker build` your image and then re-use
the `langgraph up --image my-image` and have it also spin up redis &
postgres for you.
Easier then writing your own compose file
- It now executes the same pregel algo as when the graph is executed
(without running any user code in nodes or conditional edges) to
discover all the edges
- This means we now support drawing the graph for all Pregel instances,
not just StateGraph
- This is done in preparation for new edge/node type coming in separate
PR
- Known changes
- custom labels on conditional edges to END are no longer displayed
- It now executes the same pregel algo as when the graph is executed (without running any user code in nodes or conditional edges) to discover all the edges
- This means we now support drawing the graph for all Pregel instances, not just StateGraph
If the state schema uses validators, skip the model construct
optimization.
For context, pydantic state can be significantly slower to run than
typed dict and dataclass states due to the full recursive validation.
We have some optimizations to reduce the impact of this (using cached
validators with model_construct), but this doesn't handle things like
field_validator.
We prefer correctness over performance, obviously.
Resolves: https://github.com/langchain-ai/langgraph/issues/4074
Signed-off-by: William Fu-Hinthorn <13333726+hinthornw@users.noreply.github.com>
The configuration expects the key "fields", not "text_fields": I had
failed to update across all implementations in the original PR
Thank you to Vincent Min for the fix!
---------
Co-authored-by: Vincent Min <93780551+VMinB12@users.noreply.github.com>
- Deletes all data associated with a thread_id
- Implemented in InMemory, Sqlite and Postgres checkpointers
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
- attached to every node to handle command/send return values
- used to be a full blown conditional edge, can be simpler by doing all
of it in a single function
- attached to every node to handle command/send return values
- used to be a full blown conditional edge, can be simpler by doing all of it in a single function
- Uses new `install-node.sh` script already used for Python Gen UI
- Add default `node_version` / `python_version` based on provided
`graphs`
Closes#4115
- This provides a new mode of execution where only the last checkpoint
is saved
- We save the last checkpoint no matter how the agent run is terminated
(success, error, interrupt, etc)
- This cuts down on cpu time spent on checkpointing, while not losing
any resilience benefits, given individual task writes are still saved
- If an error occurs and the run is retried, any tasks that completed
successfully before will be skipped (as currently)
- checkpoint_during=True is useful when you want to time-travel to inner
steps of a run
- The default value will remain the current behavior, ie.
checkpoint_during=True
If you're deploying with langgraph API, you don't need to manually
define a checkpointer. For folks who already know they'll be developing
with the api server, I'd like to save everyone time by making this more
clear in the docs on checkpointing.
- This provides a new mode of execution where only the last checkpoint is saved
- We save the last checkpoint no matter how the agent run is terminated (success, error, interrupt, etc)
- This cuts down on cpu time spent on checkpointing, while not losing any resilience benefits, given individual task writes are still saved
- If an error occurs and the run is retried, any tasks that completed successfully before will be skipped (as currently)
- checkpoint_during=True is useful when you want to time-travel to inner steps of a run
- The default value will remain the current behavior, ie. checkpoint_during=True
…entation.
This commit fixes a syntax error in the "langchain-ai.github"
documentation. The function called "some_node_inside_alice" was missing
a colon (:) after the function, which is required for valid Python
syntax.
### Summary
This is a large refactor of the content for the LangGraph Platform
deployment options. Although there are a lot of changes, I do feel
fairly confident that this is safe to merge and won't have any negative
impact related to confusion around deployment options. However, please
review thoroughly (i.e. run the docs locally).
### Goals and Non-Goals
Just wanted to explicitly state goals and non-goals so that we're clear
about what needs to be done now versus what can be done in a smaller
follow-up PR.
Goals
1. Add new content for the new deployment options (Self-Hosted Data
Plane, Self-Hosted Control Plane).
1. Hide old content for deprecated deployment options (BYOC).
1. Create a pair of "conceptual" and "how-to" pages for each deployment
option. As much as possible, the pages should have consistent headings.
1. Introduce the terms "control plane" and "data plane" and define them
plainly without hiding/abstracting information.
Non-Goals
1. Do not change the navigation of the existing deployment options. As
much as possible, update content in-place or add new pages. Changing the
navigation is a bigger task that can be done later.
1. Do not remove old content for deprecated deployment options. We may
need to refer to this later. There are only ~2 pages (I think).
### Next Steps
1. Update the architecture diagrams for each deployment option. Commit
Excalidraw file to source control.
1. Create a "how-to" page for the Control Plane UI. This page pertains
to 3/4 deployment options. Most of the content lives in the "how-to"
page for Cloud SaaS deployment.
1. Document required RBAC permissions for K8s for Self-Hosted Data Plane
and Self-Hosted Control Plane (and update links).
1. Figure out how to consolidate plan information.
1. Figure out where to document licensing, telemetry, custom
Postgres/Redis.
1. Update autoscaling content.
- When checkpointing is disabled don't call create_checkpoint in
PregelLoop
- In local_read apply writes directly to copies of updated channels
- Add BaseChannel.copy() method to create channel copies with less
overhead
- When checkpointing is disabled don't call create_checkpoint in PregelLoop
- In local_read apply writes directly to copies of updated channels
- Add BaseChannel.copy() method to create channel copies with less overhead
- This mirrors the work done earlier on BaseChannel.get()
- Comparing to a sentinel value is significantly faster than raising and
catching an exception
- This mirrors the work done earlier on BaseChannel.get()
- Comparing to a sentinel value is significantly faster than raising and catching an exception
- If the value to serialize is None we can use encode it in the string
type, and skip msgpack encoding
- Use None value for edge/branch channels in StateGraph
### Summary
1. Update API spec.
2. Clarify how to specify `requirements.txt` in `dependencies` list.
3. Clarify deletion policy for database.
4. Clarify resource allocation for `Production` type deployments.
5. Update supported Python versions.
- Used to be 2 channels per node, it is now one per node, which is the
minimum
- Now both hard edges, conditional edges, entrypoint and conditional
entrypoint all use the same channel to trigger a node
- Used to be 2 channels per node, it is now one per node, which is the minimum
- Now both hard edges, conditional edges, entrypoint and conditional entrypoint all use the same channel to trigger a node
- Used to be 2 channels per node, it is now one per node, which is the minimum
- Now both hard edges, conditional edges, entrypoint and conditional entrypoint all use the same channel to trigger a node
This PR fixes the return type annotation of the `update` method from
`None` to `bool`, as the method returns a boolean value indicating
whether self.values has changed
Co-authored-by: kakaogames <kakaogames@Justin-MacBook-Pro.local>
This pull request includes changes to add version admonitions to the
documentation and update the styling for these admonitions. The most
important changes include the addition of version information to the
documentation, updates to the CSS for version admonitions, and
modifications to the `mkdocs.yml` configuration file to include the new
stylesheets.
this should solve this #3991
---------
Co-authored-by: Eugene Yurtsev <eugene@langchain.dev>
This PR only allows this in the HTTP client.
I can follow up with a PR to allow throughout the entire API.
The use case is to allow instantiating the client once (w/ a single connection pool), but allowing changing api keys and any other headers at run time
- Added a validator to sanitize 'name' fields and prevent
string_pattern_mismatch errors.
- Replaced deprecated `dict` method with `model_dump` in line with
Pydantic v2.0 migration guidelines.
- Updated gen_perspectives_chain and gen_queries_chain to ensure
compatibility with structured output and include raw data where needed.
This allows use of fast_llm across the notebook.
---------
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
**Description:**
Make AsyncSqliteSaver examples workable.
**Issue:**
For "Usage within StateGraph" example,
SyntaxError: 'async with' outside async function
For "Raw usage" example
KeyError: 'checkpoint_ns' and KeyError: 'id'
**Dependencies:**
N/A
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
- This makes our checkpoint benchmarks more closely resemble the
behavior of our prod checkpointers
- Also found and fixed a bug w multiple subgraphs in same node
accidentally sharing checkpoints
- This makes our checkpoint benchmarks more closely resemble the behavior of our prod checkpointers
- Also found and fixed a bug w multiple subgraphs in same node accidentally sharing checkpoints
- When there are no values in checkpoint no need to run through all the
PULL candidates
- When there are input writes save updated_channels to use on the next
call to prepare_next_tasks
- RunnableCallable: Skip signature checks for internal callables where
we know the signatures ahead of time
- PregelNode: Avoid redoing subgraphs serarch when copying it
- CompiledStateGraph: Avoid copying PregelNode when attaching writers
- RunnableCallable: Skip signature checks for internal callables where we know the signatures ahead of time
- PregelNode: Avoid redoing subgraphs serarch when copying it
- CompiledStateGraph: Avoid copying PregelNode when attaching writers
- When there are no values in checkpoint no need to run through all the PULL candidates
- When there are input writes save updated_channels to use on the next call to prepare_next_tasks
Leverage information about which channels were updated in the previous
step to determine which tasks should be triggered. This can result in
significant speed up in prepare_next_tasks in some situations.
Includes:
- Explicit unsetting of runnable context var
- Weakref for PregelExecutableTask
both to reduce the chance of keeping a reference to an internal object
and preventing garbage collection
## Description
The documentation for working with Pydantic and graph State recommends
to use `AnyMessage` when working with LangChain types, but the code
example uses `BaseMessage`.
Includes:
- Explicit unsetting of runnable context var
- Weakref for PregelExecutableTask
both to reduce the chance of keeping a reference to an internal object and preventing
garbage collection
This method is useful for recreating a thread from a list of checkpoint writes. A new method is needed to clone a checkpoint that has been created from multiple writes (functional API, map-reduce)
Port of https://github.com/langchain-ai/langgraphjs/pull/969
Current text in the doc is incorrect:
```
Use the search tool to ask the user where they are, then look up the weather there
```
The search tool is not the one to use. Instead should just tell the
model to ask the user.
In addition, an important step is missing and makes the code seem less
impactful:
```python
location = interrupt("Please provide your location:")
```
The question to ask the human is actually coming from the LLM, there is
no need to hardcode it:
```python
...
location = interrupt(ask.question)
```
Before merging, someone who validates this should push an update to cell
outputs. I cleared it out from my branch because it made too many
updates to the file and would make it harder to review.
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
This pull request corrects a couple of typographical errors in the
documentation.
---------
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
Co-authored-by: Vadym Barda <vadym@langchain.dev>
* Document that `check_same_thread` as an option when creating sqlite
connection.
* Document why it's OK to do that.
---------
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
- sequential(2000) goes from 8.4s to 4.1s
- replace UUID(str).bytes with faster binascii.unhexlify, and do it only
once per step
- find only the first active trigger, instead of the full list
- use a dedicated function for checking active trigger
## Description
This PR enhances the state-model documentation by adding comprehensive
examples for advanced Pydantic usage in LangGraph. It addresses issue
#2745 regarding the need for better documentation of Pydantic schema
behavior.
### Changes
- Added new section on Advanced Pydantic Model Usage
- Added examples for serialization behavior with nested models
- Added section on runtime type coercion with examples
- Added documentation for proper message type handling (BaseMessage vs
AnyMessage)
- Updated Pydantic error URLs to latest version
### Related Issues
Closes#2745
### Testing
- All notebook cells have been executed and outputs verified
- Examples demonstrate proper usage patterns
- Error cases are properly documented
### Documentation
The changes are documentation-focused and include:
- New examples for complex Pydantic models
- Runtime coercion behavior examples
- Message type handling best practices
### Reviewers
@eyurtsev
- sequential(2000) goes from 8.4s to 4.7s
- replace UUID(str).bytes with simpler str.encode()
- find only the first active trigger, instead of the full list
- use a dedicated function for checking active trigger
- Was O(n^2) due to individual channels created for every conditional
edge, including the default cond edge created for Command
- Now using a single channel per node for all conditional edge / command
triggers, reducing to linear complexity
- Improves run time on sequential(200) from 1.8s to 0.14s
When searching for subgraphs do not attempt to search function non
locals for RunnableCallables as this captures unwanted reference to
surrounding variables.
- Previously the global resume value was passed to subgraphs without
being consumed
- This would result in two parallel subgraph calls being able to use the
same resume value
- Note this behavior can't be implemented over the wire, that will be
fixed in future PR
Closes#3398
- Was O(n^2) due to individual channels created for every conditional edge, including the default cond edge created for Command
- Now using a single channel per node for all conditional edge / command triggers, reducing to linear complexity
- Improves run time on sequential(200) from 1.8s to 0.14s
- Previously the global resume value was passed to subgraphs without being consumed
- This would result in two parallel subgraph calls being able to use the same resume value
- Note this behavior can't be implemented over the wire, that will be fixed in future PR
- Need to use a single operation to check if present and remove item
from list
- This doesn't fix the separate issue that parallel tasks claiming a
single interrupt value have somewhat undefined behavior (in the sense
that they will race to be the first to take it). That will be fixed in a
future PR
Closes#3875
- Need to use a single operation to check if present and remove item from list
- This doesn't fix the separate issue that parallel tasks claiming a single interrupt value have somewhat undefined behavior (in the sense that they will race to be the first to take it). That will be fixed in a future PR
- no dependency on any particular encryption lib (there is no py stdlib
encryption lib)
- works with any modern checkpointer, ie. those which use dumps_typed
and loads_typed methods to serialize data
- uses the default msg pack serializer, but also works with any custom
serializer
- backwards compatible with unencrypted data in same storage (will just
be read unencrypted)
- providing easy constructor to use AES encryption through pycriptodome
library, one single line of code to add it in
- other encryption libraries or algorithms (even assymetric ones) can be
used by implementing the two-method CipherProtocol interface
- cipher name (eg. aes) is stored with encrypted payload for forwards
compatibility
```py
import sqlite3
from langgraph.checkpoint.serde.encrypted import EncryptedSerializer
from langgraph.checkpoint.sqlite import SqliteSaver
# will read AES key from env var LANGGRAPH_AES_KEY
serde = EncryptedSerializer.from_pycryptodome_aes()
# works with any other checkpointer, including custom ones
checkpointer = SqliteSaver(sqlite3.connect('...'), serde=serde)
```
- no dependency on any particular encryption lib (there is no py stdlib encryption lib)
- works with any modern checkpointer, ie. those which use dumps_typed and loads_typed methods to serialize data
- backwards compatible with unencrypted data in same storage (will just be read unencrypted)
- providing easy constructor to use AES encryption through pycriptodome library, one single line of code to add it in
- other encryption libraries or algorithms (even assymetric ones) can be used by implementing the two-method CipherProtocol interface
- cipher name (eg. aes) is stored with encrypted payload for forwards compatibility
- remove usage of require_at_least_one_of, we shouldn't be enforcing
presence of keys in inputs/updates, an empty dict is a valid
input/update
- ensure that values that were explcitly set/assigned in pydantic model
are saved even if equal to default value
- remove usage of require_at_least_one_of, we shouldn't be enforcing presence of keys in inputs/updates, an empty dict is a valid input/update
- ensure that values that were explcitly set/assigned in pydantic model are saved even if equal to default value
Since we are demonstrating thread-level memory not human-in-the-loop, a
string is more straightforward and reliable than AssertionError(), when
dealing with 'Unknown Location'.
This PR fixes the type of `foo` in the `State` class in Persistence
documentation. The type was previously defined as `int`, but the code
uses it as a `str`.
Updated the type of `foo` in the documentation to `str` to match its
actual usage in the code.
No changes to the functionality or codebase, only a documentation fix.
While working on langchain-ai/langgraphjs#984 I ported the test I was
debugging over to python so I could compare behavior. Figured I might as
well add it to this codebase, as I don't think we had this particular
case covered previously.
- When a pydantic input schema isued but dict input is passed in
validate it once after running hidden START node. If the input is an
instance of the input model we skip validation altogether
- When entering each node we need to create a standalone instance of the
state class, but we can now skip validation, as it's now run once
elsewhere
- When a pydantic input schema isued but dict input is passed in validate it once after running hidden START node. If the input is an instance of the input model we skip validation altogether
- When entering each node we need to create a standalone instance of the state class, but we can now skip validation, as it's now run once elsewhere
Currently the version badge showing langgraph version as PyPi shield
image is linking to the shield image. It would be more intuitive to link
it to PyPi.
---------
Co-authored-by: vbarda <vadym@langchain.dev>
Currently we ignore the input schema in the branch and instead use the
input schema from the previous node (or overall graph schema)
This change makes the input schema to branches respected. This means
that if you try to pass extra keys and they're NOT in the input schema,
you will receive an error. If you don't provide an annotation in the
router, it will fall back to the previous node's input schema / full
graph state schema
Alternative solution is to just ignore the input schema in the router
altogether (including ignoring the schema from previous node / full
graph), but personally I find it more confusing.
---------
Co-authored-by: Nuno Campos <nuno@langchain.dev>
Inherited attributes where not considered.
Pydantic model can inherit from other pydantic models. In those cases,
inherited attributes where not considered in the check and the code
fails.
---------
Co-authored-by: vbarda <vadym@langchain.dev>
Hi, I have built a package `nodeology` that empowers researchers to
rapidly develop, test, adapt, and execute foundation AI-integrated
scientific workflows by leveraging langgraph's state machine framework.
Please take a look and let me know if it can be added into this list.
Thank you :)
An async function of the form `def foo(P) -> T` has type `Callable[[P],
Awaitable[T]]`. The old type annotations then converted the function
into a `Callable[[P], SyncAsyncFuture[Awaitable[T]]]` which is
incorrect.
The change introduced in this commit updates the type annotations to
ensure the `Awaitable[T]` is correctly unwrapped.
I've tested it locally and confirmed it work on:
```python
@task
def sync_fn(a: int) -> int: ...
@task
def async_fn(a: int) -> int: ...
```
Let me know if you want me to add tests, just let me know how you test
type annotations.
- the previous behavior of re-creating model through config_specs would
lose custom annotations on config_type
---------
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
An async function of the form `def foo(P) -> T` has type `Callable[[P],
Awaitable[T]]`. The old type annotations then converted the function
into a `Callable[[P], SyncAsyncFuture[Awaitable[T]]]` which is
incorrect.
The change introduced in this commit updates the type annotations to
ensure the `Awaitable[T]` is correctly unwrapped.
Signed-off-by: JP-Ellis <josh@jpellis.me>
if ! diff -q schemas/schema.json schemas/schema.current.json > /dev/null; then
echo "Error: Langgraph.json configuration schema has changed. Please run 'poetry run python generate_schema.py' in the libs/cli directory and commit the changes."
> Looking for the JS version? See the [JS repo](https://github.com/langchain-ai/langgraphjs) and the [JS docs](https://langchain-ai.github.io/langgraphjs/).
## Overview
LangGraph — used by Replit, Uber, LinkedIn, GitLab and more — is a low-level orchestration framework for building controllable agents. While langchain provides integrations and composable components to streamline LLM application development, the LangGraph library enables agent orchestration — offering customizable architectures, long-term memory, and human-in-the-loop to reliably handle complex tasks.
[LangGraph](https://langchain-ai.github.io/langgraph/) is a library for building
stateful, multi-actor applications with LLMs, used to create agent and multi-agent
workflows. Check out an introductory tutorial [here](https://langchain-ai.github.io/langgraph/tutorials/introduction/).
LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.
### Why use LangGraph?
LangGraph powers [production-grade agents](https://www.langchain.com/built-with-langgraph), trusted by Linkedin, Uber, Klarna, GitLab, and many more. LangGraph provides fine-grained control over both the flow and state of your agent applications. It implements a central [persistence layer](https://langchain-ai.github.io/langgraph/concepts/persistence/), enabling features that are common to most agent architectures:
- **Memory**: LangGraph persists arbitrary aspects of your application's state,
supporting memory of conversations and other updates within and across user
interactions;
- **Human-in-the-loop**: Because state is checkpointed, execution can be interrupted
and resumed, allowing for decisions, validation, and corrections at key stages via
human input.
Standardizing these components allows individuals and teams to focus on the behavior
of their agent, instead of its supporting infrastructure.
Through [LangGraph Platform](#langgraph-platform), LangGraph also provides tooling for
the development, deployment, debugging, and monitoring of your applications.
LangGraph integrates seamlessly with
[LangChain](https://python.langchain.com/docs/introduction/) and
[LangSmith](https://docs.smith.langchain.com/) (but does not require them).
To learn more about LangGraph, check out our first LangChain Academy
course, *Introduction to LangGraph*, available for free
[LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform) is infrastructure for deploying LangGraph agents. It is a commercial solution for deploying agentic applications to production, built on the open-source LangGraph framework. The LangGraph Platform consists of several components that work together to support the development, deployment, debugging, and monitoring of LangGraph applications: [LangGraph Server](https://langchain-ai.github.io/langgraph/concepts/langgraph_server) (APIs), [LangGraph SDKs](https://langchain-ai.github.io/langgraph/concepts/sdk) (clients for the APIs), [LangGraph CLI](https://langchain-ai.github.io/langgraph/concepts/langgraph_cli) (command line tool for building the server), and [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio) (UI/debugger).
See deployment options [here](https://langchain-ai.github.io/langgraph/concepts/deployment_options/)
(includes a free tier).
Here are some common issues that arise in complex deployments, which LangGraph Platform addresses:
- **Streaming support**: LangGraph Server provides [multiple streaming modes](https://langchain-ai.github.io/langgraph/concepts/streaming) optimized for various application needs
- **Background runs**: Runs agents asynchronously in the background
- **Support for long running agents**: Infrastructure that can handle long running processes
- **[Double texting](https://langchain-ai.github.io/langgraph/concepts/double_texting)**: Handle the case where you get two messages from the user before the agent can respond
- **Handle burstiness**: Task queue for ensuring requests are handled consistently without loss, even under heavy loads
## Installation
```shell
```bash
pip install -U langgraph
```
## Example
Let's build a tool-calling [ReAct-style](https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/#react-implementation) agent that uses a search tool!
```shell
pip install langchain-anthropic
```
```shell
exportANTHROPIC_API_KEY=sk-...
```
Optionally, we can set up [LangSmith](https://docs.smith.langchain.com/) for best-in-class observability.
```shell
exportLANGSMITH_TRACING=true
exportLANGSMITH_API_KEY=lsv2_sk_...
```
The simplest way to create a tool-calling agent in LangGraph is to use `create_react_agent`:
<details open>
<summary>High-level implementation</summary>
To learn more about how to use LangGraph, check out [the docs](https://langchain-ai.github.io/langgraph/). We show a simple example below of how to create a ReAct agent.
```python
# This code depends on pip install langchain[anthropic]
fromlanggraph.prebuiltimportcreate_react_agent
fromlanggraph.checkpoint.memoryimportMemorySaver
fromlangchain_anthropicimportChatAnthropic
fromlangchain_core.toolsimporttool
# Define the tools for the agent to use
@tool
defsearch(query:str):
"""Call to surf the web."""
# This is a placeholder, but don't tell the LLM that...
{"messages":[{"role":"user","content":"what is the weather in sf"}]}
)
final_state["messages"][-1].content
```
```
"Based on the search results, I can tell you that the current weather in San Francisco is:\n\nTemperature: 60 degrees Fahrenheit\nConditions: Foggy\n\nSan Francisco is known for its microclimates and frequent fog, especially during the summer months. The temperature of 60°F (about 15.5°C) is quite typical for the city, which tends to have mild temperatures year-round. The fog, often referred to as "Karl the Fog" by locals, is a characteristic feature of San Francisco\'s weather, particularly in the mornings and evenings.\n\nIs there anything else you\'d like to know about the weather in San Francisco or any other location?"
```
Now when we pass the same <code>"thread_id"</code>, the conversation context is retained via the saved state (i.e. stored list of messages)
```python
final_state=app.invoke(
{"messages":[{"role":"user","content":"what about ny"}]},
config={"configurable":{"thread_id":42}}
)
final_state["messages"][-1].content
```
```
"Based on the search results, I can tell you that the current weather in New York City is:\n\nTemperature: 90 degrees Fahrenheit (approximately 32.2 degrees Celsius)\nConditions: Sunny\n\nThis weather is quite different from what we just saw in San Francisco. New York is experiencing much warmer temperatures right now. Here are a few points to note:\n\n1. The temperature of 90°F is quite hot, typical of summer weather in New York City.\n2. The sunny conditions suggest clear skies, which is great for outdoor activities but also means it might feel even hotter due to direct sunlight.\n3. This kind of weather in New York often comes with high humidity, which can make it feel even warmer than the actual temperature suggests.\n\nIt's interesting to see the stark contrast between San Francisco's mild, foggy weather and New York's hot, sunny conditions. This difference illustrates how varied weather can be across different parts of the United States, even on the same day.\n\nIs there anything else you'd like to know about the weather in New York or any other location?"
```
</details>
> [!TIP]
> LangGraph is a **low-level** framework that allows you to implement any custom agent
architectures. Click on the low-level implementation below to see how to implement a
tool-calling agent from scratch.
> Check out [this guide](https://langchain-ai.github.io/langgraph/tutorials/workflows/) that walks through implementing common patterns (workflows and agents) in LangGraph.
<details>
<summary>Low-level implementation</summary>
## Why use LangGraph?
```python
fromtypingimportLiteral
LangGraph is built for developers who want to build powerful, adaptable AI agents. Developers choose LangGraph for:
- **Reliability and controllability.** Steer agent actions with moderation checks and human-in-the-loop approvals. LangGraph persists context for long-running workflows, keeping your agents on course.
- **Low-level and extensible.** Build custom agents with fully descriptive, low-level primitives – free from rigid abstractions that limit customization. Design scalable multi-agent systems, with each agent serving a specific role tailored to your use case.
- **First-class streaming support.** With token-by-token streaming and streaming of intermediate steps, LangGraph gives users clear visibility into agent reasoning and actions as they unfold in real time.
LangGraph is trusted in production and powering agents for companies like:
# Define the tools for the agent to use
@tool
defsearch(query:str):
"""Call to surf the web."""
# This is a placeholder, but don't tell the LLM that...
- And many more ([see list here](https://www.langchain.com/built-with-langgraph))
## LangGraph’s ecosystem
tools=[search]
While LangGraph can be used standalone, it also integrates seamlessly with any LangChain product, giving developers a full suite of tools for building agents. To improve your LLM application development, pair LangGraph with:
tool_node=ToolNode(tools)
- [LangSmith](http://www.langchain.com/langsmith) — Helpful for agent evals and observability. Debug poor-performing LLM app runs, evaluate agent trajectories, gain visibility in production, and improve performance over time.
- [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform) — Deploy and scale agents effortlessly with a purpose-built deployment platform for long running, stateful workflows. Discover, reuse, configure, and share agents across teams — and iterate quickly with visual prototyping in [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/).
# If the LLM makes a tool call, then we route to the "tools" node
iflast_message.tool_calls:
return"tools"
# Otherwise, we stop (reply to the user)
returnEND
While LangGraph is our open-source agent orchestration framework, enterprises that need scalable agent deployment can benefit from [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform/).
LangGraph Platform can help engineering teams:
# Define the function that calls the model
defcall_model(state:MessagesState):
messages=state['messages']
response=model.invoke(messages)
# We return a list, because this will get added to the existing list
return{"messages":[response]}
- **Accelerate agent development**: Quickly create agent UXs with configurable templates and [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/) for visualizing and debugging agent interactions.
- **Deploy seamlessly**: We handle the complexity of deploying your agent. LangGraph Platform includes robust APIs for memory, threads, and cron jobs plus auto-scaling task queues & servers.
- **Centralize agent management & reusability**: Discover, reuse, and manage agents across the organization. Business users can also modify agents without coding.
## Additional resources
# Define a new graph
workflow=StateGraph(MessagesState)
- [LangChain Academy](https://academy.langchain.com/courses/intro-to-langgraph): Learn the basics of LangGraph in our free, structured course.
- [Tutorials](https://langchain-ai.github.io/langgraph/tutorials/): Simple walkthroughs with guided examples on getting started with LangGraph.
- [Templates](https://langchain-ai.github.io/langgraph/concepts/template_applications/): Pre-built reference apps for common agentic workflows (e.g. ReAct agent, memory, retrieval etc.) that can be cloned and adapted.
- [How-to Guides](https://langchain-ai.github.io/langgraph/how-tos/): Quick, actionable code snippets for topics such as streaming, adding memory & persistence, and design patterns (e.g. branching, subgraphs, etc.).
- [API Reference](https://langchain-ai.github.io/langgraph/reference/graphs/): Detailed reference on core classes, methods, how to use the graph and checkpointing APIs, and higher-level prebuilt components.
- [Built with LangGraph](https://www.langchain.com/built-with-langgraph): Hear how industry leaders use LangGraph to ship powerful, production-ready AI applications.
# Define the two nodes we will cycle between
workflow.add_node("agent",call_model)
workflow.add_node("tools",tool_node)
## Acknowledgements
# Set the entrypoint as `agent`
# This means that this node is the first one called
workflow.add_edge(START,"agent")
# We now add a conditional edge
workflow.add_conditional_edges(
# First, we define the start node. We use `agent`.
# This means these are the edges taken after the `agent` node is called.
"agent",
# Next, we pass in the function that will determine which node is called next.
should_continue,
)
# We now add a normal edge from `tools` to `agent`.
# This means that after `tools` is called, `agent` node is called next.
workflow.add_edge("tools",'agent')
# Initialize memory to persist state between graph runs
checkpointer=MemorySaver()
# Finally, we compile it!
# This compiles it into a LangChain Runnable,
# meaning you can use it as you would any other runnable.
# Note that we're (optionally) passing the memory when compiling the graph
app=workflow.compile(checkpointer=checkpointer)
# Use the agent
final_state=app.invoke(
{"messages":[{"role":"user","content":"what is the weather in sf"}]},
config={"configurable":{"thread_id":42}}
)
final_state["messages"][-1].content
```
<b>Step-by-step Breakdown</b>:
<details>
<summary>Initialize the model and tools.</summary>
<ul>
<li>
We use <code>ChatAnthropic</code> as our LLM. <strong>NOTE:</strong> we need to make sure the model knows that it has these tools available to call. We can do this by converting the LangChain tools into the format for OpenAI tool calling using the <code>.bind_tools()</code> method.
</li>
<li>
We define the tools we want to use - a search tool in our case. It is really easy to create your own tools - see documentation here on how to do that <a href="https://python.langchain.com/docs/how_to/custom_tools/">here</a>.
</li>
</ul>
</details>
<details>
<summary>Initialize graph with state.</summary>
<ul>
<li>We initialize graph (<code>StateGraph</code>) by passing state schema (in our case <code>MessagesState</code>)</li>
<li><code>MessagesState</code> is a prebuilt state schema that has one attribute -- a list of LangChain <code>Message</code> objects, as well as logic for merging the updates from each node into the state.</li>
</ul>
</details>
<details>
<summary>Define graph nodes.</summary>
There are two main nodes we need:
<ul>
<li>The <code>agent</code> node: responsible for deciding what (if any) actions to take.</li>
<li>The <code>tools</code> node that invokes tools: if the agent decides to take an action, this node will then execute that action.</li>
</ul>
</details>
<details>
<summary>Define entry point and graph edges.</summary>
First, we need to set the entry point for graph execution - <code>agent</code> node.
Then we define one normal and one conditional edge. Conditional edge means that the destination depends on the contents of the graph's state (<code>MessagesState</code>). In our case, the destination is not known until the agent (LLM) decides.
<ul>
<li>Conditional edge: after the agent is called, we should either:
<ul>
<li>a. Run tools if the agent said to take an action, OR</li>
<li>b. Finish (respond to the user) if the agent did not ask to run tools</li>
</ul>
</li>
<li>Normal edge: after the tools are invoked, the graph should always return to the agent to decide what to do next</li>
</ul>
</details>
<details>
<summary>Compile the graph.</summary>
<ul>
<li>
When we compile the graph, we turn it into a LangChain
which automatically enables calling <code>.invoke()</code>, <code>.stream()</code> and <code>.batch()</code>
with your inputs
</li>
<li>
We can also optionally pass checkpointer object for persisting state between graph runs, and enabling memory,
human-in-the-loop workflows, time travel and more. In our case we use <code>MemorySaver</code> -
a simple in-memory checkpointer
</li>
</ul>
</details>
<details>
<summary>Execute the graph.</summary>
<ol>
<li>LangGraph adds the input message to the internal state, then passes the state to the entrypoint node, <code>"agent"</code>.</li>
<li>The <code>"agent"</code> node executes, invoking the chat model.</li>
<li>The chat model returns an <code>AIMessage</code>. LangGraph adds this to the state.</li>
<li>Graph cycles the following steps until there are no more <code>tool_calls</code> on <code>AIMessage</code>:
<ul>
<li>If <code>AIMessage</code> has <code>tool_calls</code>, <code>"tools"</code> node executes</li>
<li>The <code>"agent"</code> node executes again and returns <code>AIMessage</code></li>
</ul>
</li>
<li>Execution progresses to the special <code>END</code> value and outputs the final state. And as a result, we get a list of all our chat messages as output.</li>
</ol>
</details>
</details>
## Documentation
* [Tutorials](https://langchain-ai.github.io/langgraph/tutorials/): Learn to build with LangGraph through guided examples.
* [How-to Guides](https://langchain-ai.github.io/langgraph/how-tos/): Accomplish specific things within LangGraph, from streaming, to adding memory & persistence, to common design patterns (branching, subgraphs, etc.), these are the place to go if you want to copy and run a specific code snippet.
* [Conceptual Guides](https://langchain-ai.github.io/langgraph/concepts/high_level/): In-depth explanations of the key concepts and principles behind LangGraph, such as nodes, edges, state and more.
* [API Reference](https://langchain-ai.github.io/langgraph/reference/graphs/): Review important classes and methods, simple examples of how to use the graph and checkpointing APIs, higher-level prebuilt components and more.
* [LangGraph Platform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform): LangGraph Platform is a commercial solution for deploying agentic applications in production, built on the open-source LangGraph framework.
## Resources
* [Built with LangGraph](https://www.langchain.com/built-with-langgraph): Hear how industry leaders use LangGraph to ship powerful, production-ready AI applications.
## Contributing
For more information on how to contribute, see [here](https://github.com/langchain-ai/langgraph/blob/main/CONTRIBUTING.md).
LangGraph is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/). The public interface draws inspiration from [NetworkX](https://networkx.org/documentation/latest/). LangGraph is built by LangChain Inc, the creators of LangChain, but can be used without LangChain.
This list of companies using LangGraph and their success stories is compiled from public sources. If your company uses LangGraph, we'd love for you to share your story and add it to the list. You’re also welcome to contribute updates based on publicly available information from other companies, such as blog posts or press releases.
@@ -10,14 +10,17 @@ This list of companies using LangGraph and their success stories is compiled fro
| [LinkedIn](https://www.linkedin.com/) | Social Media | Code generation; Search & discovery | [Blog post, 2025](https://www.linkedin.com/blog/engineering/ai/practical-text-to-sql-for-data-analytics); [Blog post, 2024](https://www.linkedin.com/blog/engineering/generative-ai/behind-the-platform-the-journey-to-create-the-linkedin-genai-application-tech-stack) |
An *agent* consists of three components: a **large language model (LLM)**, a set of **tools** it can use, and a **prompt** that provides instructions.
The LLM operates in a loop. In each iteration, it selects a tool to invoke, provides input, receives the result (an observation), and uses that observation to inform the next action. The loop continues until a stopping condition is met — typically when the agent has gathered enough information to respond to the user.
<figcaption>Agent loop: the LLM selects tools and uses their outputs to fulfill a user request.</figcaption>
</figure>
## Basic configuration
Use [`create_react_agent`][langgraph.prebuilt.chat_agent_executor.create_react_agent] to instantiate an agent:
```python
fromlanggraph.prebuiltimportcreate_react_agent
defget_weather(city:str)->str:# (1)!
"""Get weather for a given city."""
returnf"It's always sunny in {city}!"
agent=create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",# (2)!
tools=[get_weather],# (3)!
prompt="You are a helpful assistant"# (4)!
)
# Run the agent
agent.invoke(
{"messages":[{"role":"user","content":"what is the weather in sf"}]}
)
```
1. Define a tool for the agent to use. Tools can be defined as vanilla Python functions. For more advanced tool usage and customization, check the [tools](./tools.md) page.
2. Provide a language model for the agent to use. To learn more about configuring language models for the agents, check the [models](./models.md) page.
3. Provide a list of tools for the model to use.
4. Provide a system prompt (instructions) to the language model used by the agent.
## LLM configuration
Use [init_chat_model](https://python.langchain.com/api_reference/langchain/chat_models/langchain.chat_models.base.init_chat_model.html) to configure an LLM with specific parameters,
such as temperature:
```python
fromlangchain.chat_modelsimportinit_chat_model
fromlanggraph.prebuiltimportcreate_react_agent
# highlight-next-line
model=init_chat_model(
"anthropic:claude-3-7-sonnet-latest",
# highlight-next-line
temperature=0
)
agent=create_react_agent(
# highlight-next-line
model=model,
tools=[get_weather],
)
```
See the [models](./models.md) page for more information on how to configure LLMs.
## Custom Prompts
Prompts instruct the LLM how to behave. They can be:
* **Static**: A string is interpreted as a **system message**
* **Dynamic**: a list of messages generated at **runtime** based on input or configuration
### Static prompts
Define a fixed prompt string or list of messages.
```python
fromlanggraph.prebuiltimportcreate_react_agent
agent=create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
# A static prompt that never changes
# highlight-next-line
prompt="Never answer questions about the weather."
)
agent.invoke(
{"messages":[{"role":"user","content":"what is the weather in sf"}]}
)
```
### Dynamic prompts
Define a function that returns a message list based on the agent's state and configuration:
1. Dynamic prompts allow including non-message [context](./context.md) when constructing an input to the LLM, such as:
- Information passed at runtime, like a `user_id` or API credentials (using `config`).
- Internal agent state updated during a multi-step reasoning process (using `state`).
Dynamic prompts can be defined as functions that take `state` and `config` and return a list of messages to send to the LLM.
See the [context](./context.md) page for more information.
## Memory
To allow multi-turn conversations with an agent, you need to enable [persistence](../concepts/persistence.md) by providing a `checkpointer` when creating an agent. At runtime you need to provide a config containing `thread_id` — a unique identifier for the conversation (session):
{"messages":[{"role":"user","content":"what is the weather in sf"}]},
# highlight-next-line
config# (2)!
)
ny_response=agent.invoke(
{"messages":[{"role":"user","content":"what about new york?"}]},
# highlight-next-line
config
)
```
1.`checkpointer` allows the agent to store its state at every step in the tool calling loop. This enables [short-term memory](./memory.md#short-term-memory) and [human-in-the-loop](./human-in-the-loop.md) capabilities.
2. Pass configuration with `thread_id` to be able to resume the same conversation on future agent invocations.
When you enable the checkpointer, it stores agent state at every step in the provided checkpointer database (or in memory, if using `InMemorySaver`).
Note that in the above example, when the agent is invoked the second time with the same `thread_id`, the original message history from the first conversation is automatically included, together with the new user input.
Please see the [memory guide](./memory.md) for more details on how to work with memory.
## Structured output
To produce structured responses conforming to a schema, use the `response_format` parameter. The schema can be defined with a `Pydantic` model or `TypedDict`. The result will be accessible via the `structured_response` field.
```python
frompydanticimportBaseModel
fromlanggraph.prebuiltimportcreate_react_agent
classWeatherResponse(BaseModel):
conditions:str
agent=create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
# highlight-next-line
response_format=WeatherResponse# (1)!
)
response=agent.invoke(
{"messages":[{"role":"user","content":"what is the weather in sf"}]}
)
# highlight-next-line
response["structured_response"]
```
1. When `response_format` is provided, a separate step is added at the end of the agent loop: agent message history is passed to an LLM with structured output to generate a structured response.
To provide a system prompt to this LLM, use a tuple `(prompt, schema)`, e.g., `response_format=(prompt, WeatherResponse)`.
!!! Note "LLM post-processing"
Structured output requires an additional call to the LLM to format the response according to the schema.
| [**Config**](#config-static-context) | data passed at the start of a run | ❌ | per run |
| [**State**](#state-mutable-context) | dynamic data that can change during execution | ✅ | per run or conversation |
| [**Long-term Memory (Store)**](#long-term-memory-cross-conversation-context) | data that can be shared between conversations | ✅ | across conversations |
You can use context to:
- Adjust the system prompt the model sees
- Feed tools with necessary inputs
- Track facts during an ongoing conversation
## Providing Runtime Context
Use this when you need to inject data into an agent at runtime.
### Config (static context)
Config is for immutable data like user metadata or API keys. Use
when you have values that don't change mid-run.
Specify configuration using a key called **"configurable"** which is reserved
for this purpose:
```python
agent.invoke(
{"messages":[{"role":"user","content":"hi!"}]},
# highlight-next-line
config={"configurable":{"user_id":"user_123"}}
)
```
### State (mutable context)
State acts as short-term memory during a run. It holds dynamic data that can evolve during execution, such as values derived from tools or LLM outputs.
```python
classCustomState(AgentState):
# highlight-next-line
user_name:str
agent=create_react_agent(
# Other agent parameters...
# highlight-next-line
state_schema=CustomState,
)
agent.invoke({
"messages":"hi!",
"user_name":"Jane"
})
```
!!! tip "Turning on memory"
Please see the [memory guide](./memory.md) for more details on how to enable memory. This is a powerful feature that allows you to persist the agent's state across multiple invocations.
Otherwise, the state is scoped only to a single agent run.
### Long-Term Memory (cross-conversation context)
For context that spans *across* conversations or sessions, LangGraph allows access to **long-term memory** via a `store`. This can be used to read or update persistent facts (e.g., user profiles, preferences, prior interactions). For more, see the [Memory guide](./memory.md).
## Customizing Prompts with Context { #prompts }
Prompts define how the agent behaves. To incorporate runtime context, you can dynamically generate prompts based on the agent's state or config.
Common use cases:
- Personalization
- Role or goal customization
- Conditional behavior (e.g., user is admin)
=== "Using config"
```python
from langchain_core.messages import AnyMessage
from langchain_core.runnables import RunnableConfig
from langgraph.prebuilt import create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState
Tools can access context through special parameter **annotations**.
* Use `RunnableConfig` for config access
* Use `Annotated[StateSchema, InjectedState]` for agent state
!!! tip
These annotations prevent LLMs from attempting to fill in the values. These parameters will be **hidden** from the LLM.
=== "Using config"
```python
def get_user_info(
# highlight-next-line
config: RunnableConfig,
) -> str:
"""Look up user info."""
# highlight-next-line
user_id = config["configurable"].get("user_id")
return "User is John Smith" if user_id == "user_123" else "Unknown user"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_user_info],
)
agent.invoke(
{"messages": [{"role": "user", "content": "look up user information"}]},
# highlight-next-line
config={"configurable": {"user_id": "user_123"}}
)
```
=== "Using State"
```python
from typing import Annotated
from langgraph.prebuilt import InjectedState
class CustomState(AgentState):
# highlight-next-line
user_id: str
def get_user_info(
# highlight-next-line
state: Annotated[CustomState, InjectedState]
) -> str:
"""Look up user info."""
# highlight-next-line
user_id = state["user_id"]
return "User is John Smith" if user_id == "user_123" else "Unknown user"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_user_info],
# highlight-next-line
state_schema=CustomState,
)
agent.invoke({
"messages": "look up user information",
# highlight-next-line
"user_id": "user_123"
})
```
### Update Context from Tools
Tools can update agent's context (state and long-term memory) during execution. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts. See [Memory](./memory.md#read-short-term) guide for more information.
> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
See this [tutorial](https://langchain-ai.github.io/langgraph/tutorials/langgraph-platform/local-server/) to learn more about running LangGraph app locally.
## LangGraph Studio Web UI
LangGraph Studio Web is a specialized UI that you can connect to LangGraph API server to enable visualization, interaction, and debugging of your application locally. Test your graph in the LangGraph Studio Web UI by visiting the URL provided in the output of the `langgraph dev` command.
> - LangGraph Studio Web UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
## Deployment
Once your LangGraph app is running locally, you can deploy it using LangGraph Cloud or self-hosted options. Refer to the [deployment options guide](../tutorials/deployment.md) for detailed instructions on all supported deployment models.
To evaluate your agent's performance you can use `LangSmith` [evaluations](https://docs.smith.langchain.com/evaluation). You would need to first define an evaluator function to judge the results from an agent, such as final outputs or trajectory. Depending on your evaluation technique, this may or may not involve a reference output:
1. Specify how the trajectories will be compared. `superset` will accept output trajectory as valid if it's a superset of the reference one. Other options include: [strict](https://github.com/langchain-ai/agentevals?tab=readme-ov-file#strict-match), [unordered](https://github.com/langchain-ai/agentevals?tab=readme-ov-file#unordered-match) and [subset](https://github.com/langchain-ai/agentevals?tab=readme-ov-file#subset-and-superset-match)
As a next step, learn more about how to [customize trajectory match evaluator](https://github.com/langchain-ai/agentevals?tab=readme-ov-file#agent-trajectory-match).
### LLM-as-a-judge
You can use LLM-as-a-judge evaluator that uses an LLM to compare the trajectory against the reference outputs and output a score:
```python
importjson
fromagentevals.trajectory.llmimport(
# highlight-next-line
create_trajectory_llm_as_judge,
TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE
)
evaluator=create_trajectory_llm_as_judge(
prompt=TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE,
model="openai:o3-mini"
)
```
## Run evaluator
To run an evaluator, you will first need to create a [LangSmith dataset](https://docs.smith.langchain.com/evaluation/concepts#datasets). To use the prebuilt AgentEvals evaluators, you will need a dataset with the following schema:
- **input**: `{"messages": [...]}` input messages to call the agent with.
- **output**: `{"messages": [...]}` expected message history in the agent output. For trajectory evaluation, you can choose to keep only assistant messages.
To review, edit and approve tool calls in an agent you can use LangGraph's built-in [Human-In-the-Loop (HIL)](../concepts/human_in_the_loop.md) features, specifically the [`interrupt()`][langgraph.types.interrupt] primitive.
LangGraph allows you to pause execution **indefinitely** — for minutes, hours, or even days—until human input is received.
This is possible because the agent state is **checkpointed into a database**, which allows the system to persist execution context and later resume the workflow, continuing from where it left off.
For a deeper dive into the **human-in-the-loop** concept, see the [concept guide](../concepts/human_in_the_loop.md).
A human can review and edit the output from the agent before proceeding. This is particularly critical in applications where the tool calls requested may be sensitive or require human oversight.
</figcaption>
</figure>
## Review tool calls
To add a human approval step to a tool:
1. Use `interrupt()` in the tool to pause execution.
2. Resume with a `Command(resume=...)` to continue based on human input.
returnf"Successfully booked a stay at {hotel_name}."
# highlight-next-line
checkpointer=InMemorySaver()# (2)!
agent=create_react_agent(
model="anthropic:claude-3-5-sonnet-latest",
tools=[book_hotel],
# highlight-next-line
checkpointer=checkpointer,# (3)!
)
```
1. The [`interrupt` function][langgraph.types.interrupt] pauses the agent graph at a specific node. In this case, we call `interrupt()` at the beginning of the tool function, which pauses the graph at the node that executes the tool. The information inside `interrupt()` (e.g., tool calls) can be presented to a human, and the graph can be resumed with the user input (tool call approval, edit or feedback).
2. The `InMemorySaver` is used to store the agent state at every step in the tool calling loop. This enables [short-term memory](./memory.md#short-term-memory) and [human-in-the-loop](./human-in-the-loop.md) capabilities. In this example, we use `InMemorySaver` to store the agent state in memory. In a production application, the agent state will be stored in a database.
3. Initialize the agent with the `checkpointer`.
Run the agent with the `stream()` method, passing the `config` object to specify the thread ID. This allows the agent to resume the same conversation on future invocations.
```python
config={
"configurable":{
# highlight-next-line
"thread_id":"1"
}
}
forchunkinagent.stream(
{"messages":[{"role":"user","content":"book a stay at McKittrick hotel"}]},
# highlight-next-line
config
):
print(chunk)
print("\n")
```
> You should see that the agent runs until it reaches the `interrupt()` call, at which point it pauses and waits for human input.
Resume the agent with a `Command(resume=...)` to continue based on human input.
1. The [`interrupt` function][langgraph.types.interrupt] is used in conjunction with the [`Command`](../reference/types.md#langgraph.types.Command) object to resume the graph with a value provided by the human.
## Using with Agent Inbox
You can create a wrapper to add interrupts to *any* tool.
The example below provides a reference implementation compatible with [Agent Inbox UI](https://github.com/langchain-ai/agent-inbox) and [Agent Chat UI](https://github.com/langchain-ai/agent-chat-ui).
```python title="Wrapper that adds human-in-the-loop to any tool"
from typing import Callable
from langchain_core.tools import BaseTool, tool as create_tool
from langchain_core.runnables import RunnableConfig
from langgraph.types import interrupt
from langgraph.prebuilt.interrupt import HumanInterruptConfig, HumanInterrupt
def add_human_in_the_loop(
tool: Callable | BaseTool,
*,
interrupt_config: HumanInterruptConfig = None,
) -> BaseTool:
"""Wrap a tool to support human-in-the-loop review."""
1. This wrapper creates a new tool that calls `interrupt()` **before** executing the wrapped tool.
2. `interrupt()` is using special input and output format that's expected by [Agent Inbox UI](https://github.com/langchain-ai/agent-inbox):
- a list of [`HumanInterrupt`][langgraph.prebuilt.interrupt.HumanInterrupt] objects is sent to `AgentInbox` render interrupt information to the end user
- resume value is provided by `AgentInbox` as a list (i.e., `Command(resume=[...])`)
You can use the `add_human_in_the_loop` wrapper to add `interrupt()` to any tool without having to add it *inside* the tool:
```python
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.prebuilt import create_react_agent
# highlight-next-line
checkpointer = InMemorySaver()
def book_hotel(hotel_name: str):
"""Book a hotel"""
return f"Successfully booked a stay at {hotel_name}."
agent = create_react_agent(
model="anthropic:claude-3-5-sonnet-latest",
tools=[
# highlight-next-line
add_human_in_the_loop(book_hotel), # (1)!
],
# highlight-next-line
checkpointer=checkpointer,
)
config = {"configurable": {"thread_id": "1"}}
# Run the agent
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "book a stay at McKittrick hotel"}]},
# highlight-next-line
config
):
print(chunk)
print("\n")
```
1. The `add_human_in_the_loop` wrapper is used to add `interrupt()` to the tool. This allows the agent to pause execution and wait for human input before proceeding with the tool call.
> You should see that the agent runs until it reaches the `interrupt()` call,
> at which point it pauses and waits for human input.
Resume the agent with a `Command(resume=...)` to continue based on human input.
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol that standardizes how applications provide tools and context to language models. LangGraph agents can use tools defined on MCP servers through the `langchain-mcp-adapters` library.

Install the `langchain-mcp-adapters` library to use MCP tools in LangGraph:
```bash
pip install langchain-mcp-adapters
```
## Use MCP tools
The `langchain-mcp-adapters` package enables agents to use tools defined across one or more MCP servers.
```python title="Agent using tools defined on MCP servers"
# highlight-next-line
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
# highlight-next-line
async with MultiServerMCPClient(
{
"math": {
"command": "python",
# Replace with absolute path to your math_server.py file
"args": ["/path/to/math_server.py"],
"transport": "stdio",
},
"weather": {
# Ensure your start your weather server on port 8000
"url": "http://localhost:8000/sse",
"transport": "sse",
}
}
) as client:
agent = create_react_agent(
"anthropic:claude-3-7-sonnet-latest",
# highlight-next-line
client.get_tools()
)
math_response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
)
weather_response = await agent.ainvoke(
{"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
)
```
## Custom MCP servers
To create your own MCP servers, you can use the `mcp` library. This library provides a simple way to define tools and run them as servers.
Install the MCP library:
```bash
pip install mcp
```
Use the following reference implementations to test your agent with MCP tool servers.
```python title="Example Math Server (stdio transport)"
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Math")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
@mcp.tool()
def multiply(a: int, b: int) -> int:
"""Multiply two numbers"""
return a * b
if __name__ == "__main__":
mcp.run(transport="stdio")
```
```python title="Example Weather Server (SSE transport)"
<figcaption>Both <strong>short-term</strong> and <strong>long-term</strong> memory require persistent storage to maintain continuity across LLM interactions. In production environments, this data is typically stored in a database.</figcaption>
</figure>
!!! note "Terminology"
In LangGraph:
- *Short-term memory* is also referred to as **thread-level memory**.
- *Long-term memory* is also called **cross-thread memory**.
A [thread](../concepts/persistence.md#threads) represents a sequence of related runs
grouped by the same `thread_id`.
## Short-term memory
Short-term memory enables agents to track multi-turn conversations. To use it, you must:
1. Provide a `checkpointer` when creating the agent. The `checkpointer` enables [persistence](../concepts/persistence.md) of the agent's state.
2. Supply a `thread_id` in the config when running the agent. The `thread_id` is a unique identifier for the conversation session.
{"messages":[{"role":"user","content":"what is the weather in sf"}]},
# highlight-next-line
config
)
# Continue the conversation using the same thread_id
ny_response=agent.invoke(
{"messages":[{"role":"user","content":"what about new york?"}]},
# highlight-next-line
config# (4)!
)
```
1. The `InMemorySaver` is a checkpointer that stores the agent's state in memory. In a production setting, you would typically use a database or other persistent storage. Please review the [checkpointer documentation](../reference/checkpoints.md) for more options. If you're deploying with **LangGraph Platform**, the platform will provide a production-ready checkpointer for you.
2. The `checkpointer` is passed to the agent. This enables the agent to persist its state across invocations. Please note that
3. A unique `thread_id` is provided in the config. This ID is used to identify the conversation session. The value is controlled by the user and can be any string.
4. The agent will continue the conversation using the same `thread_id`. This will allow the agent to infer that the user is asking specifically about the **weather** in New York.
When the agent is invoked the second time with the same `thread_id`, the original message history from the first conversation is automatically included, allowing the agent to infer that the user is asking specifically about the **weather** in New York.
!!! Note "LangGraph Platform providers a production-ready checkpointer"
If you're using [LangGraph Platform](./deployment.md), during deployment your checkpointer will be automatically configured to use a production-ready database.
### Manage message history
Long conversations can exceed the LLM's context window. Common solutions are:
* [Summarization](#summarize-message-history): Maintain a running summary of the conversation
* [Trimming](#trim-message-history): Remove first or last N messages in the history
This allows the agent to keep track of the conversation without exceeding the LLM's context window.
To manage message history, specify `pre_model_hook` — a function ([node](../concepts/low_level.md#nodes)) that will always run before calling the language model.
<figcaption>Long conversations can exceed the LLM's context window. A common solution is to maintain a running summary of the conversation. This allows the agent to keep track of the conversation without exceeding the LLM's context window.
</figcaption>
</figure>
To summarize message history, you can use [`pre_model_hook`][langgraph.prebuilt.chat_agent_executor.create_react_agent] with a prebuilt [`SummarizationNode`](https://langchain-ai.github.io/langmem/reference/short_term/#langmem.short_term.SummarizationNode):
# NOTE: we're adding this key to keep track of previous summary information
# to make sure we're not summarizing on every LLM call
# highlight-next-line
context:dict[str,Any]# (2)!
checkpointer=InMemorySaver()# (3)!
agent=create_react_agent(
model=model,
tools=tools,
# highlight-next-line
pre_model_hook=summarization_node,# (4)!
# highlight-next-line
state_schema=State,# (5)!
checkpointer=checkpointer,
)
```
1. The `InMemorySaver` is a checkpointer that stores the agent's state in memory. In a production setting, you would typically use a database or other persistent storage. Please review the [checkpointer documentation](../reference/checkpoints.md) for more options. If you're deploying with **LangGraph Platform**, the platform will provide a production-ready checkpointer for you.
2. The `context` key is added to the agent's state. The key contains book-keeping information for the summarization node. It is used to keep track of the last summary information and ensure that the agent doesn't summarize on every LLM call, which can be inefficient.
3. The `checkpointer` is passed to the agent. This enables the agent to persist its state across invocations.
4. The `pre_model_hook` is set to the `SummarizationNode`. This node will summarize the message history before sending it to the LLM. The summarization node will automatically handle the summarization process and update the agent's state with the new summary. You can replace this with a custom implementation if you prefer. Please see the [create_react_agent][langgraph.prebuilt.chat_agent_executor.create_react_agent] API reference for more details.
5. The `state_schema` is set to the `State` class, which is the custom state that contains an extra `context` key.
#### Trim message history
To trim message history, you can use [`pre_model_hook`][langgraph.prebuilt.chat_agent_executor.create_react_agent] with [`trim_messages`](https://python.langchain.com/api_reference/core/messages/langchain_core.messages.utils.trim_messages.html) function:
```python
# highlight-next-line
fromlangchain_core.messages.utilsimport(
# highlight-next-line
trim_messages,
# highlight-next-line
count_tokens_approximately
# highlight-next-line
)
fromlanggraph.prebuiltimportcreate_react_agent
# This function will be called every time before the node that calls LLM
defpre_model_hook(state):
trimmed_messages=trim_messages(
state["messages"],
strategy="last",
token_counter=count_tokens_approximately,
max_tokens=384,
start_on="human",
end_on=("human","tool"),
)
# highlight-next-line
return{"llm_input_messages":trimmed_messages}
checkpointer=InMemorySaver()
agent=create_react_agent(
model,
tools,
# highlight-next-line
pre_model_hook=pre_model_hook,
checkpointer=checkpointer,
)
```
To learn more about using `pre_model_hook` for managing message history, see this [how-to guide](../how-tos/create-react-agent-manage-message-history.ipynb)
### Read in tools { #read-short-term }
LangGraph allows agent to access its short-term memory (state) inside the tools.
return"User is John Smith"ifuser_id=="user_123"else"Unknown user"
agent=create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_user_info],
# highlight-next-line
state_schema=CustomState,
)
agent.invoke({
"messages":"look up user information",
# highlight-next-line
"user_id":"user_123"
})
```
See the [Context](./context.md#__tabbed_2_2) guide for more information.
### Write from tools { #write-short-term }
To modify the agent's short-term memory (state) during execution, you can return state updates directly from the tools. This is useful for persisting intermediate results or making information accessible to subsequent tools or prompts.
"""Use this to greet the user once you found their info."""
user_name=state["user_name"]
returnf"Hello {user_name}!"
agent=create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[update_user_info,greet],
# highlight-next-line
state_schema=CustomState
)
agent.invoke(
{"messages":[{"role":"user","content":"greet the user"}]},
# highlight-next-line
config={"configurable":{"user_id":"user_123"}}
)
```
For more details, see [how to update state from tools](../how-tos/update-state-from-tools.ipynb).
## Long-term memory
Use long-term memory to store user-specific or application-specific data across conversations. This is useful for applications like chatbots, where you want to remember user preferences or other information.
To use long-term memory, you need to:
1. [Configure a store](../how-tos/cross-thread-persistence.ipynb) to persist data across invocations.
2. Use the [`get_store`][langgraph.config.get_store] function to access the store from within tools or prompts.
### Read { #read-long-term }
```python title="A tool the agent can use to look up user information"
from langchain_core.runnables import RunnableConfig
from langgraph.config import get_store
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore
# highlight-next-line
store = InMemoryStore() # (1)!
# highlight-next-line
store.put( # (2)!
("users",), # (3)!
"user_123", # (4)!
{
"name": "John Smith",
"language": "English",
} # (5)!
)
def get_user_info(config: RunnableConfig) -> str:
"""Look up user info."""
# Same as that provided to `create_react_agent`
# highlight-next-line
store = get_store() # (6)!
user_id = config["configurable"].get("user_id")
# highlight-next-line
user_info = store.get(("users",), user_id) # (7)!
return str(user_info.value) if user_info else "Unknown user"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_user_info],
# highlight-next-line
store=store # (8)!
)
# Run the agent
agent.invoke(
{"messages": [{"role": "user", "content": "look up user information"}]},
# highlight-next-line
config={"configurable": {"user_id": "user_123"}}
)
```
1. The `InMemoryStore` is a store that stores data in memory. In a production setting, you would typically use a database or other persistent storage. Please review the [store documentation](../reference/store.md) for more options. If you're deploying with **LangGraph Platform**, the platform will provide a production-ready store for you.
2. For this example, we write some sample data to the store using the `put` method. Please see the [BaseStore.put][langgraph.store.base.BaseStore.put] API reference for more details.
3. The first argument is the namespace. This is used to group related data together. In this case, we are using the `users` namespace to group user data.
4. A key within the namespace. This example uses a user ID for the key.
5. The data that we want to store for the given user.
6. The `get_store` function is used to access the store. You can call it from anywhere in your code, including tools and prompts. This function returns the store that was passed to the agent when it was created.
7. The `get` method is used to retrieve data from the store. The first argument is the namespace, and the second argument is the key. This will return a `StoreValue` object, which contains the value and metadata about the value.
8. The `store` is passed to the agent. This enables the agent to access the store when running tools. You can also use the `get_store` function to access the store from anywhere in your code.
### Write { #write-long-term }
```python title="Example of a tool that updates user information"
# You can access the store directly to get the value
store.get(("users",), "user_123").value
```
1. The `InMemoryStore` is a store that stores data in memory. In a production setting, you would typically use a database or other persistent storage. Please review the [store documentation](../reference/store.md) for more options. If you're deploying with **LangGraph Platform**, the platform will provide a production-ready store for you.
2. The `UserInfo` class is a `TypedDict` that defines the structure of the user information. The LLM will use this to format the response according to the schema.
3. The `save_user_info` function is a tool that allows an agent to update user information. This could be useful for a chat application where the user wants to update their profile information.
4. The `get_store` function is used to access the store. You can call it from anywhere in your code, including tools and prompts. This function returns the store that was passed to the agent when it was created.
5. The `put` method is used to store data in the store. The first argument is the namespace, and the second argument is the key. This will store the user information in the store.
6. The `user_id` is passed in the config. This is used to identify the user whose information is being updated.
### Semantic search
LangGraph also allows you to [search](https://langchain-ai.github.io/langgraph/how-tos/memory/semantic-search/#using-in-create-react-agent) for items in long-term memory by semantic similarity.
### Prebuilt memory tools
**LangMem** is a LangChain-maintained library that offers tools for managing long-term memories in your agent. See the [LangMem documentation](https://langchain-ai.github.io/langmem/) for usage examples.
This page describes how to configure the chat model used by an agent.
## Tool calling support
To enable tool-calling agents, the underlying LLM must support [tool calling](https://python.langchain.com/docs/concepts/tool_calling/).
Compatible models can be found in the [LangChain integrations directory](https://python.langchain.com/docs/integrations/chat/).
## Specifying a model by name
You can configure an agent with a model name string:
```python
fromlanggraph.prebuiltimportcreate_react_agent
agent=create_react_agent(
# highlight-next-line
model="anthropic:claude-3-7-sonnet-latest",
# other parameters
)
```
## Using `init_chat_model`
The [`init_chat_model`](https://python.langchain.com/docs/how_to/chat_models_universal_init/) utility simplifies model initialization with configurable parameters:
```python
fromlangchain.chat_modelsimportinit_chat_model
model=init_chat_model(
"anthropic:claude-3-7-sonnet-latest",
temperature=0,
max_tokens=2048
)
```
Refer to the [API reference](https://python.langchain.com/api_reference/langchain/chat_models/langchain.chat_models.base.init_chat_model.html) for advanced options.
## Using provider-specific LLMs
If a model provider is not available via `init_chat_model`, you can instantiate the provider's model class directly. The model must implement the [BaseChatModel interface](https://python.langchain.com/api_reference/core/language_models/langchain_core.language_models.chat_models.BaseChatModel.html) and support tool calling:
```python
fromlangchain_anthropicimportChatAnthropic
fromlanggraph.prebuiltimportcreate_react_agent
model=ChatAnthropic(
model="claude-3-7-sonnet-latest",
temperature=0,
max_tokens=2048
)
agent=create_react_agent(
# highlight-next-line
model=model,
# other parameters
)
```
!!! note "Illustrative example"
The example above uses `ChatAnthropic`, which is already supported by `init_chat_model`. This pattern is shown to illustrate how to manually instantiate a model not available through init_chat_model.
## Disable streaming
To disable streaming of the individual LLM tokens, set `disable_streaming=True` when initializing the model:
=== "`init_chat_model`"
```python
from langchain.chat_models import init_chat_model
model = init_chat_model(
"anthropic:claude-3-7-sonnet-latest",
# highlight-next-line
disable_streaming=True
)
```
=== "`ChatModel`"
```python
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(
model="claude-3-7-sonnet-latest",
# highlight-next-line
disable_streaming=True
)
```
Refer to the [API reference](https://python.langchain.com/api_reference/core/language_models/langchain_core.language_models.chat_models.BaseChatModel.html#langchain_core.language_models.chat_models.BaseChatModel.disable_streaming) for more information on `disable_streaming`
## Adding model fallbacks
You can add a fallback to a different model or a different LLM provider using `model.with_fallbacks([...])`:
A single agent might struggle if it needs to specialize in multiple domains or manage many tools. To tackle this, you can break your agent into smaller, independent agents and composing them into a [multi-agent system](../concepts/multi_agent.md).
In multi-agent systems, agents need to communicate between each other. They do so via [handoffs](#handoffs) — a primitive that describes which agent to hand control to and the payload to send to that agent.
Two of the most popular multi-agent architectures are:
- [supervisor](#supervisor) — individual agents are coordinated by a central supervisor agent. The supervisor controls all communication flow and task delegation, making decisions about which agent to invoke based on the current context and task requirements.
- [swarm](#swarm) — agents dynamically hand off control to one another based on their specializations. The system remembers which agent was last active, ensuring that on subsequent interactions, the conversation resumes with that agent.
## Supervisor

Use [`langgraph-supervisor`](https://github.com/langchain-ai/langgraph-supervisor-py) library to create a supervisor multi-agent system:
```bash
pip install langgraph-supervisor
```
```python
fromlangchain_openaiimportChatOpenAI
fromlanggraph.prebuiltimportcreate_react_agent
# highlight-next-line
fromlanggraph_supervisorimportcreate_supervisor
defbook_hotel(hotel_name:str):
"""Book a hotel"""
returnf"Successfully booked a stay at {hotel_name}."
defbook_flight(from_airport:str,to_airport:str):
"""Book a flight"""
returnf"Successfully booked a flight from {from_airport} to {to_airport}."
flight_assistant=create_react_agent(
model="openai:gpt-4o",
tools=[book_flight],
prompt="You are a flight booking assistant",
# highlight-next-line
name="flight_assistant"
)
hotel_assistant=create_react_agent(
model="openai:gpt-4o",
tools=[book_hotel],
prompt="You are a hotel booking assistant",
# highlight-next-line
name="hotel_assistant"
)
# highlight-next-line
supervisor=create_supervisor(
agents=[flight_assistant,hotel_assistant],
model=ChatOpenAI(model="gpt-4o"),
prompt=(
"You manage a hotel booking assistant and a"
"flight booking assistant. Assign work to them."
)
).compile()
forchunkinsupervisor.stream(
{
"messages":[
{
"role":"user",
"content":"book a flight from BOS to JFK and a stay at McKittrick Hotel"
}
]
}
):
print(chunk)
print("\n")
```
## Swarm

Use [`langgraph-swarm`](https://github.com/langchain-ai/langgraph-swarm-py) library to create a swarm multi-agent system:
description="Transfer user to the hotel-booking assistant.",
)
transfer_to_flight_assistant=create_handoff_tool(
agent_name="flight_assistant",
description="Transfer user to the flight-booking assistant.",
)
flight_assistant=create_react_agent(
model="anthropic:claude-3-5-sonnet-latest",
# highlight-next-line
tools=[book_flight,transfer_to_hotel_assistant],
prompt="You are a flight booking assistant",
# highlight-next-line
name="flight_assistant"
)
hotel_assistant=create_react_agent(
model="anthropic:claude-3-5-sonnet-latest",
# highlight-next-line
tools=[book_hotel,transfer_to_flight_assistant],
prompt="You are a hotel booking assistant",
# highlight-next-line
name="hotel_assistant"
)
# highlight-next-line
swarm=create_swarm(
agents=[flight_assistant,hotel_assistant],
default_active_agent="flight_assistant"
).compile()
forchunkinswarm.stream(
{
"messages":[
{
"role":"user",
"content":"book a flight from BOS to JFK and a stay at McKittrick Hotel"
}
]
}
):
print(chunk)
print("\n")
```
## Handoffs
A common pattern in multi-agent interactions is **handoffs**, where one agent *hands off* control to another. Handoffs allow you to specify:
- **destination**: target agent to navigate to
- **payload**: information to pass to that agent
This is used both by `langgraph-supervisor` (supervisor hands off to individual agents) and `langgraph-swarm` (an individual agent can hand off to other agents).
To implement handoffs with `create_react_agent`, you need to:
1. Create a special tool that can transfer control to a different agent
```python
def transfer_to_bob():
"""Transfer to bob."""
return Command(
# name of the agent (node) to go to
# highlight-next-line
goto="bob",
# data to send to the agent
# highlight-next-line
update={"messages": [...]},
# indicate to LangGraph that we need to navigate to
# agent node in a parent graph
# highlight-next-line
graph=Command.PARENT,
)
```
1. Create individual agents that have access to handoff tools:
1. Define a parent graph that contains individual agents as nodes:
```python
from langgraph.graph import StateGraph, MessagesState
multi_agent_graph = (
StateGraph(MessagesState)
.add_node(flight_assistant)
.add_node(hotel_assistant)
...
)
```
Putting this together, here is how you can implement a simple multi-agent system with two agents — a flight booking assistant and a hotel booking assistant:
```python
from typing import Annotated
from langchain_core.tools import tool, InjectedToolCallId
from langgraph.prebuilt import create_react_agent, InjectedState
from langgraph.graph import StateGraph, START, MessagesState
return f"Successfully booked a flight from {from_airport} to {to_airport}."
# Define agents
flight_assistant = create_react_agent(
model="anthropic:claude-3-5-sonnet-latest",
# highlight-next-line
tools=[book_flight, transfer_to_hotel_assistant],
prompt="You are a flight booking assistant",
# highlight-next-line
name="flight_assistant"
)
hotel_assistant = create_react_agent(
model="anthropic:claude-3-5-sonnet-latest",
# highlight-next-line
tools=[book_hotel, transfer_to_flight_assistant],
prompt="You are a hotel booking assistant",
# highlight-next-line
name="hotel_assistant"
)
# Define multi-agent graph
multi_agent_graph = (
StateGraph(MessagesState)
.add_node(flight_assistant)
.add_node(hotel_assistant)
.add_edge(START, "flight_assistant")
.compile()
)
# Run the multi-agent graph
for chunk in multi_agent_graph.stream(
{
"messages": [
{
"role": "user",
"content": "book a flight from BOS to JFK and a stay at McKittrick Hotel"
}
]
}
):
print(chunk)
print("\n")
```
1. Access agent's state
2. The `Command` primitive allows specifying a state update and a node transition as a single operation, making it useful for implementing handoffs.
3. Name of the agent or node to hand off to.
4. Take the agent's messages and **add** them to the parent's **state** as part of the handoff. The next agent will see the parent state.
5. Indicate to LangGraph that we need to navigate to agent node in a **parent** multi-agent graph.
!!! Note
This handoff implementation assumes that:
- each agent receives overall message history (across all agents) in the multi-agent system as its input
- each agent outputs its internal messages history to the overall message history of the multi-agent system
Check out LangGraph [supervisor](https://github.com/langchain-ai/langgraph-supervisor-py#customizing-handoff-tools) and [swarm](https://github.com/langchain-ai/langgraph-swarm-py#customizing-handoff-tools) documentation to learn how to customize handoffs.
**LangGraph** provides both low-level primitives and high-level prebuilt components for building agent-based applications. This section focuses on the **prebuilt**, **reusable** components designed to help you construct agentic systems quickly and reliably—without the need to implement orchestration, memory, or human feedback handling from scratch.
## Key features
LangGraph includes several capabilities essential for building robust, production-ready agentic systems:
- [**Memory integration**](./memory.md): Native support for *short-term* (session-based) and *long-term* (persistent across sessions) memory, enabling stateful behaviors in chatbots and assistants.
- [**Human-in-the-loop control**](./human-in-the-loop.md): Execution can pause *indefinitely* to await human feedback—unlike websocket-based solutions limited to real-time interaction. This enables asynchronous approval, correction, or intervention at any point in the workflow.
- [**Streaming support**](./streaming.md): Real-time streaming of agent state, model tokens, tool outputs, or combined streams.
- [**Deployment tooling**](./deployment.md): Includes infrastructure-free deployment tools. [**LangGraph Platform**](https://langchain-ai.github.io/langgraph/concepts/langgraph_platform/) supports testing, debugging, and deployment.
- **[Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/)**: A visual IDE for inspecting and debugging workflows.
- Supports multiple [**deployment options**](https://langchain-ai.github.io/langgraph/tutorials/deployment/) for production.
## High-level building blocks
LangGraph comes with a set of prebuilt components that implement common agent behaviors and workflows. These abstractions are built on top of the LangGraph framework, offering a faster path to production while remaining flexible for advanced customization.
Using LangGraph for agent development allows you to focus on your application's logic and behavior, instead of building and maintaining the supporting infrastructure for state, memory, and human feedback.
## Package ecosystem
The high-level components are organized into several packages, each with a specific focus.
To share your project, simply open a Pull Request adding an entry for your package in our [packages.yml](https://github.com/langchain-ai/langgraph/blob/main/docs/_scripts/third_party_page/packages.yml) file.
Agents support both synchronous and asynchronous execution using either `.invoke()` / `await .invoke()` for full responses, or `.stream()` / `.astream()` for **incremental** [streaming](streaming.md) output. This section explains how to provide input, interpret output, enable streaming, and control execution limits.
## Basic usage
Agents can be executed in two primary modes:
- **Synchronous** using `.invoke()` or `.stream()`
- **Asynchronous** using `await .invoke()` or `async for` with `.astream()`
=== "Sync invocation"
```python
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(...)
# highlight-next-line
response = agent.invoke({"messages": [{"role": "user", "content": "what is the weather in sf"}]})
```
=== "Async invocation"
```python
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(...)
# highlight-next-line
response = await agent.ainvoke({"messages": [{"role": "user", "content": "what is the weather in sf"}]})
```
## Inputs and outputs
Agents use a language model that expects a list of `messages` as an input. Therefore, agent inputs and outputs are stored as a list of `messages` under the `messages` key in the agent [state](../concepts/low_level.md#working-with-messages-in-graph-state).
## Input format
Agent input must be a dictionary with a `messages` key. Supported formats are:
| List of messages | `{"messages": [{"role": "user", "content": "Hello"}]}` |
| With custom state | `{"messages": [{"role": "user", "content": "Hello"}], "user_name": "Alice"}` — If using a custom `state_schema` |
Messages are automatically converted into LangChain's internal message format. You can read
more about [LangChain messages](https://python.langchain.com/docs/concepts/messages/#langchain-messages) in the LangChain documentation.
!!! tip "Using custom agent state"
You can provide additional fields defined in your agent’s state schema directly in the input dictionary. This allows dynamic behavior based on runtime data or prior tool outputs.
See the [context guide](./context.md) for full details.
!!! note
A string input for `messages` is converted to a [HumanMessage](https://python.langchain.com/docs/concepts/messages/#humanmessage). This behavior differs from the `prompt` parameter in `create_react_agent`, which is interpreted as a [SystemMessage](https://python.langchain.com/docs/concepts/messages/#systemmessage) when passed as a string.
## Output format
Agent output is a dictionary containing:
- `messages`: A list of all messages exchanged during execution (user input, assistant replies, tool invocations).
- Optionally, `structured_response` if [structured output](./agents.md#structured-output) is configured.
- If using a custom `state_schema`, additional keys corresponding to your defined fields may also be present in the output. These can hold updated state values from tool execution or prompt logic.
See the [context guide](./context.md) for more details on working with custom state schemas and accessing context.
## Streaming output
Agents support streaming responses for more responsive applications. This includes:
- **Progress updates** after each step
- **LLM tokens** as they're generated
- **Custom tool messages** during execution
Streaming is available in both sync and async modes:
=== "Sync streaming"
```python
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
stream_mode="updates"
):
print(chunk)
```
=== "Async streaming"
```python
async for chunk in agent.astream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
stream_mode="updates"
):
print(chunk)
```
!!! tip
For full details, see the [streaming guide](./streaming.md).
## Max iterations
To control agent execution and avoid infinite loops, set a recursion limit. This defines the maximum number of steps the agent can take before raising a `GraphRecursionError`. You can configure `recursion_limit` at runtime or when defining agent via `.with_config()`:
=== "Runtime"
```python
from langgraph.errors import GraphRecursionError
from langgraph.prebuilt import create_react_agent
max_iterations = 3
# highlight-next-line
recursion_limit = 2 * max_iterations + 1
agent = create_react_agent(
model="anthropic:claude-3-5-haiku-latest",
tools=[get_weather]
)
try:
response = agent.invoke(
{"messages": [{"role": "user", "content": "what's the weather in sf"}]},
To stream agent progress, use the [`stream()`][langgraph.graph.state.CompiledStateGraph.stream] or [`astream()`][langgraph.graph.state.CompiledStateGraph.astream] methods with [`stream_mode="updates"`](https://langchain-ai.github.io/langgraph/how-tos/streaming/#updates). This emits an event after every agent step.
For example, if you have an agent that calls a tool once, you should see the following updates:
* **LLM node**: AI message with tool call requests
* **Tool node**: Tool message with execution result
* **LLM node**: Final AI response
=== "Sync"
```python
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
)
# highlight-next-line
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line
stream_mode="updates"
):
print(chunk)
print("\n")
```
=== "Async"
```python
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
)
# highlight-next-line
async for chunk in agent.astream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line
stream_mode="updates"
):
print(chunk)
print("\n")
```
## LLM tokens
To stream tokens as they are produced by the LLM, use `stream_mode="messages"`:
=== "Sync"
```python
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
)
# highlight-next-line
for token, metadata in agent.stream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line
stream_mode="messages"
):
print("Token", token)
print("Metadata", metadata)
print("\n")
```
=== "Async"
```python
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
)
# highlight-next-line
async for token, metadata in agent.astream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line
stream_mode="messages"
):
print("Token", token)
print("Metadata", metadata)
print("\n")
```
## Tool updates
To stream updates from tools as they are executed, you can use [get_stream_writer][langgraph.config.get_stream_writer].
=== "Sync"
```python
# highlight-next-line
from langgraph.config import get_stream_writer
def get_weather(city: str) -> str:
"""Get weather for a given city."""
# highlight-next-line
writer = get_stream_writer()
# stream any arbitrary data
# highlight-next-line
writer(f"Looking up data for city: {city}")
return f"It's always sunny in {city}!"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
)
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line
stream_mode="custom"
):
print(chunk)
print("\n")
```
=== "Async"
```python
# highlight-next-line
from langgraph.config import get_stream_writer
def get_weather(city: str) -> str:
"""Get weather for a given city."""
# highlight-next-line
writer = get_stream_writer()
# stream any arbitrary data
# highlight-next-line
writer(f"Looking up data for city: {city}")
return f"It's always sunny in {city}!"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
)
async for chunk in agent.astream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line
stream_mode="custom"
):
print(chunk)
print("\n")
```
!!! Note
If you add `get_stream_writer` inside your tool, you won't be able to invoke the tool outside of a LangGraph execution context.
## Stream multiple modes
You can specify multiple streaming modes by passing stream mode as a list: `stream_mode=["updates", "messages", "custom"]`:
=== "Sync"
```python
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
)
for stream_mode, chunk in agent.stream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line
stream_mode=["updates", "messages", "custom"]
):
print(chunk)
print("\n")
```
=== "Async"
```python
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
)
async for stream_mode, chunk in agent.astream(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
# highlight-next-line
stream_mode=["updates", "messages", "custom"]
):
print(chunk)
print("\n")
```
## Disable streaming
In some applications you might need to disable streaming of individual tokens for a given model. This is useful in [multi-agent](./multi-agent.md) systems to control which agents stream their output.
See the [Models](./models.md#disable-streaming) guide to learn how to disable streaming.
## Additional resources
* [Streaming in LangGraph](https://langchain-ai.github.io/langgraph/how-tos/streaming)
[Tools](https://python.langchain.com/docs/concepts/tools/) are a way to encapsulate a function and its input schema in a way that can be passed to a chat model that supports tool calling. This allows the model to request the execution of this function with specific inputs.
You can either [define your own tools](#define-simple-tools) or use [prebuilt integrations](#prebuilt-tools) that LangChain provides.
## Define simple tools
You can pass a vanilla function to `create_react_agent` to use as a tool:
```python
fromlanggraph.prebuiltimportcreate_react_agent
defmultiply(a:int,b:int)->int:
"""Multiply two numbers."""
returna*b
create_react_agent(
model="anthropic:claude-3-7-sonnet",
tools=[multiply]
)
```
`create_react_agent` automatically converts vanilla functions to [LangChain tools](https://python.langchain.com/docs/concepts/tools/#tool-interface).
## Customize tools
For more control over tool behavior, use the `@tool` decorator:
```python
# highlight-next-line
fromlangchain_core.toolsimporttool
# highlight-next-line
@tool("multiply_tool",parse_docstring=True)
defmultiply(a:int,b:int)->int:
"""Multiply two numbers.
Args:
a: First operand
b: Second operand
"""
returna*b
```
You can also define a custom input schema using Pydantic:
{"messages":[{"role":"user","content":"Hi, I am Bob"}]}
)
```
!!! Warning "Avoid infinite loops"
Forcing tool usage without stopping conditions can create infinite loops. Use one of the following safeguards:
- Mark the tool with [`return_direct=True`](#return-tool-results-directly) to end the loop after execution.
- Set [`recursion_limit`](../concepts/low_level.md#recursion-limit) to restrict the number of execution steps.
## Handle tool errors
By default, the agent will catch all exceptions raised during tool calls and will pass those as tool messages to the LLM. To control how the errors are handled, you can use the prebuilt [`ToolNode`][langgraph.prebuilt.tool_node.ToolNode] — the node that executes tools inside `create_react_agent` — via its `handle_tool_errors` parameter:
=== "Enable error handling (default)"
```python
from langgraph.prebuilt import create_react_agent
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
if a == 42:
raise ValueError("The ultimate error")
return a * b
# Run with error handling (default)
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[multiply]
)
agent.invoke(
{"messages": [{"role": "user", "content": "what's 42 x 7?"}]}
)
```
=== "Disable error handling"
```python
from langgraph.prebuilt import create_react_agent, ToolNode
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
if a == 42:
raise ValueError("The ultimate error")
return a * b
# highlight-next-line
tool_node = ToolNode(
[multiply],
# highlight-next-line
handle_tool_errors=False # (1)!
)
agent_no_error_handling = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=tool_node
)
agent_no_error_handling.invoke(
{"messages": [{"role": "user", "content": "what's 42 x 7?"}]}
)
```
1. This disables error handling (enabled by default). See all available strategies in the [API reference][langgraph.prebuilt.tool_node.ToolNode].
=== "Custom error handling"
```python
from langgraph.prebuilt import create_react_agent, ToolNode
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
if a == 42:
raise ValueError("The ultimate error")
return a * b
# highlight-next-line
tool_node = ToolNode(
[multiply],
# highlight-next-line
handle_tool_errors=(
"Can't use 42 as a first operand, you must switch operands!" # (1)!
)
)
agent_custom_error_handling = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=tool_node
)
agent_custom_error_handling.invoke(
{"messages": [{"role": "user", "content": "what's 42 x 7?"}]}
)
```
1. This provides a custom message to send to the LLM in case of an exception. See all available strategies in the [API reference][langgraph.prebuilt.tool_node.ToolNode].
See [API reference][langgraph.prebuilt.tool_node.ToolNode] for more information on different tool error handling options.
## Working with memory
LangGraph allows access to short-term and long-term memory from tools. See [Memory](./memory.md) guide for more information on:
* how to [read](./memory.md#read-short-term) from and [write](./memory.md#write-short-term) to **short-term** memory
* how to [read](./memory.md#read-long-term) from and [write](./memory.md#write-long-term) to **long-term** memory
## Prebuilt tools
LangChain supports a wide range of prebuilt tool integrations for interacting with APIs, databases, file systems, web data, and more. These tools extend the functionality of agents and enable rapid development.
You can browse the full list of available integrations in the [LangChain integrations directory](https://python.langchain.com/docs/integrations/tools/).
You can use a prebuilt chat UI for interacting with any LangGraph agent through the [Agent Chat UI](https://github.com/langchain-ai/agent-chat-ui). Using the [deployed version](https://agentchat.vercel.app) is the quickest way to get started, and allows you to interact with both local and deployed graphs.
## Run agent in UI
First, set up LangGraph API server [locally](./deployment.md#launch-langgraph-server-locally) or deploy your agent on [LangGraph Cloud](https://langchain-ai.github.io/langgraph/cloud/quick_start/).
Then, navigate to [Agent Chat UI](https://agentchat.vercel.app), or clone the repository and [run the dev server locally](https://github.com/langchain-ai/agent-chat-ui?tab=readme-ov-file#setup):
UI has out-of-box support for rendering tool calls, and tool result messages. To customize what messages are shown, see the [Hiding Messages in the Chat](https://github.com/langchain-ai/agent-chat-ui?tab=readme-ov-file#hiding-messages-in-the-chat) section in the Agent Chat UI documentation.
## Add human-in-the-loop
Agent Chat UI has full support for [human-in-the-loop](../concepts/human_in_the_loop.md) workflows. To try it out, replace the agent code in `src/agent/graph.py` (from the [deployment](./deployment.md) guide) with this [agent implementation](./human-in-the-loop.md#using-with-agent-inbox):
Agent Chat UI works best if your LangGraph agent interrupts using the [`HumanInterrupt` schema][langgraph.prebuilt.interrupt.HumanInterrupt]. If you do not use that schema, the Agent Chat UI will be able to render the input passed to the `interrupt` function, but it will not have full support for resuming your graph.
## Generative UI
You can also use generative UI in the Agent Chat UI.
Generative UI allows you to define [React](https://react.dev/) components, and push them to the UI from the LangGraph server. For more documentation on building generative UI LangGraph agents, read [these docs](https://langchain-ai.github.io/langgraph/cloud/how-tos/generative_ui_react/).
LangGraph Cloud is available within <a href="https://www.langchain.com/langsmith" target="_blank">LangSmith</a>. To deploy a LangGraph Cloud API, navigate to the <a href="https://smith.langchain.com/" target="_blank">LangSmith UI</a>.
Before deploying, review the [conceptual guide for the Cloud SaaS](../../concepts/langgraph_cloud.md) deployment option.
## Prerequisites
@@ -107,13 +107,13 @@ After installing and authorizing LangChain's `hosted-langserve` GitHub app, repo
All traffic from `LangGraph Platform` deployments created after January 6th 2025 will come through a NAT gateway.
This NAT gateway will have several static ip addresses depending on the region you are deploying in. Refer to the table below for the list of IP addresses to whitelist:
Before deploying, review the [conceptual guide for the Self-Hosted Control Plane](../../concepts/langgraph_self_hosted_control_plane.md) deployment option.
## Prerequisites
1. You are using Kubernetes.
1. You have self-hosted LangSmith deployed.
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to [test your application locally](./test_locally.md).
1. Use the [LangGraph CLI](../../concepts/langgraph_cli.md) to build a Docker image (i.e. `langgraph build`) and push it to a registry your Kubernetes cluster has access to.
helm install keda kedacore/keda --namespace keda --create-namespace
1. Ingress Configuration
1. You must set up an ingress for your LangSmith instance. All agents will be deployed as Kubernetes services behind this ingress.
1. You can use this guide to [set up an ingress](https://docs.smith.langchain.com/self_hosting/configuration/ingress) for your instance.
1. You have slack space in your cluster for multiple deployments. `Cluster-Autoscaler` is recommended to automatically provision new nodes.
1. A valid Dynamic PV provisioner or PVs available on your cluster. You can verify this by running:
kubectl get storageclass
## Setup
1. As part of configuring your Self-Hosted LangSmith instance, you enable the `langgraphPlatform` option. This will provision a few key resources.
1.`listener`: This is a service that listens to the [control plane](../../concepts/langgraph_control_plane.md) for changes to your deployments and creates/updates downstream CRDs.
1.`LangGraphPlatform CRD`: A CRD for LangGraph Platform deployments. This contains the spec for managing an instance of a LangGraph platform deployment.
1.`operator`: This operator handles changes to your LangGraph Platform CRDs.
1.`host-backend`: This is the [control plane](../../concepts/langgraph_control_plane.md).
1. Two additional images will be used by the chart.
1. In your `values.yaml` file, configure the `hostBackendImage` and `operatorImage` options (if you need to mirror images)
1. You can also configure base templates for your agents by overriding the base templates [here](https://github.com/langchain-ai/helm/blob/main/charts/langsmith/values.yaml#L898).
1. You create a deployment from the [Control Plane UI](../../concepts/langgraph_control_plane.md#control-plane-ui).
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.