Ollama API 实战:5分钟搭建你的第一个AI聊天机器人(Python版)
Ollama API 实战:5分钟搭建你的第一个AI聊天机器人(Python版)
最近身边不少做开发的朋友都在聊,想自己动手搞个AI聊天机器人玩玩,但一看到那些大模型的部署和API调用就头大,觉得门槛太高。其实,现在有个工具能让这件事变得像搭积木一样简单,那就是Ollama。它把本地运行大模型这件事封装得极其友好,你不需要关心复杂的模型部署和环境配置,只需要几行Python代码,就能让一个拥有“智能”的对话程序跑起来。这篇文章,我就从一个Python开发者的视角,带你从零开始,手把手地构建一个属于你自己的聊天机器人。我们会聚焦于最核心的Ollama API,用最直白的代码,在5分钟内看到第一个对话结果。无论你是想做个智能客服原型、一个有趣的个人助手,还是单纯想理解AI应用的后端逻辑,这篇实战指南都能给你一个清晰、落地的起点。
1. 起手式:环境准备与Ollama初探
在开始写代码之前,我们得先把“舞台”搭好。Ollama的核心思想是让大模型在本地运行变得轻而易举,它本身是一个服务端程序。因此,第一步永远是在你的机器上安装并启动Ollama服务。
对于macOS和Linux用户,安装通常是一行命令的事。以macOS为例,打开终端直接执行:
curl -fsSL https://ollama.com/install.sh | sh
安装完成后,Ollama服务会自动启动。你可以在终端里输入 ollama run llama3 来快速测试。这个命令会拉取(如果本地没有)并运行Meta的Llama 3模型,你会直接进入一个交互式聊天界面。这证明了你的Ollama服务是健康的。
对于Windows用户,过程同样简单,直接从Ollama官网下载安装程序,像安装普通软件一样完成即可。安装后,你会在系统托盘或服务列表里找到它。
提示:首次运行
ollama run <模型名>会触发模型下载。Llama 3 8B版本大约4-5GB,请确保网络通畅和足够的磁盘空间。你也可以选择更小的模型如phi3或mistral来快速开始。
接下来是我们的Python环境。我强烈建议使用虚拟环境来管理项目依赖,这能避免包版本冲突。打开你的终端或命令行,进入项目目录,执行以下操作:
# 创建并激活虚拟环境(以venv为例)
python -m venv venv
# Windows
venv\Scripts\activate
# macOS/Linux
source venv/bin/activate
激活虚拟环境后,安装我们唯一必需的Python库:requests。它是我们与Ollama API通信的桥梁。
pip install requests
至此,我们的基础环境就绪了。Ollama服务在后台运行,监听默认的 http://localhost:11434 端口,Python环境也准备好了。我们可以通过一个简单的API调用来验证一切是否正常。创建一个名为 test_connection.py 的文件,写入以下代码:
import requests
import json
try:
# 尝试获取本地已下载的模型列表
response = requests.get("http://localhost:11434/api/tags")
if response.status_code == 200:
models = response.json().get('models', [])
print("✅ Ollama 服务连接成功!")
print(f"本地已有模型: {[m['name'] for m in models]}")
else:
print(f"❌ API返回异常状态码: {response.status_code}")
except requests.exceptions.ConnectionError:
print("❌ 无法连接到Ollama服务,请确保Ollama已启动。")
print(" 可以尝试在终端执行 'ollama serve' 来启动服务。")
运行这个脚本,如果看到成功信息和模型列表,恭喜你,舞台已经搭好,演员(模型)也已就位,接下来就是编写我们的剧本(聊天逻辑)了。
2. 核心对话引擎:理解并调用 /api/chat 接口
Ollama提供了多个API端点,但对于构建一个聊天机器人,/api/chat 是最自然、最合适的选择。它原生支持多轮对话的消息格式,与我们熟悉的OpenAI的Chat Completion API非常相似,降低了学习成本。
这个接口的核心在于 messages 参数。它是一个字典列表,每个字典代表对话中的一条消息,包含 role(角色)和 content(内容)两个字段。角色通常只有两种:user 代表用户输入,assistant 代表AI助手的回复。通过维护这个消息列表,我们就实现了对话上下文的保持。
让我们先抛开复杂的流式处理和错误处理,构建一个最基础的、一次性的问答函数。在项目根目录创建 simple_chatbot.py:
import requests
import json
def ask_ollama(prompt, model="llama3"):
"""
向Ollama发送一个简单的提示并获取回复。
"""
url = "http://localhost:11434/api/chat"
# 构建请求数据
payload = {
"model": model,
"messages": [
{"role": "user", "content": prompt}
],
"stream": False # 先使用非流式,一次性获取全部回复
}
try:
response = requests.post(url, json=payload)
response.raise_for_status() # 如果状态码不是200,抛出HTTPError异常
result = response.json()
# 从响应中提取助手的回复内容
assistant_reply = result['message']['content']
return assistant_reply
except requests.exceptions.RequestException as e:
return f"请求出错: {e}"
except KeyError:
return "解析响应数据时出错。"
if __name__ == "__main__":
# 测试我们的小机器人
user_question = "用Python写一个简单的Hello World程序。"
answer = ask_ollama(user_question)
print(f"你: {user_question}")
print(f"AI: {answer}")
运行这个脚本,你应该能看到AI返回的Python代码。这已经是一个功能完整的“一问一答”式聊天机器人了!但它的记忆只有一句话,无法进行连续对话。为了让机器人拥有“记忆”,我们需要在每次对话时,把历史记录也传给API。
我们来升级一下,创建一个能记住上下文的聊天类。新建 context_chatbot.py:
import requests
import json
class SimpleChatBot:
def __init__(self, model="llama3", system_prompt=None):
"""
初始化聊天机器人。
:param model: 使用的模型名称。
:param system_prompt: 系统提示词,用于设定AI的角色和行为。
"""
self.model = model
self.conversation_history = []
self.api_url = "http://localhost:11434/api/chat"
# 如果有系统提示,将其作为第一条消息加入历史
if system_prompt:
self.conversation_history.append({
"role": "system",
"content": system_prompt
})
def chat(self, user_input):
"""
发送用户输入并获取AI回复,同时更新对话历史。
"""
# 将用户输入加入历史
self.conversation_history.append({"role": "user", "content": user_input})
payload = {
"model": self.model,
"messages": self.conversation_history,
"stream": False
}
try:
response = requests.post(self.api_url, json=payload)
response.raise_for_status()
result = response.json()
# 获取AI回复
ai_reply = result['message']['content']
# 将AI回复加入历史
self.conversation_history.append({"role": "assistant", "content": ai_reply})
return ai_reply
except requests.exceptions.RequestException as e:
return f"网络或请求错误: {e}"
except (KeyError, json.JSONDecodeError) as e:
return f"处理响应时出错: {e}"
def get_history(self):
"""获取当前的完整对话历史。"""
return self.conversation_history
def clear_history(self):
"""清空对话历史。"""
# 保留系统提示(如果有)
self.conversation_history = [msg for msg in self.conversation_history if msg['role'] == 'system']
# 使用示例
if __name__ == "__main__":
# 创建一个具有特定角色的机器人
bot = SimpleChatBot(
model="llama3",
system_prompt="你是一个幽默的编程助手,喜欢用比喻解释技术概念。"
)
print("聊天机器人已启动(输入 'quit' 退出)")
while True:
user_input = input("\n你: ")
if user_input.lower() == 'quit':
print("再见!")
break
reply = bot.chat(user_input)
print(f"AI: {reply}")
现在,你的机器人不仅能够连续对话,还能通过 system_prompt 被赋予特定的人格或职责,比如“严谨的代码审查员”或“风趣的故事讲述者”。这就是构建个性化聊天机器人的基石。
3. 提升体验:实现流式响应与参数调优
如果你用过ChatGPT网页版,一定会对那种逐字打印出来的回复效果印象深刻。这种“打字机”效果不仅能降低用户等待的焦虑感,在生成长文本时体验也更佳。Ollama的API同样支持流式响应,实现起来并不复杂。
流式传输的关键在于将请求参数 "stream" 设置为 True,并且客户端(我们的Python程序)需要以流的方式处理HTTP响应。requests 库的 stream=True 参数和 iter_lines() 方法正是为此而生。
让我们修改 SimpleChatBot 类,增加一个流式聊天的方法。创建新文件 streaming_chatbot.py,或者直接在之前的类中添加:
import requests
import json
class StreamingChatBot(SimpleChatBot): # 假设继承自上一节的SimpleChatBot
def __init__(self, model="llama3", system_prompt=None):
super().__init__(model, system_prompt)
def chat_stream(self, user_input):
"""
以流式方式发送用户输入并实时获取AI回复。
返回一个生成器,每次yield一个回复片段。
"""
self.conversation_history.append({"role": "user", "content": user_input})
payload = {
"model": self.model,
"messages": self.conversation_history,
"stream": True # 启用流式
}
try:
# 注意这里设置了 stream=True
with requests.post(self.api_url, json=payload, stream=True) as response:
response.raise_for_status()
full_reply = ""
for line in response.iter_lines():
if line:
# 每行是一个JSON字符串
line_decoded = line.decode('utf-8')
# Ollama的流式响应每行以 "data: " 开头
if line_decoded.startswith('data: '):
json_str = line_decoded[6:] # 去掉 "data: " 前缀
try:
chunk = json.loads(json_str)
# 获取当前片段的文本
piece = chunk.get('message', {}).get('content', '')
if piece:
full_reply += piece
yield piece # 实时产出片段
# 如果收到 done 为 True,表示流结束
if chunk.get('done', False):
break
except json.JSONDecodeError:
# 忽略非JSON行(如心跳包)
continue
# 流结束后,将完整的AI回复加入历史
self.conversation_history.append({"role": "assistant", "content": full_reply})
except requests.exceptions.RequestException as e:
yield f"[流式请求出错: {e}]"
# 使用流式聊天的示例
if __name__ == "__main__":
bot = StreamingChatBot(system_prompt="你是一位诗人。")
print("流式诗人聊天机器人已启动(输入 'quit' 退出)")
while True:
user_input = input("\n你: ")
if user_input.lower() == 'quit':
break
print("AI: ", end="", flush=True)
full_response = ""
for chunk in bot.chat_stream(user_input):
print(chunk, end="", flush=True) # 关键:逐块打印,不换行
full_response += chunk
print() # 最后换行
# 此时,full_response 包含了完整的回复,也已存入历史
运行这段代码,你会看到AI的回复像真人打字一样逐个单词或词组地出现。这种交互体验对于终端界面或WebSocket驱动的网页应用至关重要。
除了流式传输,通过调整生成参数,我们可以精细控制AI的“性格”和输出质量。Ollama在请求的 options 字段中提供了丰富的调参选项。下表列出了一些最常用、效果最直观的参数:
| 参数 | 类型 | 默认值 | 作用与影响 |
|---|---|---|---|
temperature |
float | 0.8 | 创造性/随机性。值越低(如0.1),输出越确定、保守;值越高(如1.5),输出越随机、有创意。 |
top_p |
float | 0.9 | 核采样。与temperature配合,控制从概率质量最高的词汇中采样。通常0.7-0.9效果较好。 |
num_predict |
int | 128 | 最大生成长度。限制单次回复的最大token数,防止生成过长内容。 |
repeat_penalty |
float | 1.1 | 重复惩罚。大于1的值会降低重复词汇的概率,有助于减少循环和啰嗦。 |
seed |
int | 随机 | 随机种子。设置固定的种子可以使相同输入的输出具有可重复性,便于调试。 |
在代码中应用这些参数非常简单,只需在请求的payload中加入 options 字典。例如,我们想要一个更严谨、更简洁的代码助手:
def ask_with_options(prompt, model="llama3"):
url = "http://localhost:11434/api/chat"
payload = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"stream": False,
"options": { # 加入调优参数
"temperature": 0.3, # 降低随机性,让回答更专注
"num_predict": 500, # 允许更长的回答
"repeat_penalty": 1.2, # 加强重复惩罚
"top_p": 0.8
}
}
response = requests.post(url, json=payload)
return response.json()['message']['content']
通过组合流式响应和生成参数调优,你的聊天机器人已经从“能用”进化到了“好用”的阶段。
4. 从脚本到应用:构建一个简单的命令行交互界面
到目前为止,我们的机器人还停留在脚本层面。为了让其更像一个真正的应用,我们可以为其打造一个更友好的命令行界面(CLI),并增加一些实用功能,比如模型切换、历史记录查看、对话导出等。
我们将使用Python内置的 argparse 库来解析命令行参数,并构建一个功能菜单。最终目标是实现一个可以通过命令如 python chatbot_cli.py --model mistral --temp 0.5 来启动,并支持交互式对话的程序。
首先,规划一下我们CLI工具的功能点:
- 交互式聊天模式:核心功能,支持流式输出。
- 模型管理:列出本地可用模型,并允许运行时切换。
- 参数配置:允许通过命令行参数设置温度、最大生成长度等。
- 对话管理:查看当前对话历史,清空历史,或将历史导出为文件。
- 单次问答模式:不进入循环,直接回答一个问题后退出。
下面是一个简化但功能完整的CLI聊天机器人实现框架,保存在 chatbot_cli.py 中:
import argparse
import json
import sys
from datetime import datetime
# 假设我们使用了之前定义的 StreamingChatBot 类
# from streaming_chatbot import StreamingChatBot
class ChatBotCLI:
def __init__(self, model, temperature, max_tokens):
self.bot = StreamingChatBot(model=model)
# 这里可以初始化时设置options,但为了灵活,我们在每次请求时动态设置
self.default_options = {
"temperature": temperature,
"num_predict": max_tokens,
}
self.model = model
def list_local_models(self):
"""获取并打印本地已下载的模型列表。"""
try:
resp = requests.get("http://localhost:11434/api/tags")
models = resp.json().get('models', [])
print("\n本地可用模型:")
for m in models:
print(f" - {m['name']}")
except Exception as e:
print(f"获取模型列表失败: {e}")
def interactive_chat(self):
"""进入交互式聊天循环。"""
print(f"\n开始与 '{self.model}' 对话 (温度: {self.default_options['temperature']})")
print("输入 '/help' 查看命令, '/quit' 退出聊天。")
while True:
try:
user_input = input("\n你: ").strip()
if not user_input:
continue
# 处理命令
if user_input.startswith('/'):
self._handle_command(user_input)
continue
# 正常聊天
print("AI: ", end="", flush=True)
full_reply = ""
# 注意:这里需要修改bot的chat_stream方法,使其能接收options参数
for chunk in self.bot.chat_stream(user_input, options=self.default_options):
print(chunk, end="", flush=True)
full_reply += chunk
print() # 换行
except KeyboardInterrupt:
print("\n\n中断。输入 /quit 退出。")
except EOFError:
print("\n检测到文件结束符。退出。")
break
def _handle_command(self, cmd):
"""处理用户输入的命令。"""
cmd = cmd.lower()
if cmd == '/quit' or cmd == '/exit':
print("再见!")
sys.exit(0)
elif cmd == '/help':
self._print_help()
elif cmd == '/history':
self._show_history()
elif cmd == '/clear':
self.bot.clear_history()
print("对话历史已清空。")
elif cmd == '/models':
self.list_local_models()
elif cmd == '/save':
self._save_history()
else:
print(f"未知命令: {cmd}。输入 /help 查看可用命令。")
def _print_help(self):
help_text = """
可用命令:
/help - 显示此帮助信息
/history - 显示当前对话历史
/clear - 清空对话历史
/models - 列出本地可用模型
/save - 将对话历史保存为JSON文件
/quit - 退出程序
"""
print(help_text)
def _show_history(self):
history = self.bot.get_history()
if not history:
print("对话历史为空。")
return
print("\n=== 对话历史 ===")
for msg in history:
role_display = {"user": "你", "assistant": "AI", "system": "系统"}.get(msg['role'], msg['role'])
print(f"{role_display}: {msg['content'][:100]}...") # 只显示前100字符
def _save_history(self):
filename = f"chat_history_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
history = self.bot.get_history()
with open(filename, 'w', encoding='utf-8') as f:
json.dump(history, f, ensure_ascii=False, indent=2)
print(f"历史已保存至文件: {filename}")
def main():
parser = argparse.ArgumentParser(description="Ollama 命令行聊天机器人")
parser.add_argument('--model', '-m', default='llama3', help='指定使用的模型 (默认: llama3)')
parser.add_argument('--temperature', '-t', type=float, default=0.7, help='生成温度 (默认: 0.7)')
parser.add_argument('--max-tokens', '-n', type=int, default=1024, help='最大生成长度 (默认: 1024)')
parser.add_argument('--list-models', '-l', action='store_true', help='列出本地模型后退出')
args = parser.parse_args()
if args.list_models:
# 简单调用列表函数
try:
resp = requests.get("http://localhost:11434/api/tags")
models = resp.json().get('models', [])
for m in models:
print(m['name'])
except:
print("无法获取模型列表。")
sys.exit(0)
# 启动CLI
cli = ChatBotCLI(args.model, args.temperature, args.max_tokens)
cli.interactive_chat()
if __name__ == "__main__":
# 需要导入requests
import requests
main()
这个CLI程序已经具备了实用工具的雏形。你可以通过 python chatbot_cli.py --model mistral --temperature 0.9 启动一个更具创造性的Mistral模型对话,也可以在聊天过程中使用 /history 查看上下文,用 /save 将有趣的对话保存下来。
5. 进阶整合:打造一个极简的Web聊天界面
命令行工具适合开发者,但对于想分享给朋友或需要更直观交互的场景,一个网页界面显然更合适。利用Python的轻量级Web框架,比如Flask或FastAPI,我们可以用不到100行代码,为聊天机器人加上一个Web API和前端页面。
这里我们选择Flask,因为它足够简单。我们将创建两个端点:一个用于提供HTML页面,另一个用于处理聊天请求的API。前端使用简单的HTML和JavaScript,通过Fetch API与后端通信,并模拟流式输出。
首先,安装Flask:pip install flask。然后创建 app.py:
from flask import Flask, render_template, request, Response, jsonify
import json
import requests
app = Flask(__name__)
# Ollama API的基础URL
OLLAMA_API_BASE = "http://localhost:11434/api"
@app.route('/')
def index():
"""提供主页面。"""
return render_template('index.html') # 稍后创建这个模板
@app.route('/api/chat', methods=['POST'])
def chat_api():
"""处理聊天请求的API端点。"""
data = request.json
user_message = data.get('message', '')
model = data.get('model', 'llama3')
# 构建发送给Ollama的请求数据
# 注意:这里简化了,没有维护服务端的对话历史。实际应用应使用会话(如Flask-Session)来存储不同用户的历史。
payload = {
"model": model,
"messages": [{"role": "user", "content": user_message}],
"stream": True,
"options": {
"temperature": 0.7,
"num_predict": 1000
}
}
# 定义一个生成器函数,作为服务器发送事件(Server-Sent Events)的响应
def generate():
try:
# 将请求转发给Ollama
resp = requests.post(f"{OLLAMA_API_BASE}/chat", json=payload, stream=True)
resp.raise_for_status()
for line in resp.iter_lines():
if line:
line_decoded = line.decode('utf-8')
if line_decoded.startswith('data: '):
json_str = line_decoded[6:]
try:
chunk = json.loads(json_str)
content_piece = chunk.get('message', {}).get('content', '')
if content_piece:
# 将每个片段以SSE格式发送
yield f"data: {json.dumps({'content': content_piece})}\n\n"
if chunk.get('done', False):
yield "data: [DONE]\n\n"
break
except json.JSONDecodeError:
continue
except Exception as e:
yield f"data: {json.dumps({'error': str(e)})}\n\n"
# 返回一个流式响应,内容类型为 text/event-stream
return Response(generate(), mimetype='text/event-stream')
if __name__ == '__main__':
app.run(debug=True, port=5000)
接下来,在项目根目录创建一个 templates 文件夹,并在其中创建 index.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的Ollama聊天机器人</title>
<style>
body { font-family: sans-serif; max-width: 800px; margin: 20px auto; padding: 20px; }
#chatbox { border: 1px solid #ccc; height: 400px; overflow-y: auto; padding: 10px; margin-bottom: 10px; }
.message { margin-bottom: 10px; }
.user { text-align: right; color: #0066cc; }
.assistant { text-align: left; color: #333; }
#inputArea { display: flex; }
#userInput { flex-grow: 1; padding: 10px; }
#sendBtn { padding: 10px 20px; }
.thinking { color: #888; font-style: italic; }
</style>
</head>
<body>
<h1>🤖 我的AI聊天伙伴</h1>
<div id="chatbox"></div>
<div id="inputArea">
<input type="text" id="userInput" placeholder="输入你的问题..." autocomplete="off">
<button id="sendBtn">发送</button>
</div>
<div>
<label>模型: </label>
<select id="modelSelect">
<option value="llama3">Llama 3</option>
<option value="mistral">Mistral</option>
<option value="phi3">Phi-3</option>
</select>
</div>
<script>
const chatbox = document.getElementById('chatbox');
const userInput = document.getElementById('userInput');
const sendBtn = document.getElementById('sendBtn');
const modelSelect = document.getElementById('modelSelect');
function addMessage(content, sender) {
const msgDiv = document.createElement('div');
msgDiv.className = `message ${sender}`;
msgDiv.textContent = `${sender === 'user' ? '你' : 'AI'}: ${content}`;
chatbox.appendChild(msgDiv);
chatbox.scrollTop = chatbox.scrollHeight; // 滚动到底部
}
function appendToLastMessage(content) {
const lastMsg = chatbox.lastChild;
if (lastMsg && lastMsg.className.includes('assistant')) {
lastMsg.textContent += content;
chatbox.scrollTop = chatbox.scrollHeight;
}
}
async function sendMessage() {
const message = userInput.value.trim();
const model = modelSelect.value;
if (!message) return;
// 显示用户消息
addMessage(message, 'user');
userInput.value = '';
userInput.focus();
// 显示“思考中”提示
const thinkingDiv = document.createElement('div');
thinkingDiv.className = 'message assistant thinking';
thinkingDiv.textContent = 'AI: 思考中...';
chatbox.appendChild(thinkingDiv);
try {
// 发起流式请求
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, model })
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const reader = response.body.getReader();
const decoder = new TextDecoder();
// 移除“思考中”提示,准备显示真实回复
chatbox.removeChild(thinkingDiv);
addMessage('', 'assistant'); // 先创建一个空消息容器
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(line => line.trim());
for (const line of lines) {
if (line.startsWith('data: ')) {
const dataStr = line.slice(6);
if (dataStr === '[DONE]') {
return;
}
try {
const data = JSON.parse(dataStr);
if (data.error) {
console.error('Error:', data.error);
appendToLastMessage(` [错误: ${data.error}]`);
} else if (data.content) {
appendToLastMessage(data.content);
}
} catch (e) {
console.error('解析SSE数据失败:', e);
}
}
}
}
} catch (error) {
console.error('请求失败:', error);
chatbox.removeChild(thinkingDiv);
addMessage(`抱歉,请求出错: ${error.message}`, 'assistant');
}
}
sendBtn.addEventListener('click', sendMessage);
userInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendMessage();
});
</script>
</body>
</html>
现在,运行 python app.py,打开浏览器访问 http://localhost:5000,一个拥有流式输出效果的Web聊天机器人就诞生了。你可以选择不同的模型,体验实时对话。这个例子虽然简单,但它清晰地展示了如何将Ollama API无缝集成到Web应用中,为后续开发更复杂的AI应用(如知识库问答、智能客服工单系统)提供了可扩展的骨架。
走到这一步,你已经完成了一个从环境搭建、核心API调用、参数调优、CLI工具开发到Web界面整合的完整闭环。整个过程没有复杂的理论,全是可运行、可修改的实战代码。我自己的体验是,Ollama极大地降低了个人开发者探索大模型应用的门槛,让你能快速将想法变成可交互的原型。在实际项目中,你可能还需要考虑用户会话管理、历史记录持久化(比如存数据库)、更复杂的错误处理以及前端界面的美化。但有了今天打下的这个基础,那些扩展都将是顺理成章的事情。最让我惊喜的是,整个开发过程是如此地“Pythonic”——简洁、直接、高效,这正是Python生态的魅力所在。
更多推荐

所有评论(0)