一个基于 LongCat API 开放平台的聊天机器人应用,采用前后端分离架构,支持 OpenAI 和 Anthropic API 格式。



功能特性
- 🤖 支持多种 AI 模型:
- LongCat-Flash-Chat: 高性能通用对话模型
- LongCat-Flash-Thinking: 深度思考模型
- 🔌 双 API 格式支持:OpenAI 和 Anthropic
- 🖥️ 现代化前端界面,实时聊天体验
- 🐳 容器化部署,一键启动
- 🔐 API Key 认证支持(可选)
项目结构
1 2 3 4 5 6 7 8 9 10 11 12
| . ├── ai-chat-backend/ # 后端服务 │ ├── Dockerfile # 后端 Docker 配置 │ ├── api.py # Flask 后端应用 │ └── requirements.txt # Python 依赖 ├── ai-chat-frontend/ # 前端应用 │ ├── dist/ # 前端静态资源 │ │ └── index.html # 主页面 │ ├── Dockerfile # 前端 Docker 配置 │ └── nginx.conf # Nginx 配置 ├── docker-compose.yml # Docker Compose 编排文件 └── README.md # 项目说明文档
|
快速开始
使用 Docker Compose(推荐)
1 2 3 4 5 6 7 8 9 10
| git clone <repository-url> cd AIChatBot
docker-compose up -d
|
手动部署
后端服务
- 进入后端目录:
- 安装依赖:
1
| pip install -r requirements.txt
|
- 运行后端服务:
默认监听端口为 5000。
前端界面
- 进入前端目录:
- 使用 nginx 启动前端服务(确保已安装 nginx):
1 2 3 4 5
| cp -r dist/* /usr/share/nginx/html/
nginx
|
或者直接在浏览器中打开 ai-chat-frontend/dist/index.html 文件。
API 接入信息
- OpenAI 格式端点:
https://api.longcat.chat/openai
- Anthropic 格式端点:
https://api.longcat.chat/anthropic
支持的模型
| 模型标识符 |
显示名称 |
描述 |
| longcat-flash-chat |
LongCat-Flash-Chat |
高性能通用对话模型 |
| longcat-flash-thinking |
LongCat-Flash-Thinking |
深度思考模型 |
使用说明
- 启动服务后,访问前端界面
http://localhost:3000
- 在配置面板中:
- 输入您的 API Key(可选)
- 选择要使用的模型
- 选择 API 格式(OpenAI 或 Anthropic)
- 在底部输入框中输入消息,点击发送或按回车键
- 等待机器人回复
API 接口
/api/chat (POST)
处理聊天请求并转发到 LongCat API。
请求参数:
1 2 3 4 5 6
| { "messages": [], "model": "longcat-flash-chat", "api_format": "openai", "api_key": "" }
|
/api/models (GET)
列出所有支持的模型。
/health (GET)
健康检查接口。
注意事项
- 默认情况下不需要 API Key 即可使用
- 如需在生产环境中部署,请适当调整安全配置
- 前端通过 Nginx 配置将
/api 请求代理到后端服务
- Docker 部署时,前端和后端通过自定义网络进行通信
许可证
[待补充]
源码
ai-chat-backend
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| from flask import Flask, request, jsonify import requests import os
app = Flask(__name__)
LONGCAT_OPENAI_API_BASE = "https://api.longcat.chat/openai" LONGCAT_ANTHROPIC_API_BASE = "https://api.longcat.chat/anthropic"
MODELS = { "longcat-flash-chat": { "name": "LongCat-Flash-Chat", "description": "高性能通用对话模型" }, "longcat-flash-thinking": { "name": "LongCat-Flash-Thinking", "description": "深度思考模型" } }
@app.route('/api/chat', methods=['POST']) def chat(): """ 处理聊天请求并转发到LongCat API """ data = request.json messages = data.get('messages', []) model = data.get('model', 'longcat-flash-chat') api_format = data.get('api_format', 'openai') api_key = data.get('api_key', '') if model not in MODELS: return jsonify({"error": f"不支持的模型: {model}"}), 400 headers = { 'Content-Type': 'application/json' } if api_key: headers['Authorization'] = f'Bearer {api_key}' if api_format == 'openai': api_url = f"{LONGCAT_OPENAI_API_BASE}/v1/chat/completions" payload = { "model": MODELS[model]['name'], "messages": messages } elif api_format == 'anthropic': api_url = f"{LONGCAT_ANTHROPIC_API_BASE}/v1/messages" payload = { "model": MODELS[model]['name'], "messages": messages } else: return jsonify({"error": "不支持的API格式"}), 400 try: response = requests.post(api_url, json=payload, headers=headers) response.raise_for_status() return jsonify(response.json()) except requests.exceptions.RequestException as e: return jsonify({"error": f"调用API时出错: {str(e)}"}), 500
@app.route('/api/models', methods=['GET']) def list_models(): """ 列出所有支持的模型 """ return jsonify({ "models": MODELS })
@app.route('/health', methods=['GET']) def health_check(): return jsonify({'status': 'healthy', 'service': 'ai-chat-backend'})
if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)
|
Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY api.py .
EXPOSE 5000
CMD ["python", "api.py"]
|
requirements.txt
1 2
| Flask==2.3.2 requests==2.31.0
|
ai-chat-frontend
Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
|
nginx.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } location /api { proxy_pass http://ai-chat-backend:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
|
dist/index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>LongCat AI ChatBot</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; } .chat-container { background-color: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); padding: 20px; margin-bottom: 20px; height: 500px; overflow-y: auto; } .message { margin-bottom: 15px; padding: 10px; border-radius: 5px; } .user-message { background-color: #dcf8c6; text-align: right; margin-left: 20%; } .bot-message { background-color: #e5e5ea; margin-right: 20%; } .input-area { display: flex; gap: 10px; } #user-input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 5px; } button { padding: 10px 20px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; } button:hover { background-color: #45a049; } button:disabled { background-color: #cccccc; cursor: not-allowed; } .config-panel { background-color: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); padding: 20px; margin-bottom: 20px; } .api-key-input { width: 100%; padding: 8px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 5px; box-sizing: border-box; } select, label { margin-right: 10px; } .typing-indicator { display: none; color: #888; font-style: italic; } </style> </head> <body> <h1>LongCat AI ChatBot</h1> <div class="config-panel"> <label for="api-key">API Key:</label> <input type="password" id="api-key" class="api-key-input" placeholder="请输入您的 LongCat API Key(可留空)" /> <label for="model-select">选择模型:</label> <select id="model-select"> <option value="longcat-flash-chat">LongCat-Flash-Chat (高性能通用对话模型)</option> <option value="longcat-flash-thinking">LongCat-Flash-Thinking (深度思考模型)</option> </select> <label for="api-format">API格式:</label> <select id="api-format"> <option value="openai">OpenAI</option> <option value="anthropic">Anthropic</option> </select> </div> <div class="chat-container" id="chat-container"> <div class="message bot-message"> 您好!我是基于LongCat API的聊天机器人。请问有什么我可以帮您的吗? </div> </div> <div class="typing-indicator" id="typing-indicator"> 正在输入中... </div> <div class="input-area"> <input type="text" id="user-input" placeholder="请输入您的消息..." /> <button id="send-btn">发送</button> </div>
<script> const chatContainer = document.getElementById('chat-container'); const userInput = document.getElementById('user-input'); const sendBtn = document.getElementById('send-btn'); const modelSelect = document.getElementById('model-select'); const apiFormatSelect = document.getElementById('api-format'); const apiKeyInput = document.getElementById('api-key'); const typingIndicator = document.getElementById('typing-indicator'); let messages = [ { role: "assistant", content: "您好!我是基于LongCat API的聊天机器人。请问有什么我可以帮您的吗?" } ]; function addMessage(role, content) { const messageDiv = document.createElement('div'); messageDiv.classList.add('message'); messageDiv.classList.add(role === 'user' ? 'user-message' : 'bot-message'); messageDiv.textContent = content; chatContainer.appendChild(messageDiv); chatContainer.scrollTop = chatContainer.scrollHeight; } async function sendMessage() { const content = userInput.value.trim(); if (!content) return; userInput.disabled = true; sendBtn.disabled = true; typingIndicator.style.display = 'block'; addMessage('user', content); userInput.value = ''; messages.push({ role: 'user', content }); try { const response = await fetch('/demos/chatbot/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: messages, model: modelSelect.value, api_format: apiFormatSelect.value, api_key: apiKeyInput.value }) }); const data = await response.json(); if (data.error) { addMessage('assistant', `错误: ${data.error}`); } else { let reply = ''; if (data.choices && data.choices[0] && data.choices[0].message) { reply = data.choices[0].message.content; } else if (data.content) { reply = data.content[0].text || JSON.stringify(data.content); } else { reply = JSON.stringify(data); } addMessage('assistant', reply); messages.push({ role: 'assistant', content: reply }); } } catch (error) { addMessage('assistant', `请求失败: ${error.message}`); } finally { userInput.disabled = false; sendBtn.disabled = false; typingIndicator.style.display = 'none'; userInput.focus(); } } sendBtn.addEventListener('click', sendMessage); userInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } }); window.addEventListener('load', () => { userInput.focus(); }); </script> </body> </html>
|
docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| version: '3.8'
services: ai-chat-frontend: build: ./ai-chat-frontend container_name: ai-chat-frontend-container ports: - "3000:80" networks: - app-ai-chat-network depends_on: - ai-chat-backend restart: unless-stopped environment: - NGINX_HOST=localhost - NGINX_PORT=80
ai-chat-backend: build: ./ai-chat-backend container_name: ai-chat-backend-container ports: - "5000:5000" networks: - app-ai-chat-network restart: unless-stopped environment: - FLASK_ENV=production - FLASK_APP=api.py healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000/health"] interval: 30s timeout: 10s retries: 3
networks: app-ai-chat-network: driver: bridge
|
在本平台的部署过程
我选择在本地机WSL中创建项目,复制源码后执行:
1 2 3 4 5 6 7 8 9 10 11 12
| $ cd project/py/AIChatBot/
$ docker-compose build
$ docker save -o ai-chat-images.tar aichatbot_ai-chat-backend:latest aichatbot_ai-chat-frontend:latest
$ scp ai-chat-images.tar deploy@101.126.131.181:/tmp/
|
进入服务器端后执行:
1 2 3 4 5
| $ cd projects/chatbot/
$ sudo nano docker-compose.yml
|
复制粘贴以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| version: '3.8'
services: ai-chat-frontend: image: aichatbot_ai-chat-frontend:latest container_name: ai-chat-frontend-container ports: - "3000:80" networks: - app-ai-chat-network depends_on: - ai-chat-backend restart: unless-stopped environment: - NGINX_HOST=localhost - NGINX_PORT=80
ai-chat-backend: image: aichatbot_ai-chat-backend:latest container_name: ai-chat-backend-container ports: - "5000:5000" networks: - app-ai-chat-network restart: unless-stopped environment: - FLASK_ENV=production - FLASK_APP=api.py healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000/health"] interval: 30s timeout: 10s retries: 3
networks: app-ai-chat-network: driver: bridge
|
快捷键Ctrl+s保存,Ctrl+x退出,继续执行
1 2 3 4 5 6 7 8
| $ docker load -i /tmp/ai-chat-images.tar
$ docker compose up -d
$ sudo nano /etc/nginx/sites-available/hexo.conf
|
修改文件内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| server { listen 80; server_name 101.126.131.181;
location / { root /var/www/hexo; index index.html;
access_log /var/log/nginx/garden-access.log; }
location /demos/chatbot/ { rewrite ^/demos/chatbot/(.*)$ /$1 break; proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
access_log /var/log/nginx/chatbot-access.log; }
location /chatbot-api/ { proxy_pass http://localhost:5000/; proxy_set_header Host $host; } }
|
快捷键Ctrl+s保存,Ctrl+x退出,继续执行
1 2 3
| $ sudo nginx -t $ sudo systemctl reload nginx
|
增强功能(设置权限密码)
基于路径前缀的网关级访问控制
1. 安装必要依赖:
首先,为这个聊天机器人项目创建一个独立的密码文件(例如 chatbot_users)。
1 2
| sudo apt-get update sudo apt-get install apache2-utils
|
2. 创建或管理密码文件:
找到你配置 location /demos/chatbot/ 的部分,在 proxy_pass 等指令之前,加入 auth_basic 指令。
1 2 3 4
| sudo htpasswd -c /etc/nginx/conf.d/.htpasswd_chatbot username_for_chatbot
|
3. 修改主Nginx配置:
找到你配置 location /demos/chatbot/ 的部分,在 proxy_pass 等指令之前,加入 auth_basic 指令。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| server { listen 80; server_name your_domain.com;
location ^~ /demos/chatbot/ { auth_basic "ChatBot Demo - Restricted Access"; auth_basic_user_file /etc/nginx/conf.d/.htpasswd_chatbot;
proxy_pass http://frontend-docker-container; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;
}
}
|
3. 测试并重载配置
1 2 3 4 5
| sudo nginx -t
sudo systemctl reload nginx
|
✅ 配置生效后的效果
完成以上配置后,访问控制流程将变为:
- 任何用户访问
your_domain.com/demos/chatbot/ 或其下任何子路径(如 /demos/chatbot/index.html 或 /demos/chatbot/api/query)时。
- 主Nginx会首先拦截请求,并返回
401 状态码,要求浏览器弹出认证对话框。
- 用户只有在输入了正确的用户名和密码后,请求才会被继续转发给前端Docker容器,后续的API转发规则也才会正常执行。
- 一次验证,全程有效:浏览器会缓存凭证,在同一会话内访问该路径下的其他资源时通常不再重复询问。
💡 方案优势与扩展
- 精确的路径控制:
location ^~ /demos/chatbot/ 确保了以该路径开头的所有请求都被捕获并保护。
- 统一认证点:认证在主Nginx完成,与后端的Docker应用完全解耦,无需修改任何项目代码。
- 易于管理多个项目:你可以为
/demos/project-a/、/demos/project-b/ 等不同路径配置不同的 auth_basic_user_file,实现分项目、分密码、分用户的精细授权。
- 安全性:比纯前端加密可靠得多。密码验证在服务器端进行,未授权用户无法接触到任何前端文件或API接口。