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

Docker
Python
Nginx

功能特性

  • 🤖 支持多种 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

# 访问应用
# 前端界面: http://localhost:3000
# 后端API: http://localhost:5000

手动部署

后端服务

  1. 进入后端目录:
1
cd ai-chat-backend
  1. 安装依赖:
1
pip install -r requirements.txt
  1. 运行后端服务:
1
python api.py

默认监听端口为 5000

前端界面

  1. 进入前端目录:
1
cd ai-chat-frontend
  1. 使用 nginx 启动前端服务(确保已安装 nginx):
1
2
3
4
5
# 将 dist 目录内容复制到 nginx html 目录
cp -r dist/* /usr/share/nginx/html/

# 启动 nginx
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 深度思考模型

使用说明

  1. 启动服务后,访问前端界面 http://localhost:3000
  2. 在配置面板中:
    • 输入您的 API Key(可选)
    • 选择要使用的模型
    • 选择 API 格式(OpenAI 或 Anthropic)
  3. 在底部输入框中输入消息,点击发送或按回车键
  4. 等待机器人回复

API 接口

/api/chat (POST)

处理聊天请求并转发到 LongCat API。

请求参数:

1
2
3
4
5
6
{
"messages": [], // 消息历史数组
"model": "longcat-flash-chat", // 模型标识符
"api_format": "openai", // API 格式 (openai/anthropic)
"api_key": "" // API 密钥(可选)
}

/api/models (GET)

列出所有支持的模型。

/health (GET)

健康检查接口。

注意事项

  • 默认情况下不需要 API Key 即可使用
  • 如需在生产环境中部署,请适当调整安全配置
  • 前端通过 Nginx 配置将 /api 请求代理到后端服务
  • Docker 部署时,前端和后端通过自定义网络进行通信

许可证

[待补充]

源码

ai-chat-backend

api.py

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 API配置
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'
}

# 如果提供了API Key,则添加到请求头中
if api_key:
headers['Authorization'] = f'Bearer {api_key}'

# 根据API格式选择不同的端点和数据结构
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:
# 调用LongCat API
response = requests.post(api_url, json=payload, headers=headers)
response.raise_for_status()

# 返回API响应
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
# 基于Python官方镜像
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
# 基于nginx镜像
FROM nginx:alpine

# 复制前端构建文件
COPY dist /usr/share/nginx/html

# 复制nginx配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

# 暴露端口
EXPOSE 80

# 启动nginx(默认命令已包含,这里只是说明)
# CMD ["nginx", "-g", "daemon off;"]

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;
# try_files $uri $uri/ /index.html;
}

# 代理API请求到后端容器
location /api {
# 注意:这里用的是Docker服务名"ai-chat-backend",不是localhost
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 {
// 发送到后端 (此时的/demos/chatbot 为入口的前缀,在主Nginx中配置了)
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 {
// 提取回复内容(根据API格式不同可能有差异)
let reply = '';
if (data.choices && data.choices[0] && data.choices[0].message) {
// OpenAI格式
reply = data.choices[0].message.content;
} else if (data.content) {
// Anthropic格式
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" # 映射到主机的3000端口 # 主机端口 3000、3001、3002……
networks:
- app-ai-chat-network # 网络名
depends_on:
- ai-chat-backend # 依赖的后端(服务名)
restart: unless-stopped
# 环境变量
environment:
- NGINX_HOST=localhost
- NGINX_PORT=80

# 后端服务B容器
ai-chat-backend: # 服务名(对应项目后端)
build: ./ai-chat-backend
container_name: ai-chat-backend-container # 后端代码所在目录
ports:
- "5000:5000" # 可选:映射到主机5000端口(开发用) 主机端口 5000、5001、5002……
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
# 进入Garden结构的项目目录
$ cd projects/chatbot/

# 新建docker-compose.yml
$ 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:
# 前端页面B容器
ai-chat-frontend:
image: aichatbot_ai-chat-frontend:latest
container_name: ai-chat-frontend-container
ports:
- "3000:80" # 映射到主机的3000端口
networks:
- app-ai-chat-network
depends_on:
- ai-chat-backend
restart: unless-stopped
# 环境变量
environment:
- NGINX_HOST=localhost
- NGINX_PORT=80

# 后端服务B容器
ai-chat-backend:
image: aichatbot_ai-chat-backend:latest
container_name: ai-chat-backend-container
ports:
- "5000: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

# 修改主Nginx配置
$ 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; # 或你的域名/服务器IP

# Hexo 静态博客(保持不变)
location / {
root /var/www/hexo;
index index.html;

# 记录访问日志
access_log /var/log/nginx/garden-access.log;
}

# 前端页面 - 反向代理到Docker容器
location /demos/chatbot/ {
rewrite ^/demos/chatbot/(.*)$ /$1 break;
# 转发到Docker容器(端口3000)
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;
}

# 3. 直接访问后端API(可选,开发用)
location /chatbot-api/ {
proxy_pass http://localhost:5000/;
proxy_set_header Host $host;
}
}

快捷键Ctrl+s保存,Ctrl+x退出,继续执行

1
2
3
# 验证格式,重载Nginx配置
$ 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
# 系统会提示你输入并确认该用户的密码。
# 如需添加更多用户,去掉 `-c` 参数再次运行。

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;

# ... 其他配置 ...

# 保护整个 /demos/chatbot/ 路径空间
location ^~ /demos/chatbot/ {
# 1. 启用认证并设置提示信息
auth_basic "ChatBot Demo - Restricted Access";
auth_basic_user_file /etc/nginx/conf.d/.htpasswd_chatbot;

# 2. 原有的代理和重写配置保持不变
# 将带有前缀的请求代理到前端Docker容器
proxy_pass http://frontend-docker-container; # 请替换为你的前端容器地址或Upstream名称
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

# 3. 如果你的主Nginx还负责转发API请求,这个认证对API路径同样生效
# 例如,所有 /demos/chatbot/api/ 的请求也会先触发密码验证
}

# 你可以为其他Demo项目(如/demos/other/)创建类似的受保护location块
# location ^~ /demos/other/ {
# auth_basic "Other Demo";
# auth_basic_user_file /etc/nginx/conf.d/.htpasswd_other;
# proxy_pass http://other-frontend-container;
# }
}

3. 测试并重载配置

1
2
3
4
5
# 测试配置文件语法是否正确
sudo nginx -t

# 如果测试通过,重载Nginx使配置生效
sudo systemctl reload nginx # 或 sudo nginx -s reload

✅ 配置生效后的效果

完成以上配置后,访问控制流程将变为:

  1. 任何用户访问 your_domain.com/demos/chatbot/ 或其下任何子路径(如 /demos/chatbot/index.html/demos/chatbot/api/query)时。
  2. 主Nginx会首先拦截请求,并返回 401 状态码,要求浏览器弹出认证对话框。
  3. 用户只有在输入了正确的用户名和密码后,请求才会被继续转发给前端Docker容器,后续的API转发规则也才会正常执行。
  4. 一次验证,全程有效:浏览器会缓存凭证,在同一会话内访问该路径下的其他资源时通常不再重复询问。

💡 方案优势与扩展

  • 精确的路径控制location ^~ /demos/chatbot/ 确保了以该路径开头的所有请求都被捕获并保护。
  • 统一认证点:认证在主Nginx完成,与后端的Docker应用完全解耦,无需修改任何项目代码。
  • 易于管理多个项目:你可以为 /demos/project-a//demos/project-b/ 等不同路径配置不同的 auth_basic_user_file,实现分项目、分密码、分用户的精细授权。
  • 安全性:比纯前端加密可靠得多。密码验证在服务器端进行,未授权用户无法接触到任何前端文件或API接口。