feat: Add example repo for agent inbox and LangGraph

This commit is contained in:
bracesproul
2025-01-26 15:20:50 -08:00
parent 049ec8952e
commit 54b433b3db
17 changed files with 1302 additions and 146 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
{
"python.languageServer": "None"
}
+51 -61
View File
@@ -1,85 +1,75 @@
# New LangGraph Project
# Agent Inbox LangGraph Example
[![CI](https://github.com/langchain-ai/new-langgraph-project/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/langchain-ai/new-langgraph-project/actions/workflows/unit-tests.yml)
[![Integration Tests](https://github.com/langchain-ai/new-langgraph-project/actions/workflows/integration-tests.yml/badge.svg)](https://github.com/langchain-ai/new-langgraph-project/actions/workflows/integration-tests.yml)
[![Open in - LangGraph Studio](https://img.shields.io/badge/Open_in-LangGraph_Studio-00324d.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NS4zMzMiIGhlaWdodD0iODUuMzMzIiB2ZXJzaW9uPSIxLjAiIHZpZXdCb3g9IjAgMCA2NCA2NCI+PHBhdGggZD0iTTEzIDcuOGMtNi4zIDMuMS03LjEgNi4zLTYuOCAyNS43LjQgMjQuNi4zIDI0LjUgMjUuOSAyNC41QzU3LjUgNTggNTggNTcuNSA1OCAzMi4zIDU4IDcuMyA1Ni43IDYgMzIgNmMtMTIuOCAwLTE2LjEuMy0xOSAxLjhtMzcuNiAxNi42YzIuOCAyLjggMy40IDQuMiAzLjQgNy42cy0uNiA0LjgtMy40IDcuNkw0Ny4yIDQzSDE2LjhsLTMuNC0zLjRjLTQuOC00LjgtNC44LTEwLjQgMC0xNS4ybDMuNC0zLjRoMzAuNHoiLz48cGF0aCBkPSJNMTguOSAyNS42Yy0xLjEgMS4zLTEgMS43LjQgMi41LjkuNiAxLjcgMS44IDEuNyAyLjcgMCAxIC43IDIuOCAxLjYgNC4xIDEuNCAxLjkgMS40IDIuNS4zIDMuMi0xIC42LS42LjkgMS40LjkgMS41IDAgMi43LS41IDIuNy0xIDAtLjYgMS4xLS44IDIuNi0uNGwyLjYuNy0xLjgtMi45Yy01LjktOS4zLTkuNC0xMi4zLTExLjUtOS44TTM5IDI2YzAgMS4xLS45IDIuNS0yIDMuMi0yLjQgMS41LTIuNiAzLjQtLjUgNC4yLjguMyAyIDEuNyAyLjUgMy4xLjYgMS41IDEuNCAyLjMgMiAyIDEuNS0uOSAxLjItMy41LS40LTMuNS0yLjEgMC0yLjgtMi44LS44LTMuMyAxLjYtLjQgMS42LS41IDAtLjYtMS4xLS4xLTEuNS0uNi0xLjItMS42LjctMS43IDMuMy0yLjEgMy41LS41LjEuNS4yIDEuNi4zIDIuMiAwIC43LjkgMS40IDEuOSAxLjYgMi4xLjQgMi4zLTIuMy4yLTMuMi0uOC0uMy0yLTEuNy0yLjUtMy4xLTEuMS0zLTMtMy4zLTMtLjUiLz48L3N2Zz4=)](https://langgraph-studio.vercel.app/templates/open?githubUrl=https://github.com/langchain-ai/new-langgraph-project)
This template demonstrates a simple chatbot implemented using [LangGraph](https://github.com/langchain-ai/langgraph), designed for [LangGraph Studio](https://github.com/langchain-ai/langgraph-studio). The chatbot maintains persistent chat memory, allowing for coherent conversations across multiple interactions.
![Graph view in LangGraph studio UI](./static/studio_ui.png)
The core logic, defined in `src/agent/graph.py`, showcases a straightforward chatbot that responds to user queries while maintaining context from previous messages.
## What it does
The simple chatbot:
1. Takes a user **message** as input
2. Maintains a history of the conversation
3. Generates a response based on the current message and conversation history
4. Updates the conversation history with the new interaction
This template provides a foundation that can be easily customized and extended to create more complex conversational agents.
The repository contains a bare minimum code example to get started with the Agent Inbox with LangGraph.
## Getting Started
Assuming you have already [installed LangGraph Studio](https://github.com/langchain-ai/langgraph-studio?tab=readme-ov-file#download), to set up:
1. Create a `.env` file.
To get started, clone the repository:
```bash
cp .env.example .env
git clone https://github.com/langchain-ai/agent-inbox-example
```
2. Define required API keys in your `.env` file.
```bash
cd agent-inbox-example
```
<!--
Setup instruction auto-generated by `langgraph template lock`. DO NOT EDIT MANUALLY.
-->
Then, install the dependencies:
```bash
poetry install
```
Next, install the [LangGraph CLI](https://langchain-ai.github.io/langgraph/cloud/reference/cli/) if not already installed. We're installing the in-memory version so we can run the LangGraph server without Docker.
<!--
End setup instructions
-->
```bash
pip install -U "langgraph-cli[inmem]"
```
3. Customize the code as needed.
4. Open the folder in LangGraph Studio!
After this, we can start the LangGraph server:
## How to customize
```bash
langgraph dev
```
1. **Modify the system prompt**: The default system prompt is defined in [configuration.py](./src/agent/configuration.py). You can easily update this via configuration in the studio to change the chatbot's personality or behavior.
2. **Select a different model**: We default to Anthropic's Claude 3 Sonnet. You can select a compatible chat model using `provider/model-name` via configuration. Example: `openai/gpt-4-turbo-preview`.
3. **Extend the graph**: The core logic of the chatbot is defined in [graph.py](./src/agent/graph.py). You can modify this file to add new nodes, edges, or change the flow of the conversation.
This may take a second to start. Once the server is running, it should open a new browser tab to the LangGraph Studio through LangSmith. If this does not happen automatically, visit this URL:
[https://smith.langchain.com/studio/thread?baseUrl=http%3A%2F%2F127.0.0.1%3A2024](https://smith.langchain.com/studio/thread?baseUrl=http%3A%2F%2F127.0.0.1%3A2024)
You can also quickly extend this template by:
Now that our LangGraph server is running, we can start a new run in the Studio. To do this, simply enter any string into the `Interrupt Response` field, then click the `Submit` button. This will execute the graph, and interrupt on the `human_node`. Once the graph has interrupted, we can visit the Agent Inbox site to add your graph, and manage the interrupted thread.
- Adding custom tools or functions to enhance the chatbot's capabilities.
- Implementing additional logic for handling specific types of user queries or tasks.
- Integrating external APIs or databases to provide more dynamic responses.
## Agent Inbox Setup
## Development
Visit [`dev.agentinbox.ai`](https://dev.agentinbox.ai). If it's your first time visiting the site, you'll be prompted to add a new graph.
While iterating on your graph, you can edit past state and rerun your app from previous states to debug specific nodes. Local changes will be automatically applied via hot reload. Try experimenting with:
Enter the following fields into the form:
- Modifying the system prompt to give your chatbot a unique personality.
- Adding new nodes to the graph for more complex conversation flows.
- Implementing conditional logic to handle different types of user inputs.
- Graph/Assistant ID: `agent` - this corresponds to the ID of the graph defined in the [`langgraph.json`](./langgraph.json) file, or the ID of an assistant tied to your graph.
- Deployment URL: `http://127.0.0.1:2024` - this is the URL of the LangGraph server running locally.
- Name: `My Agent` - this is the optional name of the graph. You can set this to any value you want, or leave it empty and it'll be auto-assigned.
Follow-up requests will be appended to the same thread. You can create an entirely new thread, clearing previous history, using the `+` button in the top right.
Click `Submit` and watch your graph appear in the sidebar. This should automatically fetch the interrupted threads, but if it does not, click on the sidebar item & refresh the page. Once you've done this, you should see a single interrupted item in the table:
For more advanced features and examples, refer to the [LangGraph documentation](https://github.com/langchain-ai/langgraph). These resources can help you adapt this template for your specific use case and build more sophisticated conversational agents.
![Screenshot of the Agent Inbox](./static/agent_inbox_view.png)
LangGraph Studio also integrates with [LangSmith](https://smith.langchain.com/) for more in-depth tracing and collaboration with teammates, allowing you to analyze and optimize your chatbot's performance.
Click on this row, and you'll be taken to the interrupted item page. From here, you can respond in any way you'd like:
<!--
Configuration auto-generated by `langgraph template lock`. DO NOT EDIT MANUALLY.
{
"config_schemas": {
"agent": {
"type": "object",
"properties": {}
}
}
}
-->
- Accept
- Edit
- Respond
- Ignore
![Screenshot of an interrupted item in the Agent Inbox](./static/interrupted_item.png)
Once you take an action, the graph will resume the execution and end. The final state returned from the graph will be a string containing the type of action which was taken, and the value of the action args (unless `ignore` was chosen).
To view the result of the graph, go back to the LangGraph Studio and inspect the most recent thread results.
![Screenshot of the most recent thread results LangGraph Studio](./static/studio_thread_result.png)
## Go Deeper
If you'd like to go deeper, you can:
- Checkout the Agent Inbox docs, and codebase here: [github.com/langchain-ai/agent-inbox](https://github.com/langchain-ai/agent-inbox)
- See an open source Executive AI Assistant here: [github.com/langchain-ai/executive-ai-assistant](https://github.com/langchain-ai/executive-ai-assistant)
- See an open source Social Media Agent here: [github.com/langchain-ai/social-media-agent](https://github.com/langchain-ai/social-media-agent)
Generated
+1164
View File
File diff suppressed because it is too large Load Diff
+18 -25
View File
@@ -1,35 +1,28 @@
[project]
name = "agent"
[tool.poetry]
name = "agent-inbox-example"
version = "0.0.1"
description = "Starter template for making a new agent LangGraph."
authors = [
{ name = "William Fu-Hinthorn", email = "13333726+hinthornw@users.noreply.github.com" },
]
description = "Example repository for getting started with the Agent Inbox"
authors = ["Brace Sproul"]
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.9"
dependencies = [
"langgraph>=0.2.6",
"python-dotenv>=1.0.1",
license = "MIT"
packages = [
{ include = "agent", from = "src" }
]
[tool.poetry.dependencies]
python = ">=3.11,<4.0.0"
langgraph = ">=0.2.67"
python-dotenv = ">=1.0.1"
langgraph-sdk = ">=0.1.51"
pytest = "^8.3.4"
[project.optional-dependencies]
dev = ["mypy>=1.11.1", "ruff>=0.6.1"]
[tool.poetry.group.dev.dependencies]
mypy = ">=1.11.1"
ruff = ">=0.6.1"
[build-system]
requires = ["setuptools>=73.0.0", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = ["langgraph.templates.agent", "agent"]
[tool.setuptools.package-dir]
"langgraph.templates.agent" = "src/agent"
"agent" = "src/agent"
[tool.setuptools.package-data]
"*" = ["py.typed"]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.ruff]
lint.select = [
-28
View File
@@ -1,28 +0,0 @@
"""Define the configurable parameters for the agent."""
from __future__ import annotations
from dataclasses import dataclass, fields
from typing import Optional
from langchain_core.runnables import RunnableConfig
@dataclass(kw_only=True)
class Configuration:
"""The configuration for the agent."""
# Changeme: Add configurable values here!
# these values can be pre-set when you
# create assistants (https://langchain-ai.github.io/langgraph/cloud/how-tos/configuration_cloud/)
# and when you invoke the graph
my_configurable_param: str = "changeme"
@classmethod
def from_runnable_config(
cls, config: Optional[RunnableConfig] = None
) -> Configuration:
"""Create a Configuration instance from a RunnableConfig object."""
configurable = (config.get("configurable") or {}) if config else {}
_fields = {f.name for f in fields(cls) if f.init}
return cls(**{k: v for k, v in configurable.items() if k in _fields})
+63 -19
View File
@@ -1,38 +1,82 @@
"""Define a simple chatbot agent.
This agent returns a predefined response without using an actual LLM.
"""
"""Module for defining the agent's workflow graph and human interaction nodes."""
from typing import Any, Dict
from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph
from langgraph.prebuilt.interrupt import (
ActionRequest,
HumanInterrupt,
HumanInterruptConfig,
HumanResponse,
)
from langgraph.types import interrupt
from agent.configuration import Configuration
from agent.state import State
async def my_node(state: State, config: RunnableConfig) -> Dict[str, Any]:
"""Each node does work."""
configuration = Configuration.from_runnable_config(config)
# configuration = Configuration.from_runnable_config(config)
# You can use runtime configuration to alter the behavior of your
# graph.
async def human_node(state: State, config: RunnableConfig) -> Dict[str, Any]:
"""Call the interrupt function to pause the graph and handle user interaction.
Once resumed, it will log the type of action which was returned from
the interrupt function.
"""
# Define the interrupt request
action_request = ActionRequest(
action="Confirm Joke",
args={"joke": "What did the engineer say to the manager?"},
)
interrupt_config = HumanInterruptConfig(
allow_ignore=True, # Allow the user to `ignore` the interrupt.
allow_respond=True, # Allow the user to `respond` to the interrupt.
allow_edit=True, # Allow the user to `edit` the interrupt's args.
allow_accept=True, # Allow the user to `accept` the interrupt's args.
)
# The description will be rendered as markdown in the UI, so you may use markdown syntax.
description = (
"# Confirm Joke\n"
+ "Please carefully example the joke, and decide if you want to accept, edit, or ignore the joke."
+ "If you accept, the joke will be added to the conversation."
+ "If you edit and submit, the edited joke will be added to the conversation."
+ "If you ignore, the conversation will continue without adding the joke."
+ "If you respond, the response will be used to generate a new joke."
)
request = HumanInterrupt(
action_request=action_request, config=interrupt_config, description=description
)
human_response: HumanResponse = interrupt([request])[0]
if human_response.get("type") == "response":
message = f"User responded with: {human_response.get('args')}"
return {"interrupt_response": message}
elif human_response.get("type") == "accept":
message = f"User accepted with: {human_response.get('args')}"
return {"interrupt_response": message}
elif human_response.get("type") == "edit":
message = f"User edited with: {human_response.get('args')}"
return {"interrupt_response": message}
elif human_response.get("type") == "ignore":
message = "User ignored interrupt."
return {"interrupt_response": message}
return {
"changeme": "output from my_node. "
f"Configured with {configuration.my_configurable_param}"
"interrupt_response": "Unknown interrupt response type: " + str(human_response)
}
# Define a new graph
workflow = StateGraph(State, config_schema=Configuration)
workflow = StateGraph(State)
# Add the node to the graph
workflow.add_node("my_node", my_node)
# Add the node to the graph. This node will interrupt when it is invoked.
workflow.add_node("human_node", human_node)
# Set the entrypoint as `call_model`
workflow.add_edge("__start__", "my_node")
# Set the entrypoint as `human_node` so the first node will interrupt
workflow.add_edge("__start__", "human_node")
# Compile the workflow into an executable graph
graph = workflow.compile()
graph.name = "New Graph" # This defines the custom name in LangSmith
graph.name = "Agent Inbox Example" # This defines the custom name in LangSmith
+3 -8
View File
@@ -1,4 +1,4 @@
"""Define the state structures for the agent."""
"""State module for managing agent state."""
from __future__ import annotations
@@ -7,11 +7,6 @@ from dataclasses import dataclass
@dataclass
class State:
"""Defines the input state for the agent, representing a narrower interface to the outside world.
"""Class representing the state of the agent interaction."""
This class is used to define the initial state and structure of incoming data.
See: https://langchain-ai.github.io/langgraph/concepts/low_level/#state
for more information.
"""
changeme: str = "example"
interrupt_response: str = "example"
Binary file not shown.

After

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

-5
View File
@@ -1,5 +0,0 @@
from agent.configuration import Configuration
def test_configuration_empty() -> None:
Configuration.from_runnable_config({})