使用说明
LangGraph Python后端带React TypeScript前端
🤓 太长,读不下去了
LangGraph 后端使用 快速API 和 WebSocket 沟通 反应 前端,显示模型生成
响应和流式传输,作为模板变得简单。运行通过 pip install -r requirements.txt 然后
./start-local.sh --backend。查看下面的更多信息!
🏛 背景
作为 LangGraph 随着技术的发展,将需要建立实现无缝交互的最佳方式 在...之间 LangGraph 以及任何前端框架。LLM聊天设置中UI的一个关键方面是确保 机器人是 积极地 正在处理您发送的回复。确认可以采取各种形式,例如 作为一种视觉辅助工具,如旋转器、吐司、状态/进度条,或在生成令牌时流式传输令牌。 这是一个模板,旨在融合前端和后端之间的核心概念,以提供出色的用户体验 对于不了解LLM及其局限性的人来说,这是一种经验。
我发现流媒体方法非常成功,表明LLM认可了我的信息
正在生成响应。我使用WebSockets来处理前端之间的连接和通信
后端,便于集成 _LangChain_s 异步流事件
和 _反应_s 使用效果挂钩。此设置允许应用程序在以下时间等待机器人的响应
在生成令牌时同时流式传输令牌。我的目标是让它简单明了,有据可查
尽可能让任何人都可以克隆、启动和继续这个项目,包括直接替换
您的LangGraph可在中运行 graph.py,只需要 useWebSocket.ts 任何前端的实现。
🔍 项目概述
Project Tree
.
├── Dockerfile # Shipable blueprint Dockerfile
├── Procfile # web command to run on servers (PaaS eg. Heroku)
├── README.md
├── cust_logger.py # Custom logger utility for logging for backend serving
├── frontend
│ ├── package.json # Contains metadata and dependencies for React project
│ ├── public # Default Create-React-App Public assets
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── src # Source code for the React frontend
│ │ ├── App.css
│ │ ├── App.tsx # Main App component in TypeScript
│ │ ├── components # Demo components used in App.tsx
│ │ │ └── easter_egg # ssshhhh... try chatting "LangChain"
│ │ │ ├── ee.css
│ │ │ └── ee.tsx
│ │ ├── index.tsx # Entry point for the React app
│ │ ├── react-app-env.d.ts # Default from Create-React-App
│ │ └── services
│ │ └── useWebSocket.ts # WebSocket hook for real-time communication via WebSocket
│ └── tsconfig.json # TypeScript configuration for the frontend
├── graph.py # Simple LangGraph implementation
├── requirements.txt # Python dependencies required for the backend project
├── server.py # FastAPI implementation
└── start-local.sh # Shell script to start the application locally (backend or frontend)
📡 沟通
WebSocket连接允许React前端之间的实时双向通信
FastAPI后端服务器持续接收消息,无需重新加载或重复请求。
有很多方法可以处理发送的数据。这可能是消息本身,一大块消息
(msg_history),或结构化格式。我选择使用结构化格式,因为它允许客户端
并且服务器期望特定顺序的某些密钥,并为未来添加到有效载荷提供灵活性。
我选择使用JSON,因为它最容易阅读和使用,但你可以自由使用其他格式,如XML,
YAML、CSV、协议缓冲区(Protobuff)或BSON。简而言之,双方都有一个预定义的JSON结构
正在寻找。如果在交换过程中,无论是前端还是后端,都满足某些条件,它将触发
在特定方面采取行动。例如,如果事件类型为 on_chat_model_stream,它应该流
对客户的回应。或者,如果客户端在套接字连接中收到任何更改,它将进行调整和
相应地渲染或记录。
我们的沟通交流: 从代码库中的注释中了解更多关于每个函数的作用,或者在 演示.
🖥️ 前端
前端有一些功能可以启动:
- 您可以通过指示器知道您已连接到服务器。
- 使用多行打字(
Shift + Enter)和正常Enter发送消息的键。 - 生成响应时会自动滚动。
- 如果服务器停机,WebSocket会自动重试重新连接和消息传递。
- 前端是无状态的,这意味着消息不会存储在前端,而是作为
msg_history;只发送一条消息。 - 关于如何使用自定义LangGraph事件触发前端组件的说明:
- 试着向模型询问LangChain或LangGraph!D
🛠️ 后端
我试图在保持功能的同时保持后端非常纤薄。它使用FastAPI和Uvicorn来处理异步请求。一些功能包括:
-
简单的FastAPI实现
- 提供从生成的静态web文件
npm build. - 允许灵活性
React Router. - 提供简单的WebSocket连接。
- 提供从生成的静态web文件
-
表达性测井
- 匹配Uvicorn的日志格式。
- 使用
file_name:line_num格式便于日志跟踪。 - 支持自定义日志颜色,允许您在任何文件中设置相应的颜色。
- 日志示例:
INFO: server.py:34 - {"timestamp": "YYYY-MM-DDTHH:MM:SS.MS", "uuid": "nearer-zebra-one-worker", "received": {"uuid": "oldest-honor-create-card", "message": "what is e?", "init": false}} INFO: graph.py:86 - {"timestamp": "YYYY-MM-DDTHH:MM:SS.MS", "uuid": "nearer-zebra-one-worker", "llm_method": "on_chat_model_end", "sent": "2.718281828459045"} - JSON中一致的日志格式和输出允许轻松导入任何可观察性系统,由时间序列中的时间戳、会话UUID或LLM函数调用指定。
- 日志演示
-
简单图形
- 此图示例是一个没有工具调用的通用调用模型,但可以很容易地用您自己的图替换。
- 为简单起见,它目前使用会话UUID来指定线程 内存持久性 使用内存保护程序。
- 这允许在后端的WebSocket通道中维护会话历史,确保客户端在数据丢失或刷新时不会被定制为渲染和存储。
-
事件处理器
- 将WebSocket响应发送回指定的客户端。
- 目前,您可以在图形中处理聊天流和自定义事件,例如触发前端组件或标志,使用
adispatch_custom_event和astream_events.
🚀 让我们运行它
🤖 自动运行
我试图在本地尽可能简单地进行设置和运行 此脚本用于启动应用程序的前端和后端。它接受各种命令行选项来控制要运行的组件及其各自的配置。
在开始之前,请确保 pip install -r requirements.txt,我建议使用你个人风格的虚拟环境,由于用户的假设,我没有自动设置pip。
# required libraries
fastapi # Fast web framework to handle our APIs
uvicorn # ASGI server for serving FastAPI applications
websockets # core WebSocket server and client library
python-dotenv # reads key-value pairs from a `.env` file and sets them as environment variables
langchain-openai # LangChain x OpenAI's implementation to use OpenAI's ChatModel within Langchain ecosystem
langgraph # An awesome talking parrot that shoots webs :D
colorama # used for coloring logs
./start-local.sh 选项
请阅读 start-local.sh 了解后台运行的别名命令。
重要:您不能使用 --frontend 和 --backend 同时。
-
--frontend:- 启动前端应用程序。
- 例子:
./start-local.sh --frontend
-
--backend:- 默认情况下,启动后端应用程序并在启动后端之前构建前端。
- 例子:
./start-local.sh --backend
-
--nobuild可选标志:- 在开始后端之前跳过构建前端。此选项仅在启动后端时相关。
- 例子:
./start-local.sh --backend --nobuild
-
--frontend-port <port>可选标志:- 指定前端应用程序应在其上运行的端口。默认端口为
3000. - 例子:
./start-local.sh --frontend --frontend-port 4000
- 指定前端应用程序应在其上运行的端口。默认端口为
-
--backend-port <port>可选标志:- 指定后端应用程序应在其上运行的端口。默认端口为
8000. - 例子:
./start-local.sh --backend --backend-port 9000
- 指定后端应用程序应在其上运行的端口。默认端口为
-
使用限制:
- 这
--frontend和--backend选项不能一起使用。 - 请选择一个,然后打开一个新终端,用另一个终端运行脚本。
- 这是由于重叠的阻塞客户端和服务器控制台,您可以将它们放在后台,但这会变得很麻烦
kill $PID在开发工作流程中。
- 这
🖐 ️手动启动应用程序
如果您不想使用 start-local.sh 脚本,您可以使用以下步骤手动启动前端和后端组件。
我建议使用2个不同的终端选项卡。
- 对于 前端,导航到
frontend目录并安装所需的包,然后使用您喜欢的node包管理器运行:cd frontend npm install npm run - 对于 后端,位于项目文件夹的根目录中。
pip install -r requirements.txt cd frontend npm install npm build cd .. uvicorn server:app --host 0.0.0.0 --port 8000 --reload
🎥 演示
正如我们在上面看到的,即使两个人在托管服务器上打字,它也可以以非阻塞的方式处理它 并且在不损失响应吞吐量的情况下为两个客户端提供服务。
下面是它从服务器上看起来的样子,可能不是RGB格式:D
🚢 部署
有一个Dockerfile和一个Procfile;两者都可以用于将服务器作为独立的Docker容器运行,或者 在您的解决方案中。我建议使用Docker Compose,通过Helm charts在Kubernetes上轻松部署, 以及用于创建具有VTCL/HZTL缩放的简单副本。如果您的解决方案需要处理端口和SSL 需要它。
注意:此服务器的后端不安全。 由于localhost是HTTP,它目前没有使用WebSocket Secure 以及缺乏便于本地运行的核心安全功能。
我建议在前端添加这样的内容:
const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
const wsUrl = `${wsProtocol}://${window.location.hostname}/ws`;
此外,我建议对传入的服务器请求进行原产地检查,并使用JWT令牌保护您的API, OAuth的承载令牌、API密钥等。还可以考虑对WebSocket连接和处理实施速率限制 安全地重新连接,作为防止DDoS攻击的预防措施。
💡 思考与未来改进
这是一个有趣的实现,将两种你想一起使用但没有明确定义的技术混合在一起, 尤其是在两者都可以很好地利用而彼此不受限制的情况下。
可以做出许多改进,但这些改进并不是为了保持简单性和避免潜在的 为未来的用户锁定。我愿意为不同的用例创建一个新模板或添加到这个存储库中, 可能包括:
- 工具调用可以通过LangGraph中的任何图实现,但它不是作为前端可视化更新处理的。
- 存储在内存中的对话可以从专用的网络数据库解决方案中受益。
- 共享对话或通过对话ID查找对话。
- 按用户处理对话分组需要对用户进行某种形式的区分。
以下是我希望在未来做出的改进:
- 前端和后端测试
- Kubernetes(K8s)Docker Compose设置
- 如果后端太大,可能会把它放在一个文件夹里
- 移动到vite模板,因为这是FE前进的方向
