Phi-3 Mini开源模型部署案例:Docker Compose多服务编排(含向量DB)
本文介绍了如何在星图GPU平台上自动化部署🌿 Phi-3 Forest Laboratory | 森林晨曦实验室镜像,快速搭建一个集成了Phi-3 Mini模型、Web界面和向量数据库的完整AI对话系统。通过Docker Compose编排,用户可轻松获得一个具备对话记忆功能的智能助手,适用于代码编写、学习答疑和创意写作等多种场景。
Phi-3 Mini开源模型部署案例:Docker Compose多服务编排(含向量DB)
1. 引言
想象一下,你有一个功能强大的轻量级AI模型,它不仅逻辑严谨、响应迅速,还能记住长达一本书的对话内容。现在,你想把它变成一个既有实用价值,又有审美趣味的在线应用,同时还要给它配上“记忆”能力,让它能记住你们聊过的所有事情。
这听起来是不是有点复杂?需要部署模型服务、搭建Web界面、配置向量数据库,还要让它们之间能顺畅通信。如果手动一个个去配置,光是环境依赖和网络设置就能让人头疼半天。
今天,我们就来解决这个问题。我将带你用Docker Compose,像搭积木一样,轻松部署一个完整的Phi-3 Mini AI对话系统。这个系统不仅包含了模型推理服务、美观的Web界面,还集成了向量数据库,让AI能记住你们的每一次对话。
2. 项目概览:我们要搭建什么?
在开始动手之前,我们先看看最终要搭建的系统长什么样,由哪些部分组成。
2.1 系统架构
整个系统由三个核心服务组成,它们通过Docker Compose编排在一起:
- 模型服务:基于Phi-3 Mini 128K Instruct模型,负责AI推理和对话生成
- Web界面:基于Streamlit的治愈系森林主题界面,提供用户交互
- 向量数据库:基于ChromaDB,用于存储和检索对话历史
这三个服务的关系很简单:你在Web界面上输入问题,界面把问题发给模型服务,模型生成回答后返回给界面显示。同时,重要的对话内容会被存入向量数据库,下次你可以基于历史对话继续聊天。
2.2 为什么选择这个组合?
你可能会问,为什么选这些技术?我来简单解释一下:
- Phi-3 Mini模型:只有38亿参数,但在逻辑推理、代码生成等方面表现优秀,支持12.8万token的超长上下文,而且推理速度快
- Streamlit界面:用Python就能快速搭建Web应用,开发简单,界面美观
- ChromaDB向量数据库:轻量级、易部署,专门为AI应用设计,适合存储文本的向量表示
- Docker Compose:一键启动所有服务,不用操心环境配置和网络连接
这个组合既保证了功能完整,又控制了部署复杂度,特别适合个人开发者和小团队使用。
3. 环境准备与快速部署
好了,理论说完了,现在开始动手。我会带你一步步完成部署,从零开始到完全运行。
3.1 你需要准备什么?
在开始之前,请确保你的电脑上已经安装了以下工具:
- Docker:版本20.10以上
- Docker Compose:版本2.0以上
- 至少16GB内存:模型运行需要一定内存
- 支持CUDA的NVIDIA显卡(可选):如果有显卡,推理速度会快很多
如果你还没有安装Docker,可以去Docker官网下载安装包,按照指引安装就行。安装完成后,打开终端输入以下命令检查是否安装成功:
docker --version
docker-compose --version
如果能看到版本号,说明安装成功了。
3.2 获取项目文件
我们需要先下载项目所需的文件。创建一个新文件夹,比如叫做phi3-forest-lab,然后在这个文件夹里创建以下文件。
第一步:创建docker-compose.yml文件
这是最重要的文件,它定义了所有服务如何运行。创建一个名为docker-compose.yml的文件,内容如下:
version: '3.8'
services:
# 模型服务
phi3-model:
image: ghcr.io/huggingface/text-generation-inference:latest
container_name: phi3-model
runtime: nvidia # 如果有GPU的话
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
ports:
- "8080:80"
environment:
- MODEL_ID=microsoft/Phi-3-mini-128k-instruct
- QUANTIZE=bitsandbytes-nf4
- MAX_BATCH_PREFILL_TOKENS=32768
- MAX_INPUT_LENGTH=131072
- MAX_TOTAL_TOKENS=131072
volumes:
- ./models:/data
command: --model-id ${MODEL_ID} --quantize ${QUANTIZE} --max-batch-prefill-tokens ${MAX_BATCH_PREFILL_TOKENS} --max-input-length ${MAX_INPUT_LENGTH} --max-total-tokens ${MAX_TOTAL_TOKENS}
networks:
- phi3-network
# 向量数据库
chromadb:
image: chromadb/chroma:latest
container_name: chromadb
ports:
- "8000:8000"
environment:
- IS_PERSISTENT=TRUE
- PERSIST_DIRECTORY=/chroma/chroma
volumes:
- ./chroma_data:/chroma/chroma
networks:
- phi3-network
# Web界面
web-ui:
build: .
container_name: phi3-web-ui
ports:
- "7860:7860"
environment:
- MODEL_API_URL=http://phi3-model:80
- CHROMA_API_URL=http://chromadb:8000
depends_on:
- phi3-model
- chromadb
networks:
- phi3-network
networks:
phi3-network:
driver: bridge
第二步:创建Dockerfile
接下来创建Web界面的Dockerfile。创建一个名为Dockerfile的文件,内容如下:
FROM python:3.9-slim
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y \
gcc \
g++ \
&& rm -rf /var/lib/apt/lists/*
# 复制依赖文件
COPY requirements.txt .
# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY app.py .
COPY utils.py .
COPY assets/ ./assets/
# 暴露端口
EXPOSE 7860
# 启动应用
CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]
第三步:创建requirements.txt
这是Python依赖文件,创建一个名为requirements.txt的文件:
streamlit>=1.28.0
requests>=2.31.0
chromadb>=0.4.15
sentence-transformers>=2.2.2
pydantic>=2.0.0
python-dotenv>=1.0.0
第四步:创建应用代码文件
现在创建主要的应用文件。先创建app.py:
import streamlit as st
import requests
import json
from typing import List, Dict
import chromadb
from sentence_transformers import SentenceTransformer
import time
# 页面配置
st.set_page_config(
page_title="Phi-3 Forest Laboratory",
page_icon="🌿",
layout="wide",
initial_sidebar_state="expanded"
)
# 自定义CSS样式
def load_css():
st.markdown("""
<style>
/* 主背景 - 森林渐变 */
.stApp {
background: linear-gradient(135deg, #f5f7fa 0%, #e4efe9 100%);
}
/* 聊天气泡样式 */
.stChatMessage {
border-radius: 20px !important;
padding: 20px !important;
margin: 10px 0 !important;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important;
}
.stChatMessage[data-testid="user"] {
background-color: #e8f5e9 !important;
border-left: 5px solid #4caf50 !important;
}
.stChatMessage[data-testid="assistant"] {
background-color: #ffffff !important;
border-left: 5px solid #81c784 !important;
}
/* 输入框样式 */
.stTextInput > div > div > input {
border-radius: 10px !important;
border: 2px solid #81c784 !important;
}
/* 按钮样式 */
.stButton > button {
border-radius: 10px !important;
background: linear-gradient(135deg, #81c784 0%, #4caf50 100%) !important;
color: white !important;
border: none !important;
padding: 10px 24px !important;
}
/* 侧边栏样式 */
.css-1d391kg {
background: linear-gradient(180deg, #f8fff9 0%, #e8f5e9 100%) !important;
}
</style>
""", unsafe_allow_html=True)
# 初始化会话状态
def init_session_state():
if "messages" not in st.session_state:
st.session_state.messages = []
if "chroma_client" not in st.session_state:
st.session_state.chroma_client = None
if "embedding_model" not in st.session_state:
st.session_state.embedding_model = None
if "collection" not in st.session_state:
st.session_state.collection = None
# 初始化向量数据库
def init_chromadb():
try:
chroma_client = chromadb.HttpClient(
host="chromadb",
port=8000
)
# 创建或获取集合
collection = chroma_client.get_or_create_collection(
name="phi3_conversations",
metadata={"description": "Phi-3对话历史存储"}
)
# 加载嵌入模型
embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
st.session_state.chroma_client = chroma_client
st.session_state.embedding_model = embedding_model
st.session_state.collection = collection
return True
except Exception as e:
st.error(f"初始化向量数据库失败: {str(e)}")
return False
# 调用模型API
def call_phi3_api(prompt: str, temperature: float = 0.7) -> str:
try:
url = "http://phi3-model:80/generate"
payload = {
"inputs": prompt,
"parameters": {
"temperature": temperature,
"max_new_tokens": 1024,
"do_sample": True,
"top_p": 0.95,
"repetition_penalty": 1.1
}
}
response = requests.post(url, json=payload, timeout=300)
if response.status_code == 200:
result = response.json()
return result[0]["generated_text"]
else:
return f"请求失败: {response.status_code}"
except Exception as e:
return f"调用API时出错: {str(e)}"
# 保存对话到向量数据库
def save_to_chromadb(user_input: str, assistant_response: str):
if not st.session_state.collection or not st.session_state.embedding_model:
return
try:
# 生成嵌入向量
text_to_embed = f"用户: {user_input}\n助手: {assistant_response}"
embedding = st.session_state.embedding_model.encode(text_to_embed).tolist()
# 准备元数据
metadata = {
"user_input": user_input,
"assistant_response": assistant_response,
"timestamp": time.time()
}
# 生成唯一ID
doc_id = f"conv_{int(time.time())}_{hash(user_input) % 10000}"
# 添加到集合
st.session_state.collection.add(
embeddings=[embedding],
documents=[text_to_embed],
metadatas=[metadata],
ids=[doc_id]
)
except Exception as e:
st.warning(f"保存对话历史时出错: {str(e)}")
# 从向量数据库检索相关历史
def search_chromadb(query: str, n_results: int = 3) -> List[Dict]:
if not st.session_state.collection or not st.session_state.embedding_model:
return []
try:
# 生成查询向量
query_embedding = st.session_state.embedding_model.encode(query).tolist()
# 搜索相似对话
results = st.session_state.collection.query(
query_embeddings=[query_embedding],
n_results=n_results
)
# 格式化结果
formatted_results = []
if results["documents"]:
for i in range(len(results["documents"][0])):
formatted_results.append({
"content": results["documents"][0][i],
"metadata": results["metadatas"][0][i],
"distance": results["distances"][0][i]
})
return formatted_results
except Exception as e:
st.warning(f"检索历史对话时出错: {str(e)}")
return []
# 主应用
def main():
# 加载CSS
load_css()
# 初始化会话状态
init_session_state()
# 侧边栏
with st.sidebar:
st.title("🌿 森林控制台")
# 温度调节
temperature = st.slider(
"🌡️ 创造力温度",
min_value=0.1,
max_value=1.5,
value=0.7,
step=0.1,
help="值越低回答越严谨,值越高越有创意"
)
# 历史记录开关
use_history = st.checkbox("📚 启用对话记忆", value=True)
# 重置按钮
if st.button("🍂 拂去往事", use_container_width=True):
st.session_state.messages = []
st.rerun()
st.divider()
# 系统状态
st.subheader("系统状态")
# 初始化向量数据库
if st.session_state.chroma_client is None:
if st.button("初始化记忆库", use_container_width=True):
with st.spinner("正在连接记忆库..."):
if init_chromadb():
st.success("记忆库已连接")
st.rerun()
else:
st.success("✅ 记忆库已连接")
# 显示历史统计
if st.session_state.collection:
count = st.session_state.collection.count()
st.info(f"已存储对话: {count} 条")
# 主界面
st.title("🌿 Phi-3 Forest Laboratory")
st.caption("在森林的深处,听见智慧的呼吸。")
# 显示聊天历史
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# 聊天输入
if prompt := st.chat_input("向森林深处发出讯息..."):
# 添加用户消息
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# 准备上下文
context = ""
if use_history and st.session_state.collection:
# 检索相关历史对话
historical_contexts = search_chromadb(prompt)
if historical_contexts:
context = "\n\n相关历史对话:\n"
for i, hist in enumerate(historical_contexts[:2], 1):
context += f"{i}. {hist['content'][:200]}...\n"
# 构建完整提示
full_prompt = f"{context}\n\n当前对话:\n用户: {prompt}\n助手:"
# 生成助手回复
with st.chat_message("assistant"):
with st.spinner("🌲 森林正在思考..."):
response = call_phi3_api(full_prompt, temperature)
# 流式显示回复
message_placeholder = st.empty()
full_response = ""
# 模拟流式输出
for chunk in response.split():
full_response += chunk + " "
message_placeholder.markdown(full_response + "▌")
time.sleep(0.05)
message_placeholder.markdown(full_response)
# 保存到会话状态
st.session_state.messages.append({"role": "assistant", "content": response})
# 保存到向量数据库
if use_history:
save_to_chromadb(prompt, response)
if __name__ == "__main__":
main()
第五步:创建工具函数文件
再创建一个utils.py文件,用于存放一些工具函数:
import hashlib
from datetime import datetime
from typing import Optional
def generate_conversation_id(user_input: str, timestamp: Optional[float] = None) -> str:
"""生成对话ID"""
if timestamp is None:
timestamp = datetime.now().timestamp()
# 使用用户输入和时间戳生成唯一ID
input_hash = hashlib.md5(user_input.encode()).hexdigest()[:8]
return f"conv_{int(timestamp)}_{input_hash}"
def format_timestamp(timestamp: float) -> str:
"""格式化时间戳"""
return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
def truncate_text(text: str, max_length: int = 200) -> str:
"""截断文本"""
if len(text) <= max_length:
return text
return text[:max_length] + "..."
def validate_model_response(response: str) -> bool:
"""验证模型响应是否有效"""
if not response or response.strip() == "":
return False
# 检查是否包含错误信息
error_keywords = ["错误", "失败", "出错", "error", "failed", "exception"]
for keyword in error_keywords:
if keyword.lower() in response.lower():
return False
return True
第六步:创建assets文件夹
最后创建一个assets文件夹,里面可以放一些静态资源,比如图片、图标等。这里我们先创建一个空文件夹:
mkdir assets
3.3 一键启动所有服务
现在所有文件都准备好了,目录结构应该是这样的:
phi3-forest-lab/
├── docker-compose.yml
├── Dockerfile
├── requirements.txt
├── app.py
├── utils.py
└── assets/
打开终端,进入这个文件夹,然后运行一个简单的命令:
docker-compose up -d
这个命令会做以下几件事:
- 下载Phi-3 Mini模型(第一次运行需要下载,大概7GB)
- 启动ChromaDB向量数据库
- 构建并启动Web界面
- 把所有服务连接在一起
第一次运行可能需要10-20分钟,主要时间花在下载模型上。你可以用下面的命令查看运行状态:
docker-compose logs -f
看到所有服务都正常启动后,打开浏览器,访问 http://localhost:7860,就能看到你的Phi-3 Forest Laboratory了!
4. 使用你的AI对话系统
现在系统已经运行起来了,我来带你看看怎么使用它。
4.1 第一次对话
打开浏览器,访问 http://localhost:7860,你会看到一个清新简洁的界面。在底部的输入框里,试着问一些问题:
- "你好,介绍一下你自己"
- "Python里怎么快速排序一个列表?"
- "帮我写一个简单的网页登录界面"
你会看到AI的回复以流式的方式显示出来,就像真的在思考一样。
4.2 调节创造力
在左侧的侧边栏,你可以看到一个叫"创造力温度"的滑块。这个值控制着AI回答的创造性:
- 低温度(0.1-0.5):回答更加严谨、准确,适合技术问题、代码生成
- 中等温度(0.6-0.9):平衡创造性和准确性,适合一般对话
- 高温度(1.0-1.5):回答更加有创意、多样化,适合写故事、诗歌
你可以根据不同的需求调整这个值。
4.3 使用对话记忆
系统默认启用了"对话记忆"功能。这意味着你的对话会被保存到向量数据库里。当你问类似的问题时,AI可以参考之前的对话历史,给出更连贯的回答。
比如你可以先问:"Python里列表和元组有什么区别?" 然后问:"那我应该什么时候用列表,什么时候用元组呢?"
第二个问题AI会参考第一个问题的回答,给出更贴切的建议。
4.4 查看和管理历史
在侧边栏,你可以看到"已存储对话"的数量。如果你想清空所有历史,点击"🍂 拂去往事"按钮,所有的对话记忆都会被清除。
5. 实际应用场景
这个系统不只是个玩具,它在很多实际场景中都能发挥作用。
5.1 个人学习助手
你可以用它来学习编程、准备考试、或者了解新知识。比如:
- "解释一下机器学习中的梯度下降算法"
- "帮我制定一个学习Python的30天计划"
- "用简单的例子说明什么是递归函数"
Phi-3 Mini的逻辑推理能力很强,能给出清晰、准确的解释。
5.2 代码编写和调试
作为开发者,你可以用它来:
- "写一个Flask REST API的示例"
- "帮我调试这段Python代码,为什么报错?"
- "用React写一个TODO列表组件"
模型支持128K的上下文,意味着你可以把大段的代码贴给它看,它会帮你分析问题。
5.3 创意写作和头脑风暴
调高创造力温度,它可以帮你:
- "写一个关于人工智能的短篇科幻故事开头"
- "为我的咖啡店想10个有创意的名字"
- "帮我写一封给客户的道歉邮件"
5.4 长期项目协作
因为有了向量数据库的记忆功能,你可以:
- 长期在同一个项目上和AI协作
- AI会记住你们之前讨论过的需求、设计决策
- 每次对话都基于完整的历史上下文
这对于软件开发、论文写作、研究项目特别有用。
6. 常见问题与解决
在使用的过程中,你可能会遇到一些问题。这里我整理了一些常见问题和解决方法。
6.1 模型下载太慢怎么办?
第一次运行需要下载7GB左右的模型文件。如果下载慢,你可以:
- 使用镜像加速:修改docker-compose.yml中的模型地址为国内镜像
- 手动下载:先到HuggingFace下载模型,放到
./models文件夹 - 使用已经下载好的模型:如果你有其他地方下载的模型,可以直接使用
6.2 内存不够怎么办?
Phi-3 Mini需要一定的内存来运行:
- 纯CPU运行:需要至少8GB空闲内存
- GPU运行:需要至少4GB显存(如RTX 3060以上)
如果内存不足,可以尝试:
# 在docker-compose.yml中调整资源限制
phi3-model:
deploy:
resources:
limits:
memory: 8G
6.3 如何备份对话历史?
所有的对话都保存在./chroma_data文件夹里。你可以定期备份这个文件夹:
# 备份
tar -czf chroma_backup_$(date +%Y%m%d).tar.gz chroma_data/
# 恢复
tar -xzf chroma_backup_20240315.tar.gz
6.4 如何更新到新版本?
如果你想更新模型或者界面:
# 停止服务
docker-compose down
# 拉取最新镜像
docker-compose pull
# 重新启动
docker-compose up -d
6.5 性能优化建议
如果觉得响应速度不够快,可以尝试:
- 使用GPU:确保你的显卡驱动和Docker GPU支持已正确安装
- 调整参数:减少
max_new_tokens的值,生成更短的回复 - 使用量化:我们已经使用了4-bit量化,这是内存和速度的很好平衡
7. 进阶配置与定制
如果你对这个基础版本满意了,想要更多功能,这里有一些进阶的定制方法。
7.1 更换模型
如果你想试试其他模型,只需要修改docker-compose.yml中的一行:
environment:
- MODEL_ID=microsoft/Phi-3-mini-128k-instruct # 改成其他模型
支持的模型包括:
microsoft/Phi-3-mini-4k-instruct:4K上下文版本,更快microsoft/Phi-3-small-128k-instruct:小尺寸版本microsoft/Phi-3-medium-128k-instruct:中等尺寸,能力更强
7.2 添加身份验证
如果你想给Web界面加上密码保护,修改app.py:
# 在main函数开头添加
def check_password():
if 'authenticated' not in st.session_state:
st.session_state.authenticated = False
if not st.session_state.authenticated:
password = st.text_input("请输入访问密码", type="password")
if password == "你的密码": # 设置你的密码
st.session_state.authenticated = True
st.rerun()
elif password:
st.error("密码错误")
return False
return True
# 然后在main()开头调用
if not check_password():
st.stop()
7.3 集成其他工具
你还可以集成其他AI工具,比如:
- 联网搜索:添加搜索功能,让AI能获取最新信息
- 文件上传:让用户上传文档,AI基于文档内容回答
- 多模态支持:添加图片理解功能
这些都需要对代码进行一些扩展,但基本思路是一样的:添加新的服务,然后在Web界面中集成。
7.4 部署到服务器
如果你想在云服务器上部署:
- 选择服务器:建议至少4核CPU、16GB内存、50GB硬盘
- 安装Docker:按照服务器系统的文档安装
- 上传文件:把整个项目文件夹上传到服务器
- 修改配置:调整docker-compose.yml中的端口映射
- 启动服务:同样运行
docker-compose up -d
记得配置防火墙,开放7860端口(Web界面)和必要的其他端口。
8. 总结
通过这个教程,我们完成了一个完整的AI对话系统的部署。让我简单总结一下我们都做了什么:
我们搭建的系统有这些特点:
- 基于强大的Phi-3 Mini模型,逻辑推理能力强,响应速度快
- 拥有美观的森林主题界面,使用体验好
- 集成了向量数据库,能记住对话历史
- 用Docker Compose一键部署,简单方便
你学到的关键技能:
- 如何使用Docker Compose编排多个服务
- 如何部署大语言模型服务
- 如何集成向量数据库
- 如何用Streamlit快速搭建Web界面
- 如何让这些服务协同工作
这个项目的价值:
- 对于学习者:一个随时可用的AI学习伙伴
- 对于开发者:一个可扩展的AI应用基础框架
- 对于研究者:一个方便的模型测试和演示平台
最重要的是,你现在有了一个完全在自己控制下的AI系统。你可以随意修改它、扩展它、用它来解决实际问题。所有的代码都在你手里,所有的数据都在你的服务器上,不用担心隐私问题,也不用担心服务突然不可用。
AI技术正在快速发展,但很多时候我们觉得它离我们很远。通过这个项目,我希望你能感受到:搭建自己的AI应用并没有那么难。有了Docker这样的工具,有了开源模型,每个人都可以成为AI技术的使用者和创造者。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐
所有评论(0)