From 22cc9b109699edaba470e655527762e40fd98b41 Mon Sep 17 00:00:00 2001 From: root <953994191@qq.com> Date: Mon, 13 Apr 2026 23:57:16 +0800 Subject: [PATCH] =?UTF-8?q?```=20docs(.gitignore/README/QUICKSTART):=20?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3=E5=92=8C=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加IDE配置、日志和数据文件到.gitignore - 重构QUICKSTART.md,提供Docker Compose和本地开发两种部署方式 - 更新README.md,优化项目介绍和架构说明 - 移除旧的agent.py和backend.py文件 ``` --- .env.example | 9 + .gitea/workflows/deploy.yml | 61 ++++ .gitignore | 13 + QUICKSTART.md | 434 +++++++++++++++-------- README.md | 402 +++++++++++---------- app/__init__.py | 8 + agent.py => app/agent.py | 8 +- backend.py => app/backend.py | 2 +- graph_builder.py => app/graph_builder.py | 0 tools.py => app/tools.py | 0 data/user_docs/README.md | 16 + {user_docs => data/user_docs}/a.txt | 0 {user_docs => data/user_docs}/b.pdf | Bin {user_docs => data/user_docs}/c.xlsx | Bin docker/Dockerfile.backend | 22 ++ docker/Dockerfile.frontend | 15 + docker/docker-compose.yml | 63 ++++ frontend.py => frontend/frontend.py | 0 start.sh => scripts/start.sh | 8 +- test_multi_model.py | 134 ------- 20 files changed, 714 insertions(+), 481 deletions(-) create mode 100644 .env.example create mode 100644 .gitea/workflows/deploy.yml create mode 100644 app/__init__.py rename agent.py => app/agent.py (91%) rename backend.py => app/backend.py (99%) rename graph_builder.py => app/graph_builder.py (100%) rename tools.py => app/tools.py (100%) create mode 100644 data/user_docs/README.md rename {user_docs => data/user_docs}/a.txt (100%) rename {user_docs => data/user_docs}/b.pdf (100%) rename {user_docs => data/user_docs}/c.xlsx (100%) create mode 100644 docker/Dockerfile.backend create mode 100644 docker/Dockerfile.frontend create mode 100644 docker/docker-compose.yml rename frontend.py => frontend/frontend.py (100%) rename start.sh => scripts/start.sh (95%) mode change 100644 => 100755 delete mode 100644 test_multi_model.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5c84b6c --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# AI Agent 环境变量配置模板 +# 复制此文件为 .env 并填入真实的 API Key +LOCAL_MODEL_PATH=gemma-4-E2B-it +# 智谱 AI API Key(必需) +# 获取地址: https://open.bigmodel.cn/ +ZHIPUAI_API_KEY=4d568a4367f1442bbc226cc0daf84566.44SsKVWkVIM2Mkeg + +# vLLM 本地模型 Token(可选,仅使用本地模型时需要) +VLLM_LOCAL_KEY=token-abc123 diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..5e84622 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,61 @@ +name: 构建并部署 AI Agent 服务 + +on: + push: + branches: [ main, master ] + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 设置 Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: 缓存 pip 依赖 + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirement.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: 安装 Python 依赖 (仅用于验证) + run: pip install -r requirement.txt + + - name: 准备环境变量文件 + run: | + cat > .env << EOF + ZHIPUAI_API_KEY=${{ secrets.ZHIPUAI_API_KEY }} + VLLM_LOCAL_KEY=${{ secrets.VLLM_LOCAL_KEY }} + EOF + + - name: 重新构建并启动前后端服务(不影响数据库) + run: docker compose up -d --build backend frontend + + - name: 健康检查 + run: | + echo "等待后端服务启动..." + sleep 15 + for i in {1..10}; do + if curl -f http://localhost:8001/ > /dev/null 2>&1; then + echo "✅ 后端服务正常" + exit 0 + fi + echo "等待中... ($i/10)" + sleep 5 + done + echo "❌ 后端服务未就绪,查看日志:" + docker compose logs backend --tail 50 + exit 1 + + - name: 清理无用的 Docker 资源 + run: docker system prune -f + + - name: 显示运行状态 + run: docker compose ps diff --git a/.gitignore b/.gitignore index 36b13f1..50ef1b7 100644 --- a/.gitignore +++ b/.gitignore @@ -137,6 +137,19 @@ ENV/ env.bak/ venv.bak/ +# IDE 配置 +.vscode/ +.idea/ + +# 日志和数据 +logs/ +*.log +data/*.db +data/*.sqlite + +# Docker 卷数据 +pg_data/ + # Spyder project settings .spyderproject .spyproject diff --git a/QUICKSTART.md b/QUICKSTART.md index 09d2717..ce29c76 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -1,38 +1,65 @@ -# 快速开始指南 - 多模型切换功能 +# 快速开始指南 -## 🚀 5分钟快速启动 +详细的启动和部署指南,面向开发者和运维人员。 -### 步骤 1: 启动必要的容器 +--- + +## 🚀 快速启动(3 步) + +### 方式一:Docker Compose(推荐 ⭐) + +适合生产环境,一键启动所有服务。 + +#### 1. 配置环境变量 ```bash -# 使用提供的启动脚本(推荐) -./start.sh +cat > .env << EOF +ZHIPUAI_API_KEY=your_zhipuai_api_key_here +VLLM_LOCAL_KEY=token-abc123 +EOF +``` -# 或者手动启动容器 -# 1. 启动 vLLM (如果需要本地模型) -docker run -d --rm \ - --group-add=video \ - --cap-add=SYS_PTRACE \ - --security-opt seccomp=unconfined \ - --device=/dev/kfd \ - --device=/dev/dri \ - -v /home/huang/Study/AIModel/gemma-4-E2B-it:/models/gemma-4-E2B-it \ - -e VLLM_ROCM_USE_AITER=0 \ - -e HF_TOKEN="$HF_TOKEN" \ - -p 8000:8000 \ - --ipc=host \ - --entrypoint vllm \ - my-vllm-gemma4:working \ - serve /models/gemma-4-E2B-it \ - --served-model-name gemma-4-E2B-it \ - --dtype auto \ - --api-key token-abc123 \ - --trust-remote-code \ - --port 8000 \ - --gpu-memory-utilization 0.85 \ - --max-model-len 8192 +#### 2. 启动服务 -# 2. 启动 PostgreSQL +```bash +docker compose up -d --build +``` + +#### 3. 访问应用 + +- **前端**: http://localhost:8501 +- **后端 API**: http://localhost:8001 + +#### 常用命令 + +```bash +# 查看服务状态 +docker compose ps + +# 查看日志 +docker compose logs -f + +# 重启特定服务 +docker compose restart backend + +# 停止所有服务 +docker compose down +``` + +--- + +### 方式二:本地开发模式 + +适合开发和调试。 + +#### 前置要求 + +- Python 3.10+ +- Docker(用于 PostgreSQL) + +#### 1. 启动 PostgreSQL + +```bash docker run -d \ --name postgres-langgraph \ -e POSTGRES_PASSWORD=mysecretpassword \ @@ -42,109 +69,126 @@ docker run -d \ postgres:16 ``` -### 步骤 2: 配置环境变量 +#### 2. 安装依赖 + +```bash +pip install -r requirement.txt +``` + +#### 3. 配置环境变量 编辑 `.env` 文件: ```env -ZHIPUAI_API_KEY=your_actual_zhipuai_api_key -VLLM_LOCAL_KEY=token-abc123 +ZHIPUAI_API_KEY=your_api_key_here ``` -### 步骤 3: 启动服务 +#### 4. 启动服务 +**终端 1 - 后端:** ```bash -# 方式1: 使用启动脚本(推荐) -./start.sh - -# 方式2: 手动启动 -# 终端1: 启动后端 python backend.py +``` -# 终端2: 启动前端 +**终端 2 - 前端:** +```bash streamlit run frontend.py ``` -### 步骤 4: 访问应用 - -浏览器打开: `http://localhost:8501` +浏览器自动打开 http://localhost:8501 --- -## 🎯 使用多模型切换功能 +## 🐳 Docker 部署详解 -### 在前端切换模型 +### 文件说明 -1. **打开侧边栏**:点击左上角的菜单图标 -2. **选择模型**:在"选择大模型"下拉框中选择: - - 智谱 GLM-4.7-Flash(在线) - - 本地 vLLM(Gemma-4) -3. **开始对话**:输入您的问题,系统会使用选定的模型处理 +| 文件 | 用途 | +|------|------| +| `docker-compose.yml` | 服务编排配置 | +| `Dockerfile.backend` | 后端镜像构建 | +| `Dockerfile.frontend` | 前端镜像构建 | +| `.gitea/workflows/deploy.yml` | CI/CD 自动化部署 | -### 特性说明 +### docker-compose.yml 结构 -✅ **实时切换**:可以在对话过程中随时切换模型 -✅ **记忆共享**:同一会话 ID 下,不同模型共享对话历史 -✅ **自动降级**:如果选择的模型不可用,自动切换到可用模型 -✅ **状态显示**:每条回复下方会显示实际使用的模型 - ---- - -## 🧪 测试功能 - -### 运行自动化测试 - -```bash -# 确保后端正在运行 -python test_multi_model.py +```yaml +services: + postgres: # PostgreSQL 数据库 + backend: # FastAPI 后端服务 + frontend: # Streamlit 前端界面 ``` -测试内容包括: -- 各模型的可用性测试 -- 跨模型会话记忆测试 -- API 响应格式验证 +**特性:** +- ✅ PostgreSQL 健康检查,确保数据库就绪后才启动后端 +- ✅ 数据持久化到 Docker volume +- ✅ 自动重启策略(`restart: unless-stopped`) +- ✅ 内部网络隔离,外部无法直接访问数据库 -### 手动测试 +### 只更新特定服务 -1. **测试智谱模型**: - - 选择"智谱 GLM-4.7-Flash" - - 询问:"你好,请介绍一下自己" - - 观察回复速度和内容质量 +```bash +# 只重新构建后端(不影响数据库) +docker compose up -d --build backend -2. **测试本地模型**: - - 选择"本地 vLLM(Gemma-4)" - - 询问相同问题 - - 对比两个模型的回复差异 +# 只重新启动前端 +docker compose up -d frontend +``` -3. **测试记忆功能**: - - 第一轮(智谱模型):"我叫小明,记住我的名字" - - 第二轮(本地模型):"我叫什么名字?" - - 验证是否能正确回忆 +### 数据持久化 + +PostgreSQL 数据存储在命名 volume `pg_data` 中: + +```bash +# 查看 volume +docker volume ls | grep pg_data + +# 备份数据 +docker run --rm -v pg_data:/data -v $(pwd):/backup alpine tar czf /backup/pg_backup.tar.gz /data + +# 恢复数据 +docker run --rm -v pg_data:/data -v $(pwd):/backup alpine tar xzf /backup/pg_backup.tar.gz -C / +``` --- -## 🔧 常见问题 +## 🔧 开发指南 -### Q1: 某个模型初始化失败怎么办? +### 添加新工具 -**A:** 系统会自动跳过失败的模型,使用其他可用模型。检查日志了解具体原因: -- 智谱模型:确认 `ZHIPUAI_API_KEY` 是否正确 -- 本地模型:确认 vLLM 容器是否运行 +在 `tools.py` 中添加: -### Q2: 如何添加新模型? +```python +@tool +def my_new_tool(param: str) -> str: + """ + 工具描述(会显示给 LLM) + + Args: + param: 参数说明 + + Returns: + 返回值说明 + """ + # 实现逻辑 + return result +``` -**A:** 在 `agent.py` 中添加: +工具会自动注册,无需修改其他文件。 + +### 添加新模型 + +在 `agent.py` 中: ```python def _create_new_model_llm(self): """创建新模型的 LLM""" return YourChatModel( model="model-name", - api_key="your-key", - # ... 其他参数 + api_key=os.getenv("YOUR_API_KEY"), ) -# 在 initialize() 方法的 model_configs 中添加 +# 在 initialize() 方法中注册 model_configs = { "zhipu": self._create_zhipu_llm, "local": self._create_local_llm, @@ -152,94 +196,190 @@ model_configs = { } ``` -然后在前端 `frontend.py` 的 `MODEL_OPTIONS` 中添加对应选项。 +在前端 `frontend.py` 中添加选项: -### Q3: 会话记忆是如何工作的? +```python +MODEL_OPTIONS = { + "智谱 GLM-4": "zhipu", + "本地 Gemma-4": "local", + "新模型": "new_model", # 新增 +} +``` -**A:** -- 使用 PostgreSQL 存储对话历史 -- 通过 `thread_id` 关联同一会话的消息 -- 不同模型共享同一个 checkpointer,因此可以跨模型保持上下文 -- 点击"新会话"按钮会生成新的 `thread_id` +### 调试技巧 -### Q4: 性能优化建议 +```bash +# 进入后端容器 +docker compose exec backend bash -**A:** -- 智谱模型:适合快速响应场景,无需本地 GPU -- 本地模型:适合数据隐私要求高的场景,需要 GPU 支持 -- 长时间对话建议定期开启新会话,避免上下文过长 +# 查看实时日志 +docker compose logs -f backend + +# 检查数据库连接 +docker compose exec postgres psql -U postgres -d langgraph_db -c "\dt" + +# 测试后端 API +curl http://localhost:8001/ +``` --- -## 📊 架构优势 +## 🔄 CI/CD 自动化部署 -### 预编译 Graph +### Gitea Workflows -每个模型在启动时都会预编译独立的 LangGraph: -- ✅ 避免每次请求都重新编译,提升性能 -- ✅ 各模型独立,互不影响 -- ✅ 支持热插拔,可动态添加/移除模型 +项目包含自动化部署配置 `.gitea/workflows/deploy.yml`。 -### 智能降级 +**触发条件:** +- 推送到 `main` 或 `master` 分支 +- 手动触发(workflow_dispatch) -如果选择的模型不可用: -1. 后端自动切换到第一个可用模型 -2. 返回响应中包含 `model_used` 字段 -3. 前端显示实际使用的模型 -4. 用户无感知,体验流畅 +**部署流程:** +1. 检出代码 +2. 安装 Python 依赖(验证用) +3. 准备环境变量 +4. 重新构建并启动前后端(不影响数据库) +5. 健康检查(等待后端就绪) +6. 清理无用 Docker 资源 -### 统一接口 +**配置 Secrets:** -无论使用哪个模型: -- API 接口保持一致 -- 工具调用方式相同 -- 会话记忆机制统一 -- 前端操作体验一致 +在 Gitea 仓库设置中添加: +- `ZHIPUAI_API_KEY` +- `VLLM_LOCAL_KEY` --- -## 🎓 进阶使用 +## 🐛 故障排查 -### 固定会话 ID +### 常见问题 -如需在不同浏览器或设备间继续同一会话: +#### 1. PostgreSQL 连接失败 -```python -# 在 frontend.py 中修改 -st.session_state.thread_id = "my_fixed_session_id" +```bash +# 检查容器状态 +docker compose ps postgres + +# 查看日志 +docker compose logs postgres + +# 测试连接 +docker compose exec postgres pg_isready -U postgres ``` -### 自定义超时时间 +**解决方案:** +- 确认容器正在运行 +- 检查密码是否正确 +- 等待健康检查通过(约 10-30 秒) -```python -# 在 frontend.py 中修改 timeout 参数 -response = requests.post( - API_URL, - json={...}, - timeout=120 # 增加到 120 秒 -) +#### 2. 后端启动失败 + +```bash +# 查看详细日志 +docker compose logs backend + +# 检查端口占用 +lsof -i :8001 ``` -### 批量测试 +**常见原因:** +- API Key 未配置或错误 +- 端口 8001 被占用 +- 依赖包缺失 -```python -# 创建测试脚本 -import requests +#### 3. 前端无法连接后端 -messages = ["问题1", "问题2", "问题3"] -for msg in messages: - response = requests.post(API_URL, json={"message": msg, "model": "zhipu"}) - print(response.json()["reply"]) +```bash +# 检查后端是否正常运行 +curl http://localhost:8001/ + +# 检查网络连接 +docker compose exec frontend ping backend ``` +**解决方案:** +- 确认后端服务已启动 +- 检查防火墙设置 +- 重启前端服务 + +#### 4. 模型初始化失败 + +```bash +# 查看后端启动日志 +docker compose logs backend | grep -i "model\|error" +``` + +**可能原因:** +- 智谱 API Key 无效 +- vLLM 容器未启动(如使用本地模型) +- 网络连接问题 + +--- + +## 📊 监控和维护 + +### 查看资源使用 + +```bash +# Docker 容器资源使用 +docker stats + +# 磁盘空间 +docker system df + +# 清理无用资源 +docker system prune -f +``` + +### 日志管理 + +```bash +# 查看所有服务日志 +docker compose logs + +# 查看特定服务最近 100 行日志 +docker compose logs --tail=100 backend + +# 实时跟踪日志 +docker compose logs -f backend frontend +``` + +### 备份和恢复 + +```bash +# 备份数据库 +docker compose exec postgres pg_dump -U postgres langgraph_db > backup.sql + +# 恢复数据库 +cat backup.sql | docker compose exec -T postgres psql -U postgres langgraph_db +``` + +--- + +## 🎯 性能优化建议 + +### 开发环境 + +- 使用 `docker compose watch` 实现热重载(需配置) +- 挂载代码目录而非复制到镜像 +- 启用 Python 调试模式 + +### 生产环境 + +- 使用反向代理(Nginx) +- 启用 HTTPS +- 配置日志轮转 +- 设置资源限制(CPU、内存) +- 定期备份数据库 + --- ## 📞 获取帮助 -- 查看完整文档:[README.md](README.md) -- 查看项目结构:参考 [README.md](README.md) 中的项目结构部分 -- 报告问题:提交 Issue 并附上日志信息 +- **完整文档**: [README.md](README.md) +- **RAG 示例**: `rag_example.py` +- **报告问题**: 提交 Issue 并附上日志 --- -**祝您使用愉快!** 🎉 +**祝您部署顺利!** 🎉 diff --git a/README.md b/README.md index 2ed9a01..ca0dce4 100644 --- a/README.md +++ b/README.md @@ -1,192 +1,205 @@ -# AI Agent - 个人生活助手和数据分析助手 +# AI Agent - 智能助手系统 -## 项目概述 +一个基于 LangGraph + FastAPI 的智能对话助手,支持多模型切换、RAG 知识库检索、文件处理和网页抓取等功能。 -这是一个基于 LangGraph、LangChain 和 FastAPI 构建的 AI 助手系统,能够处理天气查询、文件读取、网页抓取等任务。采用前后端分离架构,支持 PostgreSQL 持久化对话记忆。 +--- -## 项目结构 +## 🎯 核心功能 + +### 面向用户的功能 + +- 💬 **智能对话**:支持多轮对话,自动记忆上下文 +- 🌤️ **天气查询**:实时获取各地天气信息 +- 📄 **文档处理**:读取 TXT、PDF、Excel 等格式文件 +- 🌐 **网页抓取**:提取网页正文内容 +- 🔍 **知识库检索(RAG)**:基于向量数据库的智能问答 +- 🔄 **多模型切换**:前端可选择不同大语言模型 + +### 技术特性 + +- ✅ **持久化记忆**:PostgreSQL 存储对话历史,重启不丢失 +- ✅ **高可用架构**:模型自动降级,确保服务稳定 +- ✅ **前后端分离**:FastAPI 后端 + Streamlit 前端 +- ✅ **Docker 部署**:一键启动所有服务 + +--- + +## 🏗️ 技术架构 + +### 技术栈 + +| 层级 | 技术选型 | 说明 | +|------|---------|------| +| **LLM 服务** | 智谱 AI API / vLLM (Gemma-4) | 云端 API 或本地推理 | +| **Embedding** | 智谱 Embedding API | 向量嵌入(无需 PyTorch) | +| **Agent 框架** | LangGraph + LangChain | 工作流编排 | +| **向量数据库** | ChromaDB / pgvector | RAG 知识检索 | +| **后端框架** | FastAPI + Uvicorn | RESTful API + WebSocket | +| **前端框架** | Streamlit | 交互式 Web 界面 | +| **数据库** | PostgreSQL 16 | 对话记忆持久化 | +| **容器化** | Docker + Docker Compose | 服务编排 | + +### 架构图 + +``` +┌──────────────┐ +│ 用户浏览器 │ Streamlit 前端 (8501) +└──────┬───────┘ + │ HTTP/WebSocket + ↓ +┌──────────────────┐ +│ FastAPI 后端 │ 端口 8001 +│ ┌────────────┐ │ +│ │ AIAgent │ │ 多模型管理 +│ └─────┬──────┘ │ +│ │ │ +│ ┌─────▼──────┐ │ +│ │LangGraph │ │ 工作流引擎 +│ │ StateGraph │ │ +│ └─────┬──────┘ │ +│ │ │ +│ ┌─────▼──────┐ │ +│ │ Tools │ │ 工具集合 +│ │ - Weather │ │ +│ │ - File IO │ │ +│ │ - Web Scrap│ │ +│ │ - RAG │ │ +│ └────────────┘ │ +└────────┬─────────┘ + │ + ┌────┴────┐ + ↓ ↓ +┌────────┐ ┌──────────┐ +│PostgreSQL│ │ChromaDB │ +│(记忆存储)│ │(向量检索)│ +└────────┘ └──────────┘ +``` + +### 项目结构 ``` Agent1/ -├── tools.py # 工具定义(纯函数、@tool) -├── graph_builder.py # LangGraph 状态图构建(状态定义、节点、边) -├── agent.py # AIAgentService 类(模型初始化、graph 管理、消息处理) -├── backend.py # FastAPI 应用(路由、WebSocket、lifespan) -├── frontend.py # Streamlit 前端(通过 HTTP 调用后端) -├── .env # 环境变量(ZHIPUAI_API_KEY 等) -├── requirement.txt # Python 依赖包列表 -└── user_docs/ # 允许读取的文档目录 +├── agent.py # Agent 服务核心(多模型管理) +├── graph_builder.py # LangGraph 状态图构建器 +├── tools.py # 工具函数定义(@tool 装饰器) +├── backend.py # FastAPI 后端应用 +├── frontend.py # Streamlit 前端界面 +├── rag_example.py # RAG 实现示例(无 PyTorch) +├── docker-compose.yml # Docker 服务编排 +├── Dockerfile.backend # 后端镜像构建 +├── Dockerfile.frontend # 前端镜像构建 +├── requirement.txt # Python 依赖 +├── .env # 环境变量配置 +└── user_docs/ # 用户文档目录 ├── a.txt ├── b.pdf └── c.xlsx ``` -## 核心功能 +--- -- 🌤️ **天气查询**:获取指定地点的当前温度 -- 📄 **文本文件读取**:读取 `.txt`、`.md` 等文本文件 -- 📑 **PDF 文件读取**:解析 PDF 文件并提取文本内容 -- 📊 **Excel 数据处理**:读取 Excel 文件并转换为 Markdown 表格 -- 🌐 **网页抓取**:抓取网页正文内容 -- 💾 **持久化记忆**:使用 PostgreSQL 保存对话历史,支持多轮对话上下文 -- 🔄 **多模型动态切换**:前端可选择不同的大语言模型,后端自动切换处理 +## 🚀 快速开始 -## 技术栈 +详细启动指南请查看 [QUICKSTART.md](QUICKSTART.md) -- **后端框架**:FastAPI + Uvicorn -- **前端框架**:Streamlit -- **AI 框架**:LangGraph + LangChain -- **数据库**:PostgreSQL(用于持久化对话记忆) -- **LLM 支持**: - - 智谱 AI(glm-4.7-flash):在线服务,响应速度快 - - 本地 vLLM(gemma-4-E2B-it):本地部署,数据隐私性好 - -系统支持多种大语言模型,可在前端动态切换。每个模型在启动时都会预编译独立的 LangGraph,确保最佳性能。如果某个模型初始化失败(如 API Key 未配置),系统会自动降级到可用模型。 - -## 环境要求 - -- Python 3.10+ -- PostgreSQL 16+ -- Docker(可选,用于运行 PostgreSQL) - -## 安装步骤 - -### 1. 启动 PostgreSQL 容器 +### 方式一:Docker Compose(推荐) ```bash -docker run -d \ - --name postgres-langgraph \ +# 1. 配置 .env 文件 +echo "ZHIPUAI_API_KEY=your_key_here" > .env + +# 2. 启动所有服务 +docker compose up -d --build + +# 3. 访问应用 +# 前端: http://localhost:8501 +# 后端: http://localhost:8001 +``` + +### 方式二:本地开发模式 + +```bash +# 1. 启动 PostgreSQL +docker run -d --name postgres-langgraph \ -e POSTGRES_PASSWORD=mysecretpassword \ -e POSTGRES_DB=langgraph_db \ - -p 5432:5432 \ - -v ~/docker_volumes/postgres_data:/var/lib/postgresql/data \ - postgres:16 -``` + -p 5432:5432 postgres:16 -### 2. 安装 Python 依赖 - -```bash -pip install fastapi uvicorn streamlit requests psycopg[binary,pool] \ - langgraph langgraph-checkpoint-postgres langchain langchain-community \ - langchain-openai python-dotenv pypdf pandas beautifulsoup4 -``` - -或者使用 requirements.txt: - -```bash +# 2. 安装依赖 pip install -r requirement.txt -``` -### 3. 配置环境变量 - -编辑 `.env` 文件,设置您的 API 密钥: - -```env -ZHIPUAI_API_KEY=your_zhipuai_api_key_here -VLLM_LOCAL_KEY=token-abc123 # 如果使用本地模型 -``` - -## 运行步骤 - -### 1. 启动后端服务 - -```bash +# 3. 启动后端 python backend.py -``` -看到 `Uvicorn running on http://0.0.0.0:8001` 即表示启动成功。 - -### 2. 启动前端界面(新终端) - -```bash +# 4. 启动前端(新终端) streamlit run frontend.py ``` -浏览器会自动打开 `http://localhost:8501`,即可开始使用。 +--- -## API 接口 +## 📖 使用指南 -### POST /chat +### 基础对话 -同步对话接口,支持模型选择 +直接在聊天框输入问题即可: -**请求体:** -```json -{ - "message": "今天北京天气怎么样?", - "thread_id": "optional-thread-id", - "model": "zhipu" // 可选: "zhipu" 或 "local" -} ``` - -**响应:** -```json -{ - "reply": "当前北京的温度为25℃", - "thread_id": "generated-or-provided-thread-id", - "model_used": "zhipu" // 实际使用的模型 -} +你好,请介绍一下自己 +今天北京天气怎么样? +帮我总结一下 a.txt 的内容 ``` -**模型选项:** -- `zhipu`:智谱 GLM-4.7-Flash(在线) -- `local`:本地 vLLM Gemma-4(需要启动 vLLM 容器) - -### WebSocket /ws - -流式对话接口(可选扩展) - -## 使用说明 - ### 工具调用示例 -1. **查询天气**: - ``` - 用户:今天上海天气怎么样? - ``` - -2. **读取文本文件**: - ``` - 用户:请读取 a.txt 文件的内容 - ``` - -3. **读取 PDF 文件**: - ``` - 用户:帮我总结一下 b.pdf 的内容 - ``` - -4. **读取 Excel 文件**: - ``` - 用户:显示 c.xlsx 的数据 - ``` - -5. **抓取网页**: - ``` - 用户:请抓取 https://example.com 的内容 - ``` - -### 会话记忆 - -- 系统会自动为每个会话生成唯一的 `thread_id` -- 相同 `thread_id` 的对话会共享历史记录 -- 即使重启后端服务,对话历史依然保存在 PostgreSQL 中 -- 如需固定会话 ID,可在前端代码中修改 `st.session_state.thread_id` 为固定字符串 +| 功能 | 示例提问 | +|------|---------| +| 🌤️ 天气查询 | "上海今天天气如何?" | +| 📄 读取文本 | "读取 a.txt 的内容" | +| 📑 解析 PDF | "总结 b.pdf 的主要内容" | +| 📊 Excel 数据 | "显示 c.xlsx 的数据" | +| 🌐 网页抓取 | "抓取 https://example.com 的内容" | +| 🔍 知识库检索 | "根据知识库回答:XXX" | ### 多模型切换 -**前端操作:** -1. 在左侧边栏的"选择大模型"下拉框中选择模型 -2. 可随时切换模型,甚至在同一会话中 -3. 点击"🔄 新会话"按钮可清空当前对话并开始新的会话 +1. 在左侧边栏选择模型: + - **智谱 GLM-4**:在线服务,速度快 + - **本地 Gemma-4**:本地部署,隐私性好 -**后端行为:** -- 启动时会预编译所有可用模型的 LangGraph -- 如果某个模型初始化失败(如 API Key 未配置),会自动跳过 -- 请求时如果选择的模型不可用,会自动降级到第一个可用模型 -- 响应中会返回 `model_used` 字段,显示实际使用的模型 +2. 可随时切换,甚至在同一会话中 + +3. 点击 "🔄 新会话" 清空当前对话 + +--- + +## 🔧 开发指南 + +### 添加新工具 + +在 `tools.py` 中添加新的 `@tool` 装饰函数: + +```python +@tool +def my_new_tool(param: str) -> str: + """ + 工具描述(会显示给 LLM) + + Args: + param: 参数说明 + + Returns: + 返回值说明 + """ + # 实现逻辑 + return result +``` + +工具会自动注册到 `AVAILABLE_TOOLS` 列表中。 + +### 添加新模型 + +在 `agent.py` 的 `initialize()` 方法中添加模型配置: -**添加新模型:** -在 `agent.py` 的 `initialize()` 方法中的 `model_configs` 字典添加新模型即可: ```python model_configs = { "zhipu": self._create_zhipu_llm, @@ -195,74 +208,75 @@ model_configs = { } ``` -## 架构说明 +### Docker 部署 -### 模块职责 +项目包含完整的 Docker 配置: -- **tools.py**:独立工具模块,包含所有 `@tool` 装饰的纯函数,无外部依赖,可单独测试 -- **graph_builder.py**:LangGraph 状态图构建器,定义状态、节点函数和条件边 -- **agent.py**:AIAgentService 服务类,负责模型初始化和 graph 编译,使用 `AsyncPostgresSaver` -- **backend.py**:FastAPI 应用,提供 REST API 和 WebSocket 接口,端口 8001 -- **frontend.py**:Streamlit 前端,通过 HTTP 调用后端 API,实现友好的用户界面 +- **docker-compose.yml**:服务编排(PostgreSQL + Backend + Frontend) +- **Dockerfile.backend**:后端镜像构建 +- **Dockerfile.frontend**:前端镜像构建 +- **.gitea/workflows/deploy.yml**:CI/CD 自动化部署 -### 数据流 +详见 [QUICKSTART.md](QUICKSTART.md) 的 Docker 部署章节。 -``` -用户输入 → Streamlit 前端 → FastAPI 后端 → AIAgentService -→ LangGraph StateGraph → LLM + Tools → PostgreSQL (记忆) -→ 返回响应 → 前端展示 +--- + +## ⚙️ 环境配置 + +### 必需的环境变量 + +在 `.env` 文件中配置: + +```env +# 智谱 AI API Key(必需) +ZHIPUAI_API_KEY=your_api_key_here + +# vLLM 本地模型 Token(可选) +VLLM_LOCAL_KEY=token-abc123 ``` -## 注意事项 +### 数据库配置 -1. **文件安全**:所有文件读取操作仅限于 `./user_docs` 目录,防止路径遍历攻击 -2. **端口冲突**:后端使用 8001 端口,避免与本地 vLLM 服务的 8000 端口冲突 -3. **API 密钥**:请妥善保管 `.env` 文件中的 API 密钥,不要提交到版本控制系统 -4. **数据库持久化**:PostgreSQL 数据卷挂载到 `~/docker_volumes/postgres_data`,确保数据安全 +默认使用 PostgreSQL,连接字符串: +``` +postgresql://postgres:mysecretpassword@localhost:5432/langgraph_db +``` -## 故障排除 +如使用 Docker Compose,数据库会在内部网络中自动配置。 -### 问题:无法连接 PostgreSQL +--- -**解决方案:** +## 🐛 故障排查 + +### 常见问题 + +**Q: 无法连接 PostgreSQL?** ```bash -# 检查容器是否运行 -docker ps | grep postgres-langgraph +# 检查容器状态 +docker ps | grep postgres -# 查看容器日志 +# 查看日志 docker logs postgres-langgraph - -# 重新启动容器 -docker restart postgres-langgraph ``` -### 问题:后端启动失败 - -**解决方案:** +**Q: 后端启动失败?** - 确认端口 8001 未被占用 -- 检查 `.env` 文件中的 API 密钥是否正确配置 -- 确认所有依赖包已正确安装 -- 查看启动日志,确认至少有一个模型初始化成功 +- 检查 `.env` 中的 API Key 是否正确 +- 查看启动日志确认模型初始化成功 -### 问题:模型切换后无响应 - -**解决方案:** -- 检查所选模型的配置是否正确(如智谱 API Key) -- 确认 vLLM 容器是否正在运行(如果使用本地模型) -- 查看后端日志,确认模型是否初始化成功 +**Q: 模型切换后无响应?** +- 检查所选模型的配置是否正确 +- 确认 vLLM 容器是否运行(如使用本地模型) - 尝试切换到另一个模型 -### 问题:工具调用失败 +更多问题排查请查看 [QUICKSTART.md](QUICKSTART.md) -**解决方案:** -- 确认文件位于 `./user_docs` 目录下 -- 检查文件格式是否正确 -- 查看后端日志获取详细错误信息 +--- -## 许可证 +## 📝 许可证 本项目采用 MIT 许可证。详见 [LICENSE](LICENSE) 文件。 -## 贡献 +## 🤝 贡献 欢迎提交 Issue 和 Pull Request! diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..ca80779 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,8 @@ +""" +AI Agent 应用模块 +""" + +from .agent import AIAgentService +from .tools import AVAILABLE_TOOLS, TOOLS_BY_NAME + +__all__ = ["AIAgentService", "AVAILABLE_TOOLS", "TOOLS_BY_NAME"] diff --git a/agent.py b/app/agent.py similarity index 91% rename from agent.py rename to app/agent.py index 34e79f0..a7a2727 100644 --- a/agent.py +++ b/app/agent.py @@ -11,8 +11,8 @@ from langchain_openai import ChatOpenAI from pydantic import SecretStr # 本地模块 -from graph_builder import GraphBuilder -from tools import AVAILABLE_TOOLS, TOOLS_BY_NAME +from app.graph_builder import GraphBuilder +from app.tools import AVAILABLE_TOOLS, TOOLS_BY_NAME load_dotenv() @@ -44,7 +44,9 @@ class AIAgentService: def _create_local_llm(self): """创建本地 vLLM 服务 LLM""" return ChatOpenAI( - base_url="http://localhost:8000/v1", + # 原来是 http://localhost:8000/v1 + # 改为 FRP 穿透后的公网地址 + base_url = "http://115.190.121.151:18000/v1", api_key=SecretStr(os.getenv("VLLM_LOCAL_KEY", "")), model="gemma-4-E2B-it", ) diff --git a/backend.py b/app/backend.py similarity index 99% rename from backend.py rename to app/backend.py index 6b4df52..69f312a 100644 --- a/backend.py +++ b/app/backend.py @@ -11,7 +11,7 @@ from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver -from agent import AIAgentService +from app.agent import AIAgentService # PostgreSQL 连接字符串 DB_URI = "postgresql://postgres:mysecretpassword@localhost:5432/langgraph_db?sslmode=disable" diff --git a/graph_builder.py b/app/graph_builder.py similarity index 100% rename from graph_builder.py rename to app/graph_builder.py diff --git a/tools.py b/app/tools.py similarity index 100% rename from tools.py rename to app/tools.py diff --git a/data/user_docs/README.md b/data/user_docs/README.md new file mode 100644 index 0000000..8be02eb --- /dev/null +++ b/data/user_docs/README.md @@ -0,0 +1,16 @@ +# 用户文档目录 + +将您的文档文件(.txt, .pdf, .xlsx 等)放在此目录中,AI Agent 可以读取和分析这些文件。 + +## 支持的文件格式 + +- 📄 **文本文件**: `.txt`, `.md` +- 📑 **PDF 文档**: `.pdf` +- 📊 **Excel 表格**: `.xlsx`, `.xls` + +## 注意事项 + +1. 所有文件必须是可读格式 +2. 文件大小建议不超过 50MB +3. 敏感信息请勿放入此目录 +4. 文件内容会被用于 AI 分析和检索 diff --git a/user_docs/a.txt b/data/user_docs/a.txt similarity index 100% rename from user_docs/a.txt rename to data/user_docs/a.txt diff --git a/user_docs/b.pdf b/data/user_docs/b.pdf similarity index 100% rename from user_docs/b.pdf rename to data/user_docs/b.pdf diff --git a/user_docs/c.xlsx b/data/user_docs/c.xlsx similarity index 100% rename from user_docs/c.xlsx rename to data/user_docs/c.xlsx diff --git a/docker/Dockerfile.backend b/docker/Dockerfile.backend new file mode 100644 index 0000000..5d2c187 --- /dev/null +++ b/docker/Dockerfile.backend @@ -0,0 +1,22 @@ +FROM python:3.11-slim + +WORKDIR /app + +# 复制依赖文件并安装(利用 Docker 层缓存) +COPY requirement.txt . +RUN pip install --no-cache-dir -r requirement.txt + +# 复制项目代码 +COPY app/ ./app/ +COPY frontend/ ./frontend/ +COPY data/ ./data/ +COPY scripts/ ./scripts/ + +# 设置 PYTHONPATH 确保模块能被找到 +ENV PYTHONPATH=/app + +# 暴露端口(文档用途) +EXPOSE 8001 + +# 启动命令 +CMD ["python", "app/backend.py"] diff --git a/docker/Dockerfile.frontend b/docker/Dockerfile.frontend new file mode 100644 index 0000000..4873d88 --- /dev/null +++ b/docker/Dockerfile.frontend @@ -0,0 +1,15 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirement.txt . +RUN pip install --no-cache-dir -r requirement.txt + +COPY frontend/ ./frontend/ +COPY app/ ./app/ + +ENV PYTHONPATH=/app + +EXPOSE 8501 + +CMD ["streamlit", "run", "frontend/frontend.py", "--server.port", "8501", "--server.address", "0.0.0.0"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..903920b --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,63 @@ +version: '3.8' + +services: + postgres: + image: postgres:16 + container_name: ai-postgres + environment: + POSTGRES_PASSWORD: mysecretpassword # 请替换为强密码 + POSTGRES_DB: langgraph_db + volumes: + - pg_data:/var/lib/postgresql/data + networks: + - ai-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + # 如需外部访问数据库,取消下面注释 + # ports: + # - "5432:5432" + + backend: + build: + context: .. # 构建上下文为项目根目录 + dockerfile: docker/Dockerfile.backend + container_name: ai-backend + environment: + - ZHIPUAI_API_KEY=${ZHIPUAI_API_KEY} + - VLLM_LOCAL_KEY=${VLLM_LOCAL_KEY} + - DB_URI=postgresql://postgres:mysecretpassword@postgres:5432/langgraph_db?sslmode=disable + volumes: + - ../data/user_docs:/app/data/user_docs # 挂载文档目录 + - ../logs:/app/logs + networks: + - ai-network + depends_on: + postgres: + condition: service_healthy + restart: unless-stopped + + frontend: + build: + context: .. + dockerfile: docker/Dockerfile.frontend + container_name: ai-frontend + environment: + - API_URL=http://backend:8001/chat + ports: + - "8501:8501" + networks: + - ai-network + depends_on: + - backend + restart: unless-stopped + +networks: + ai-network: + driver: bridge + +volumes: + pg_data: diff --git a/frontend.py b/frontend/frontend.py similarity index 100% rename from frontend.py rename to frontend/frontend.py diff --git a/start.sh b/scripts/start.sh old mode 100644 new mode 100755 similarity index 95% rename from start.sh rename to scripts/start.sh index f1e0574..a7e72a3 --- a/start.sh +++ b/scripts/start.sh @@ -84,7 +84,9 @@ check_postgres() { # 启动后端 start_backend() { echo -e "\n${BLUE}🚀 启动后端服务 (端口 8001)...${NC}" - python backend.py & + cd /home/huang/Study/AIProject/Agent1 + export PYTHONPATH=$(pwd) + python app/backend.py & BACKEND_PID=$! echo -e "${GREEN}✓ 后端服务已启动 (PID: $BACKEND_PID)${NC}" sleep 2 @@ -93,7 +95,9 @@ start_backend() { # 启动前端 start_frontend() { echo -e "\n${BLUE}🎨 启动前端界面...${NC}" - streamlit run frontend.py & + cd /home/huang/Study/AIProject/Agent1 + export PYTHONPATH=$(pwd) + streamlit run frontend/frontend.py & FRONTEND_PID=$! echo -e "${GREEN}✓ 前端服务已启动 (PID: $FRONTEND_PID)${NC}" echo -e "${GREEN}✓ 请在浏览器中打开: http://localhost:8501${NC}" diff --git a/test_multi_model.py b/test_multi_model.py deleted file mode 100644 index a64d5bd..0000000 --- a/test_multi_model.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -多模型切换功能测试脚本 -用于验证后端是否正确支持多模型动态切换 -""" - -import requests -import json - -API_URL = "http://localhost:8001/chat" - - -def test_model_switching(): - """测试模型切换功能""" - - print("=" * 60) - print("测试多模型切换功能") - print("=" * 60) - - # 测试消息 - test_message = "你好,请简单介绍一下自己" - - # 测试不同的模型 - models_to_test = ["zhipu", "local"] - - for model in models_to_test: - print(f"\n📤 测试模型: {model}") - print("-" * 60) - - try: - response = requests.post( - API_URL, - json={ - "message": test_message, - "model": model - }, - timeout=30 - ) - - if response.status_code == 200: - data = response.json() - print(f"✅ 成功!") - print(f" 使用的模型: {data['model_used']}") - print(f" 会话 ID: {data['thread_id'][:8]}...") - print(f" 回复预览: {data['reply'][:100]}...") - else: - print(f"❌ 失败! 状态码: {response.status_code}") - print(f" 错误信息: {response.text}") - - except requests.exceptions.Timeout: - print(f"⏰ 超时! 模型 '{model}' 响应时间过长") - except requests.exceptions.ConnectionError: - print(f"🔌 连接失败! 请确认后端服务正在运行 (python backend.py)") - except Exception as e: - print(f"💥 异常: {str(e)}") - - print("\n" + "=" * 60) - print("测试完成!") - print("=" * 60) - - -def test_conversation_memory(): - """测试跨模型的会话记忆""" - - print("\n" + "=" * 60) - print("测试跨模型会话记忆") - print("=" * 60) - - import uuid - thread_id = str(uuid.uuid4()) - - print(f"\n📝 使用固定会话 ID: {thread_id[:8]}...") - - # 第一轮对话 - 使用 zhipu 模型 - print("\n📤 第1轮 - 使用 zhipu 模型") - try: - response1 = requests.post( - API_URL, - json={ - "message": "我叫小明,记住我的名字", - "thread_id": thread_id, - "model": "zhipu" - }, - timeout=30 - ) - if response1.status_code == 200: - data1 = response1.json() - print(f" ✅ 回复: {data1['reply'][:100]}...") - print(f" 🤖 使用模型: {data1['model_used']}") - except Exception as e: - print(f" ❌ 失败: {e}") - return - - # 第二轮对话 - 切换到 local 模型,测试是否记得名字 - print("\n📤 第2轮 - 切换到 local 模型") - try: - response2 = requests.post( - API_URL, - json={ - "message": "我叫什么名字?", - "thread_id": thread_id, - "model": "local" - }, - timeout=30 - ) - if response2.status_code == 200: - data2 = response2.json() - print(f" ✅ 回复: {data2['reply'][:100]}...") - print(f" 🤖 使用模型: {data2['model_used']}") - - # 检查是否记得名字 - if "小明" in data2['reply']: - print(" 🎉 成功!跨模型记忆功能正常") - else: - print(" ⚠️ 注意:模型可能没有正确回忆上下文") - except Exception as e: - print(f" ❌ 失败: {e}") - - print("\n" + "=" * 60) - print("会话记忆测试完成!") - print("=" * 60) - - -if __name__ == "__main__": - print("\n⚠️ 请确保后端服务正在运行 (python backend.py)\n") - - # 运行基本测试 - test_model_switching() - - # 询问是否运行记忆测试 - choice = input("\n是否运行会话记忆测试?(y/n): ").strip().lower() - if choice == 'y': - test_conversation_memory() - - print("\n✨ 所有测试完成!")