@18612183942
2026-03-13T01:14:36.000000Z
字数 33078
阅读 897
| 文档版本 | 修订记录 | 修订人 | 修订时间 |
|---|---|---|---|
| v1.0 | 初始版本 | 杨金奎 | 2025-03-31 |
本文档描述小鲸需要的Ubuntu系统安装与配置,前后端部署。特别注意,以下脚本仅供参考,不同软件环境需要的脚本可能有所不同,其中空行分开的命令建议分开执行,没有空行分开的命令,建议整体执行。
官网https://cn.ubuntu.com/download/server/step1,下载ubuntu-24.04.2-live-server-amd64.iso,默认安装,存储空间建议设置50G以上,配置用户和密码。
也可以安装22.04系统,自行下载。
sudo apt autoremove open-vm-tools
sudo apt open-vm-tools
sudo apt open-vm-tools-desktop
sudo apt install ubuntu-desktop -y
先在vmware中,选择虚拟机,虚拟机设置,共享文件夹,总是启用,配置一个共享文件夹目录,建议名称为VD。然后再执行下面脚本,其中第二条命令可能报错文件夹已存在,忽略即可。重启虚拟机后,如果提示共享文件夹不存在,是目录没有正常挂载,只需执行第三条命令即可。通过cp命令可以实现共享文件夹和操作系统的双向复制。
vmware-hgfsclient
sudo mkdir /mnt/hgfs
sudo /usr/bin/vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other -o uid=1000 -o gid=1000 -o umask=022
wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y dotnet-sdk-8.0
dotnet --version
sudo apt install -y dotnet-runtime-8.0
dotnet --list-runtimes
通过以下脚本安装,安装成功后,在命令行输入code可以打开VSCode,用于连接SQL Server2022数据库。
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
sudo install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg
echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" | sudo tee /etc/apt/sources.list.d/vscode.list > /dev/null
sudo apt update
sudo apt install code
释放vscode监听进程,以下代码不用执行,研发参考
sudo ss -tulpn | grep :5253
sudo kill -9 5253
其中sudo /opt/mssql/bin/mssql-conf setup,本条命令配置SQLServer,选择版本、语言,设置管理员密码。systemctl status mssql-server --no-pager,本条命令查看SQLServer启动状态。
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | sudo gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg
curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc
curl -fsSL https://packages.microsoft.com/config/ubuntu/22.04/mssql-server-2022.list | sudo tee /etc/apt/sources.list.d/mssql-server-2022.list
wget http://mirrors.kernel.org/ubuntu/pool/main/o/openldap/libldap-2.5-0_2.5.11+dfsg-1~exp1ubuntu3_amd64.deb
sudo dpkg -i libldap-2.5-0_2.5.11+dfsg-1~exp1ubuntu3_amd64.deb
sudo apt-get update
sudo apt-get install -y mssql-server
sudo /opt/mssql/bin/mssql-conf setup
systemctl status mssql-server --no-pager
sudo ufw allow 1433/tcp
sudo ufw enable
sudo ufw status
以下命令启动SQL Server后,重启后开机默认会启动。
sudo systemctl start mssql-server
systemctl status mssql-server --no-pager
sudo systemctl stop mssql-server
击左侧侧边栏中的扩展图标(图标是四个方块堆叠的样子),在扩展视图的搜索框中输入 “SQL Server”,在搜索结果里会看到 “mssql” 插件,它是 Microsoft 官方提供的用于在 VS Code 中操作 SQL Server 数据库的插件。点击 “mssql” 插件旁边的 “安装” 按钮,稍等片刻,插件就会安装完成。安装完成后,“安装” 按钮会变为 “已安装”。
在VS Code中添加以下连接字符串,需要切换到通过字符串连接,证书不被信任的,需要增加TrustServerCertificate=True。连接后就可以执行sql语句了。
Data Source=.;Initial Catalog=master;User ID=sa;Password=Dakuinancymaomao@2022;TrustServerCertificate=True;
如果mssql下没有backup文件夹,先创建文件夹。然后在命令行执行以下脚本。
sudo cp /mnt/hgfs/VD/0321.bak /opt/mssql/backup/
sudo chown -R mssql:mssql /opt/mssql/data
在VS Code中执行以下脚本
RESTORE FILELISTONLY
FROM DISK = '/opt/mssql/backup/0321.bak'
以下代码在替换已有数据库时执行,初次导入的时候不需要执行。
-- 1. 声明变量存储要删除的数据库名
DECLARE @dbName NVARCHAR(128) = N'Zckx.Help';
-- 2. 关闭数据库的新连接(设置为单用户模式,自动断开现有连接)
ALTER DATABASE [Zckx.Help] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
/*
ROLLBACK IMMEDIATE:立即回滚所有未完成的事务,并断开所有现有连接
替代方案(如果想等待事务完成):WITH ROLLBACK AFTER 10 SECONDS(等待10秒后回滚)
*/
-- 3. 检查并删除数据库
IF EXISTS (SELECT name FROM sys.databases WHERE name = @dbName)
BEGIN
DROP DATABASE [Zckx.Help];
PRINT '数据库 ' + @dbName + ' 已成功删除';
END
ELSE
BEGIN
PRINT '数据库 ' + @dbName + ' 不存在';
END;
-- 4. 可选:恢复 SQL Server 的多用户模式(如果需要,这里是全局设置,一般不需要)
-- ALTER DATABASE [master] SET MULTI_USER;
还原数据库
RESTORE DATABASE [Zckx.Help]
FROM DISK = '/opt/mssql/backup/0122.bak'
WITH MOVE 'Zckx.Help' TO '/var/opt/mssql/data/Zckx.Help.mdf',
MOVE 'Zckx.Help_log' TO '/var/opt/mssql/data/Zckx.Help.ldf',
-- 可选:如果备份集有多个,指定还原哪个;如果数据库已存在,强制覆盖
REPLACE,
STATS = 10; -- 显示还原进度(每完成10%打印一次)
通过以下安装ollama及qwen3:4b模型,监听11434端口。
curl -fsSL https://ollama.com/install.sh | sh
ollama run qwen3:4b
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
chmod +x Miniconda3-latest-Linux-x86_64.sh
./Miniconda3-latest-Linux-x86_64.sh
source ~/.bashrc
conda --version
conda create -n vllm python=3.12 -y
conda activate vllm
sudo apt-get update -y
sudo apt-get install -y gcc-12 g++-12 libnuma-dev
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 10 --slave /usr/bin/g++ g++ /usr/bin/g++-12
git clone https://github.com/vllm-project/vllm.git vllm_source
cd vllm_source
pip install --upgrade pip
pip install "cmake>=3.26" wheel packaging ninja "setuptools-scm>=8" numpy
pip install -v -r requirements/cpu.txt --extra-index-url https://download.pytorch.org/whl/cpu
VLLM_TARGET_DEVICE=cpu python setup.py install
conda activate vllm
pip install modelscope
mkdir -p ~/models/DeepSeek-R1-Distill-Qwen-1.5B
cd ~/models/DeepSeek-R1-Distill-Qwen-1.5B
modelscope download --model deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B --local_dir ~/models/DeepSeek-R1-Distill-Qwen-1.5B
注意下面dakui是你的ubuntu系统用户名称。
vllm serve /home/dakui/models/DeepSeek-R1-Distill-Qwen-1.5B --tensor-parallel-size 1 --max-model-len 32768 --enforce-eager --port 8002
conda activate vllm
pip install modelscope
mkdir -p ~/models/Qwen2.5-VL-3B
cd ~/models/Qwen2.5-VL-3B
modelscope download --model Qwen/Qwen2.5-VL-3B-Instruct --local_dir ~/models/Qwen2.5-VL-3B
注意下面dakui是你的ubuntu系统用户名称。
vllm serve /home/dakui/models/Qwen2.5-VL-3B --tensor-parallel-size 1 --max-model-len 32768 --enforce-eager --port 8003
注意下面dakui是你的ubuntu系统用户名称。
sudo apt install curl
curl http://localhost:8000/v1/completions -H "Content-Type: application/json" -d '{
"model": "/home/dakui/models/DeepSeek-R1-Distill-Qwen-1.5B",
"prompt": ["<|begin▁of▁sentence|>北京好玩的地方<|end▁of▁sentence|>"],
"max_tokens": 1000,
"temperature": 0.6
}'
curl http://localhost:8000/v1/completions -H "Content-Type: application/json" -d '{
"model": "/home/dakui/models/Qwen2.5-VL-3B",
"prompt": ["<|begin▁of▁sentence|>北京好玩的地方<|end▁of▁sentence|>"],
"max_tokens": 1000,
"temperature": 0.6
}'
注意:pip show vllm 查看vllm版本需要不低于0.91版本,如果安装过之前的版本,需要执行下面命令删除虚拟环境后重做!
conda remove --name vllm2 --all
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
chmod +x Miniconda3-latest-Linux-x86_64.sh
./Miniconda3-latest-Linux-x86_64.sh
source ~/.bashrc
conda --version
conda create -n vllm2 python=3.12 -y
conda activate vllm2
conda activate vllm2
git clone https://github.com/vllm-project/vllm.git
cd vllm
VLLM_USE_PRECOMPILED=1 pip install --editable .
conda activate vllm2
pip install modelscope
mkdir -p ~/models/DeepSeek-R1-Distill-Qwen-1.5B
cd ~/models/DeepSeek-R1-Distill-Qwen-1.5B
modelscope download --model deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B --local_dir ~/models/DeepSeek-R1-Distill-Qwen-1.5B
注意下面dakui是你的ubuntu系统用户名称。
conda activate vllm2
vllm serve /home/dakui/models/DeepSeek-R1-Distill-Qwen-1.5B --tensor-parallel-size 1 --max-model-len 3000 --enforce-eager --port 8002
conda activate vllm2
pip install modelscope
mkdir -p ~/models/Qwen2.5-VL-3B
cd ~/models/Qwen2.5-VL-3B
modelscope download --model Qwen/Qwen2.5-VL-3B-Instruct --local_dir ~/models/Qwen2.5-VL-3B
注意下面dakui是你的ubuntu系统用户名称。
conda activate vllm2
vllm serve /home/dakui/models/Qwen2.5-VL-3B --tensor-parallel-size 1 --max-model-len 3000 --enforce-eager --port 8003
注意下面dakui是你的ubuntu系统用户名称。
sudo apt install curl
curl http://localhost:8000/v1/completions -H "Content-Type: application/json" -d '{
"model": "/home/dakui/models/DeepSeek-R1-Distill-Qwen-1.5B",
"prompt": ["<|begin▁of▁sentence|>北京好玩的地方<|end▁of▁sentence|>"],
"max_tokens": 1000,
"temperature": 0.6
}'
curl http://localhost:8000/v1/completions -H "Content-Type: application/json" -d '{
"model": "/home/dakui/models/Qwen2.5-VL-3B",
"prompt": ["<|begin▁of▁sentence|>北京好玩的地方<|end▁of▁sentence|>"],
"max_tokens": 1000,
"temperature": 0.6
}'
通过以下脚本安装,安装后默认不会开机启动,需要cd到程序目录,然后./start运行。
curl -fsSL https://cdn.anythingllm.com/latest/installer.sh | sh
cd ~/AnythingLLMDesktop
./start
安装后运行,如果运行报错,执行下面脚本,不报错不用执行。
cd ~/AnythingLLMDesktop/anythingllm-desktop/
sudo chown root:root chrome-sandbox
sudo chmod 4755 chrome-sandbox
如果安装的ollama,LLM偏好选择ollama。如果安装的vLLM,LLM偏好选择LocalAI,URL地址填写http://localhost:8000/v1,回车会自动识别模型(vLLM需要启动并加载模型)。
安装node.js,通过nvm和yarn方式。
# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
# in lieu of restarting the shell
\. "$HOME/.nvm/nvm.sh"
# Download and install Node.js:
nvm install 22
# Verify the Node.js version:
node -v # Should print "v22.15.0".
nvm current # Should print "v22.15.0".
# Download and install Yarn:
corepack enable yarn
# Verify Yarn version:
yarn -v
下载源码
git clone https://github.com/Mintplex-Labs/anything-llm.git
cd anything-llm
在项目根目录,建立.yarnrc文件,将下列内容拷贝到文件中。
registry "https://registry.npmmirror.com"
sass_binary_site "https://npmmirror.com/mirrors/node-sass/"
phantomjs_cdnurl "http://cnpmjs.org/downloads"
electron_mirror "https://npmmirror.com/mirrors/electron/"
sqlite3_binary_host_mirror "https://foxgis.oss-cn-shanghai.aliyuncs.com/"
profiler_binary_host_mirror "https://npmmirror.com/mirrors/node-inspector/"
chromedriver_cdnurl "https://npmmirror.com/mirrors/chromedriver/"
编译代码
cd anything-llm
yarn setup
启动前端、后台、文档处理3个站点。前端http://localhost:3000/,后台http://localhost:3001/api/docs/,文档处理http://localhost:8888/。
cd anything-llm/server
yarn start
cd anything-llm
yarn dev:frontend
cd anything-llm
yarn dev:collector
如果安装的ollama,LLM偏好选择ollama。如果安装的vLLM,LLM偏好选择LocalAI,URL地址填写http://localhost:8000/v1,回车会自动识别模型(vLLM需要启动并加载模型)。
searXNG是Anyhing LLM的联网搜索程序。首次运行python searx/webapp.py前,先修改searx文件夹中的settings.yml,查找secret_key,后面内容替换为afbf6622cdab7aa9f32e1dd7f8af73464d929a23c8d1d9dfd2bf6a66c1a6ef87。打开需要的搜索引擎,如baidu,找到对应的disabled: true,修改为disabled: false。同理,关闭不需要的搜索引擎,在下面增加disabled: true。找到 formats:
添加内容- json。修改port为8889。然后打开http://本机ip:8889/,网页正常打开可以搜索就安装好了。
sudo apt install -y git python3-pip python3-venv
git clone https://github.com/searxng/searxng.git
cd searxng
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
export PYTHONPATH=$PYTHONPATH:/home/dakui/searxng
python searx/webapp.py
sudo ufw allow 1433/tcp
sudo ufw enable
sudo ufw status
重新打开命令行,再次执行,需要执行下面代码。
cd searxng
source venv/bin/activate
export PYTHONPATH=$PYTHONPATH:/home/dakui/searxng
python searx/webapp.py
先参考前面描述安装conda,然后下载编译代码,注意源码有修改,需要替换。然后在项目文件夹下新建一个mp3_files文件夹用来保存文件。
sudo apt-get install ffmpeg
export HF_ENDPOINT=https://hf-mirror.com
export HF_HOME=https://hf-mirror.com
git clone https://github.com/2noise/ChatTTS
cd ChatTTS
conda create -n chattts
conda activate chattts
pip install -r requirements.txt
conda activate chattts
cd ChatTTS
python3 examples/web/webui.py
conda activate chattts
sudo pip install fastapi[all]
sudo pip install "uvicorn[standard]"
#conda activate chattts
#cd ChatTTS
#python3 examples/api/client.py
找到/ChatTTS/examples/api文件夹下的main.py文件,替换为以下代码。
import io
import os
import sys
import zipfile
import traceback
import uuid
from fastapi import Request
import numpy as np
import re
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
if sys.platform == "darwin":
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
now_dir = os.getcwd()
sys.path.append(now_dir)
from typing import Optional
import ChatTTS
from tools.audio import pcm_arr_to_mp3_view
from tools.logger import get_logger
import torch
from pydantic import BaseModel
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from tools.normalizer.en import normalizer_en_nemo_text
from tools.normalizer.zh import normalizer_zh_tn
from fastapi.staticfiles import StaticFiles
logger = get_logger("Command")
app = FastAPI()
app.mount("/mp3_files", StaticFiles(directory="mp3_files"), name="mp3_files")
# 配置 CORS 中间件
origins = [
"*", # 允许所有来源,在生产环境中建议指定具体的域名列表
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"], # 允许所有请求方法
allow_headers=["*"], # 允许所有请求头
)
@app.on_event("startup")
async def startup_event():
global chat
chat = ChatTTS.Chat(get_logger("ChatTTS"))
chat.normalizer.register("en", normalizer_en_nemo_text())
chat.normalizer.register("zh", normalizer_zh_tn())
logger.info("Initializing ChatTTS...")
if chat.load(source="local"):
logger.info("Models loaded successfully.")
else:
logger.error("Models load failed.")
sys.exit(1)
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc: RequestValidationError):
logger.error(f"Validation error: {exc.errors()}")
return JSONResponse(status_code=422, content={"detail": exc.errors()})
class ChatTTSParams(BaseModel):
text: list[str]
stream: bool = False
lang: Optional[str] = None
skip_refine_text: bool = False
refine_text_only: bool = False
use_decoder: bool = True
do_text_normalization: bool = True
do_homophone_replacement: bool = False
params_refine_text: ChatTTS.Chat.RefineTextParams = None
params_infer_code: ChatTTS.Chat.InferCodeParams
MP3_DIR = "mp3_files"
if not os.path.exists(MP3_DIR):
os.makedirs(MP3_DIR)
def save_mp3_file(wav, file_name):
data = pcm_arr_to_mp3_view(wav)
file_path = os.path.join(MP3_DIR, file_name)
with open(file_path, "wb") as f:
f.write(data)
logger.info(f"Audio saved to {file_name}")
@app.post("/generate_voice")
async def generate_voice(request: Request, params: ChatTTSParams):
try:
logger.info("Text input: %s", str(params.text))
# audio seed
if params.params_infer_code.manual_seed is not None:
current_dir = os.getcwd()
file_path = os.path.join(current_dir, 'asset', f'seed_{params.params_infer_code.manual_seed}_restored_emb.pt')
params.params_infer_code.spk_emb = torch.load(file_path, map_location=torch.device('cuda'))
# text seed for text refining
if params.params_refine_text:
text = chat.infer(
text=params.text, skip_refine_text=False, refine_text_only=True
)
logger.info(f"Refined text: {text}")
else:
# no text refining
text = params.text
logger.info("Use speaker:")
logger.info(params.params_infer_code.spk_emb)
logger.info("Start voice inference.")
wavs = chat.infer(
text=text,
stream=params.stream,
lang=params.lang,
skip_refine_text=params.skip_refine_text,
use_decoder=params.use_decoder,
do_text_normalization=params.do_text_normalization,
do_homophone_replacement=params.do_homophone_replacement,
params_infer_code=params.params_infer_code,
params_refine_text=params.params_refine_text,
)
logger.info("Inference completed.")
mp3_urls = []
for idx, wav in enumerate(wavs):
file_name = f"{uuid.uuid4()}.mp3"
save_mp3_file(wav, file_name)
base_url = str(request.base_url) + "mp3_files/"
mp3_url = base_url + file_name
mp3_urls.append(mp3_url)
return JSONResponse({"mp3_urls": mp3_urls})
except Exception as e:
logger.info(f"An error occurred: {e}")
print("Stack trace:")
traceback.print_exc()
# 这里可以根据具体需求添加更多的异常处理逻辑,比如返回错误响应等
return JSONResponse({"error": str(e)}, status_code=500)
# 定义用于接收请求体数据的模型
class TTSRequestParams(BaseModel):
text: str
seed: int
streaming: bool
async def process_streaming(text, seed, request):
params_refine_text = {
"prompt": "[oral_0][laugh_0][break_0]",
"top_P": 0.7,
"top_K": 20,
"temperature": 0.03,
"repetition_penalty": 1,
"max_new_token": 384,
"min_new_token": 0,
"show_tqdm": True,
"ensure_non_empty": True,
"stream_batch": 24,
}
params_infer_code = {
"prompt": "[oral_0][laugh_0][break_0][speed_5]",
"top_P": 0.1,
"top_K": 20,
"temperature": 0.03,
"repetition_penalty": 1.05,
"max_new_token": 2048,
"min_new_token": 0,
"show_tqdm": True,
"ensure_non_empty": True,
"stream_batch": True,
"spk_emb": None,
}
text = '\n'.join([line for line in text.split('\n') if line.strip()])
sentences = re.split(r'[。!?::]', text)
sentences = [s.strip() for s in sentences if s.strip()]
for sentence in sentences:
text_list = [sentence]
params_chat_tts = ChatTTSParams(
text=text_list,
stream=False,
lang=None,
skip_refine_text=True,
use_decoder=True,
do_text_normalization=True,
do_homophone_replacement=True,
params_infer_code=params_infer_code,
params_refine_text=params_refine_text
)
# audio seed
if seed is not None:
current_dir = os.getcwd()
file_path = os.path.join(current_dir, 'asset', f'seed_{seed}_restored_emb.pt')
params_chat_tts.params_infer_code.manual_seed = seed
params_chat_tts.params_infer_code.spk_emb = torch.load(file_path, map_location=torch.device('cuda'))
# text seed for text refining
if params_chat_tts.params_refine_text and not params_chat_tts.skip_refine_text:
refined_text = chat.infer(
text=params_chat_tts.text, skip_refine_text=False, refine_text_only=True
)
logger.info(f"Refined text: {refined_text}")
else:
refined_text = params_chat_tts.text
logger.info("Use speaker:")
logger.info(params_chat_tts.params_infer_code.spk_emb)
logger.info("Start voice inference.")
wavs = chat.infer(
text=refined_text,
stream=params_chat_tts.stream,
lang=params_chat_tts.lang,
skip_refine_text=params_chat_tts.skip_refine_text,
use_decoder=params_chat_tts.use_decoder,
do_text_normalization=params_chat_tts.do_text_normalization,
do_homophone_replacement=params_chat_tts.do_homophone_replacement,
params_infer_code=params_chat_tts.params_infer_code,
params_refine_text=params_chat_tts.params_refine_text,
)
logger.info("Inference completed.")
for idx, wav in enumerate(wavs):
file_name = f"{uuid.uuid4()}.mp3"
file_content = pcm_arr_to_mp3_view(wav)
headers = {
"Content-Disposition": f"attachment; filename={file_name}"
}
yield StreamingResponse(iter([file_content]), media_type="audio/mpeg", headers=headers)
async def process_non_streaming(text, seed, request):
params_refine_text = {
"prompt": "[oral_0][laugh_0][break_0]",
"top_P": 0.7,
"top_K": 20,
"temperature": 0.03,
"repetition_penalty": 1,
"max_new_token": 384,
"min_new_token": 0,
"show_tqdm": True,
"ensure_non_empty": True,
"stream_batch": 24,
}
params_infer_code = {
"prompt": "[oral_0][laugh_0][break_0][speed_5]",
"top_P": 0.1,
"top_K": 20,
"temperature": 0.03,
"repetition_penalty": 1.05,
"max_new_token": 2048,
"min_new_token": 0,
"show_tqdm": True,
"ensure_non_empty": True,
"stream_batch": True,
"spk_emb": None,
}
text = '\n'.join([line for line in text.split('\n') if line.strip()])
sentences = re.split(r'[。!?::]', text)
sentences = [s.strip() for s in sentences if s.strip()]
wavList = []
for sentence in sentences:
text_list = [sentence]
params_chat_tts = ChatTTSParams(
text=text_list,
stream=False,
lang=None,
skip_refine_text=True,
use_decoder=True,
do_text_normalization=True,
do_homophone_replacement=True,
params_infer_code=params_infer_code,
params_refine_text=params_refine_text
)
# audio seed
if seed is not None:
current_dir = os.getcwd()
file_path = os.path.join(current_dir, 'asset', f'seed_{seed}_restored_emb.pt')
params_chat_tts.params_infer_code.manual_seed = seed
params_chat_tts.params_infer_code.spk_emb = torch.load(file_path, map_location=torch.device('cuda'))
# text seed for text refining
if params_chat_tts.params_refine_text and not params_chat_tts.skip_refine_text:
refined_text = chat.infer(
text=params_chat_tts.text, skip_refine_text=False, refine_text_only=True
)
logger.info(f"Refined text: {refined_text}")
else:
refined_text = params_chat_tts.text
logger.info("Use speaker:")
logger.info(params_chat_tts.params_infer_code.spk_emb)
logger.info("Start voice inference.")
wavs = chat.infer(
text=refined_text,
stream=params_chat_tts.stream,
lang=params_chat_tts.lang,
skip_refine_text=params_chat_tts.skip_refine_text,
use_decoder=params_chat_tts.use_decoder,
do_text_normalization=params_chat_tts.do_text_normalization,
do_homophone_replacement=params_chat_tts.do_homophone_replacement,
params_infer_code=params_chat_tts.params_infer_code,
params_refine_text=params_chat_tts.params_refine_text,
)
logger.info("Inference completed.")
for idx, wav in enumerate(wavs):
wavList.append(wav)
file_content = memoryview(b'')
for idx, wav in enumerate(wavList):
file_content = memoryview(bytes(file_content) + bytes(pcm_arr_to_mp3_view(wav)))
file_name = f"{uuid.uuid4()}.mp3"
file_path = os.path.join(MP3_DIR, file_name)
with open(file_path, 'wb') as f:
f.write(file_content)
base_url = str(request.base_url).replace("/tts", "") + "mp3_files/"
mp3_url = base_url + file_name
return JSONResponse({"mp3_url": mp3_url})
@app.post("/tts")
async def tts(request: Request, params: TTSRequestParams):
try:
text = params.text
seed = params.seed
streaming = params.streaming
logger.info("Text input: %s", str(text))
if streaming:
async def stream_generator():
async for response in process_streaming(text, seed, request):
if isinstance(response, StreamingResponse):
# 遍历 response.body_iterator 并 yield 每个元素
async for part in response.body_iterator:
yield part
else:
yield response
return StreamingResponse(stream_generator(), media_type="audio/mpeg")
else:
return await process_non_streaming(text, seed, request)
except Exception as e:
logger.info(f"An error occurred: {e}")
print("Stack trace:")
traceback.print_exc()
return JSONResponse({"error": str(e)}, status_code=500)
@app.get("/tts_get")
async def tts_get(request: Request, text: str, seed: int, streaming: bool):
try:
logger.info("Text input: %s", str(text))
if streaming:
async def stream_generator():
async for response in process_streaming(text, seed, request):
if isinstance(response, StreamingResponse):
# 遍历 response.body_iterator 并 yield 每个元素
async for part in response.body_iterator:
yield part
else:
yield response
return StreamingResponse(stream_generator(), media_type="audio/mpeg")
else:
return await process_non_streaming(text, seed, request)
except Exception as e:
logger.info(f"An error occurred: {e}")
print("Stack trace:")
traceback.print_exc()
return JSONResponse({"error": str(e)}, status_code=500)
启动ChatTTS
conda activate chattts
cd ChatTTS
fastapi dev examples/api/main.py --host 0.0.0.0 --port 8001
在根目录,新建get.html文件,拷贝以下代码到文件中。双击文件,可以测试chatTTS服务。其中的语音文件找同事要。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小鲸 TTS Get接口</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
max-width: 600px;
margin: auto;
}
label,
button {
display: block;
margin-top: 10px;
}
textarea,
select,
input[type="number"],
input[type="text"] {
width: calc(100% - 10px);
margin-top: 5px;
padding: 5px;
font-size: 16px;
}
button {
width: 100%;
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
font-size: 16px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
h2,
pre {
margin-top: 20px;
}
audio {
width: 100%;
margin-top: 10px;
}
</style>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body>
<h1>小鲸ChatTTS API</h1>
<label for="api_url">接口 URL:</label>
<input type="text" id="api_url" value="http://localhost:8001/tts_get">
<label for="text">文本:</label>
<textarea id="text" rows="10">颐和园坐落于北京西郊海淀区,是一座举世闻名的大型皇家园林。它始建于清朝乾隆十五年,经修复后改名颐和园。这座园林以万寿山和昆明湖为主体,将自然山水与人工建筑融为一体,水面约占全园面积的四分之三,湖光山色,美不胜收。园内建筑风格多样,有宏伟壮观的佛香阁,它高高矗立在万寿山前山的高台上,是颐和园的标志性建筑,登阁可将昆明湖及周边景色尽收眼底;还有雕梁画栋的长廊,全长 728 米,枋梁上绘有 14000 余幅彩画,色彩绚丽,内容丰富,令人赞叹不已。十七孔桥横卧在昆明湖上,如一道长虹,桥身由 17 个孔组成,造型优美,与南湖岛相连,极具观赏性。后山后湖景区的苏州街则充满江南水乡风情,两岸店铺林立,仿佛让人置身于江南古镇。颐和园不仅是一座精美的园林,更集传统造园艺术之大成,体现了 “虽由人作,宛自天开” 的造园准则
</textarea>
<label for="seed">语音编号:</label>
<input type="number" id="seed" value="1910">
<label for="streaming">流式输出:</label>
<input type="checkbox" id="streaming" checked>
<button onclick="sendRequest()">发送请求</button>
<h2>输出</h2>
<pre id="output"></pre>
<div id="audio-container"></div> <!-- 用于显示音频播放器 -->
<script>
function sendRequest() {
const apiUrl = $('#api_url').val();
const text = $('#text').val();
const seed = parseInt($('#seed').val());
const streaming = $('#streaming').is(':checked');
const output = $('#output');
output.text("请求中...\n");
// 构建带有查询参数的 URL
const url = `${apiUrl}?text=${encodeURIComponent(text)}&seed=${seed}&streaming=${streaming}`;
console.log('请求 URL:', url);
// 禁用按钮防止重复点击
$('button').prop('disabled', true);
if (streaming) {
const audioElement = $('<audio>', {
controls: true,
src: url
});
audioElement.on('error', function () {
console.error('音频播放错误:', this.error);
});
const audioContainer = $('#audio-container');
audioContainer.empty();
audioContainer.append(audioElement);
output.append("音频即将播放...\n");
audioElement[0].play();
} else {
$.ajax({
url: url,
method: 'GET',
xhrFields: {
responseType: 'text'
},
success: function (response) {
if (typeof response ==='string') {
try {
response = JSON.parse(response);
} catch (error) {
output.append(`解析响应数据时出错: ${error.message}\n`);
console.error('解析响应数据时出错:', error);
// 请求失败后启用按钮
$('button').prop('disabled', false);
return;
}
}
if (response.error) {
output.append(`错误: ${response.error}\n`);
} else if (response.mp3_url) {
const audioContainer = $('#audio-container');
audioContainer.empty(); // 清空之前的音频播放器
const audioElement = $('<audio>', {
controls: true,
src: response.mp3_url
});
audioContainer.append(audioElement);
output.append("音频即将播放...\n");
audioElement[0].play();
} else {
output.append("未获取到有效的 MP3 URL\n");
}
// 请求成功后启用按钮
$('button').prop('disabled', false);
},
error: function (jqXHR, textStatus, errorThrown) {
output.append(`请求失败,状态码: ${jqXHR.status}\n`);
console.error('请求错误:', textStatus, errorThrown);
console.error('响应头:', jqXHR.getAllResponseHeaders());
console.error('响应文本:', jqXHR.responseText);
// 请求失败后启用按钮
$('button').prop('disabled', false);
}
});
}
}
</script>
</body>
</html>
安装docker
sudo apt update
sudo apt install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
配置镜像源
sudo nano /etc/docker/daemon.json
粘贴以下内容到上面文件,ctrl+o回车,再ctr+x退出。
{
"registry-mirrors": [
"https://docker.1panelproxy.com",
"https://2a6bf1988cb6428c877f723ec7530dbc.mirror.swr.myhuaweicloud.com",
"https://docker.m.daocloud.io",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com",
"https://your_preferred_mirror",
"https://dockerhub.icu",
"https://docker.registry.cyou",
"https://docker-cf.registry.cyou",
"https://dockercf.jsdelivr.fyi",
"https://docker.jsdelivr.fyi",
"https://dockertest.jsdelivr.fyi",
"https://mirror.aliyuncs.com",
"https://dockerproxy.com",
"https://mirror.baidubce.com",
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
"https://docker.mirrors.sjtug.sjtu.edu.cn",
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.iscas.ac.cn",
"https://docker.rainbond.cc"
]
}
重启docker
sudo systemctl daemon-reload
sudo systemctl restart docker
依赖docker,务必参考安装docker章节先安装docker。
sudo apt-get update
sudo apt-get install unixodbc-dev
pip install uv
pip install pre-commit
git clone https://github.com/infiniflow/ragflow.git
cd ragflow/
uv sync --python 3.10 --all-extras
下面的命令可能会报错,一般不用处理,继续就行。
uv run download_deps.py
pre-commit install
新建一个download.py文件,拷贝以下代码保存。
import nltk
nltk.download('wordnet')
nltk.download('punkt_tab')
运行上面创建的文件下载wordnet和punkt_tab。
uv run download.py
下载三方软件,时间很长。
sudo docker compose -f docker/docker-compose-base.yml up -d
修改etc/hosts文件
sudo nano /etc/hosts
粘贴以下内容到上面文件,ctrl+o回车,再ctr+x退出。
127.0.0.1 es01 infinity mysql minio redis
docker文件夹下的.env文件,看不见按ctrl+H键,第120行取消注释。
HF_ENDPOINT=https://hf-mirror.com
编译ragflow前端
cd ragflow/web
npm install
vim .umirc.ts #Update proxy.target in .umirc.ts to http://127.0.0.1:9380
启动ragflow前端
cd ragflow/web
npm run dev
启动ragflow后台
cd ragflow
docker compose -f docker/docker-compose-base.yml up -d
export HF_ENDPOINT=https://hf-mirror.com
export OPENAI_API_KEY="local-rag-key"
source .venv/bin/activate
export PYTHONPATH=$(pwd)
#python api/ragflow_server.py
bash docker/launch_backend_service.sh
启动ragflow mcp,其中api_key根据ragflow中分配的替换。
cd ragflow
uv run mcp/server/server.py --host=127.0.0.1 --port=9382 --base_url=http://127.0.0.1:9380 --mode=self-host --api_key=ragflow-JkNzBhNmE4MmZlMzExZjA4NTM4ZDdkMT
添加vllm模型,打开前端http://localhost:9222,注册账号登录。务必使用谷歌浏览器,其他的可能不兼容。点击右上方头像,选择左侧的模型供应商。点击下面的vllm添加模型,模型类型选择chat,模型名称填写模型下载的路径,如/home/dakui/models/Qwen2.5-VL-3B,基础url填写vllm监听的地址,如http://localhost:8002/v1,注意v1后面不能有/。最大token数填写4096,API-KEY不用填写。ragflow,选择上方的聊天菜单,新建助理,模型设置选择刚才配置的模型。配置好助理后,选择助理聊天右边的+号,开始对话。
ragflow调用阿里百炼在线模型。点击右上方头像,选择左侧的模型供应商。点击OpenAI-API-Compatible添加模型,模型类型选择chat,模型名称根据测试的实际模型填写,例如qwen-plus-latest,基础url填写https://dashscope.aliyuncs.com/compatible-mode/v1,最大token数填写4096,API-KEY填写申请的key,例如sk-**。ragflow,选择上方的聊天菜单,新建助理,模型设置选择刚才配置的模型。配置好助理后,选择助理聊天右边的+号,开始对话。
ragflow切换向量数据库为infinity,注意下面命令会清空ragflow所有数据。
cd ragflow
docker compose -f docker/docker-compose-base.yml down -v
设置 docker/.env 目录中的 DOC_ENGINE 为 infinity,如果看不到.env文件,按ctrl+H组合键显示后修改。
启动后台创建账号登录。
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt update
sudo apt install -y libxss1 libappindicator1 libindicator7
sudo dpkg -i google-chrome-stable_current_amd64.deb
进入官网https://www.cursor.com/cn,点击下载linux,下载文件拷贝到home下的cursor目录。
cd cursor
./Cursor-0.50.5-x86_64.AppImage
下载源码并编译
git clone --recursive https://github.com/FunAudioLLM/CosyVoice.git
cd CosyVoice
git submodule update --init --recursive
conda create -n cosyvoice -y python=3.10
conda activate cosyvoice
conda install -y -c conda-forge pynini==2.1.5
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host=mirrors.aliyun.com
pip install ffmpeg-python
pip install lameenc
pip install uvloop
pip install wetext
检查 CUDA 版本,cuda版本需要和显卡驱动版本匹配。
nvcc --version # 应显示 12.1
下载模型文件,以下内容保存到download.py中。然后在命令行执行python download.py即可看到下载模型的进度。
from modelscope import snapshot_download
snapshot_download('iic/CosyVoice2-0.5B', local_dir='pretrained_models/CosyVoice2-0.5B')
启动demo
cd CosyVoice
conda activate cosyvoice
python3 webui.py --port 50000 --model_dir pretrained_models/CosyVoice2-0.5B
启动server
cd CosyVoice
conda activate cosyvoice
python3 api.py
git clone --recursive https://github.com/FunAudioLLM/CosyVoice.git
conda create -n cosyvoice3 -y python=3.10
conda activate cosyvoice3
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host=mirrors.aliyun.com
conda activate cosyvoice3
pip uninstall -y ruamel.yaml
pip install ruamel.yaml==0.17.21
pip install ffmpeg-python
pip install lameenc
pip install uvloop
pip install wetext
下载模型文件,以下内容保存到download.py中。然后在命令行执行python download.py即可看到下载模型的进度。
from modelscope import snapshot_download
snapshot_download('FunAudioLLM/Fun-CosyVoice3-0.5B-2512',local_dir='pretrained_models/Fun-CosyVoice3-0.5B')
启动demo
cd CosyVoice3
conda activate cosyvoice3
python3 webui.py --port 50000 --model_dir pretrained_models/Fun-CosyVoice3-0.5B
启动server
cd CosyVoice3
conda activate cosyvoice3
python3 api.py
下载源码并编译
git clone https://github.com/FunAudioLLM/SenseVoice.git
cd SenseVoice
# 创建虚拟环境
python3 -m venv sensevoice-venv
# 激活虚拟环境
source sensevoice-venv/bin/activate
# 安装项目基础依赖
pip install -r requirements.txt
# 安装 fastapi 完整版本
pip install "fastapi[standard]"
# 启动服务
cd SenseVoice
source sensevoice-venv/bin/activate
export SENSEVOICE_DEVICE=cuda:0
fastapi run --port 50000
#启动webui
cd SenseVoice
source sensevoice-venv/bin/activate
export SENSEVOICE_DEVICE=cuda:0
python webui.py
# 配置API_KEY
echo "export DASHSCOPE_API_KEY='YOUR_DASHSCOPE_API_KEY'" >> ~/.bashrc
source ~/.bashrc
# 查看配置的API_KEY(重新打开一个终端窗口)
echo $DASHSCOPE_API_KEY
# 安装DashScope Python SDK
cd ragflow
source .venv/bin/activate
pip install -U dashscope
# 安装pyaudio(需要激活到source .venv/bin/activate虚拟环境中安装)
sudo apt-get update
sudo apt-get install portaudio19-dev
wget https://bootstrap.pypa.io/get-pip.py
~/ragflow/.venv/bin/python3 get-pip.py
~/ragflow/.venv/bin/python3 -m pip install pyaudio
#编译文件
cd ragflow
source .venv/bin/activate
#创建虚拟环境
conda create -n embeddings python=3.10 -y
conda activate embeddings
#下载模型
conda activate embeddings
python /home/dakui/Embeddings/download_model.py
#安装依赖
cd /home/dakui/Embeddings/code
conda activate embeddings
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
#运行服务
conda activate embeddings
cd /home/dakui/Embeddings/code
python embedding_server.py
cd到程序根目录,使用dotnet publish编译。
cd C:\Jinkui\Work\代码\XiaojingApp\XiaoJing.App
npm run build
cd到程序根目录,使用dotnet publish编译。
cd C:\Jinkui\Work\代码\XiaojingService\XiaoJing.Service
dotnet publish -c Release -r linux-x64 --self-contained true
安装.net运行时
# 添加 Microsoft 包签名密钥
wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
# 更新包索引
sudo apt update
# 安装 .NET Core 运行时
sudo apt install aspnetcore-runtime-8.0
#sudo apt install dotnet-runtime-8.0=8.0.14-1
在opt文件夹,创建XiaoJing.App目录。需要sudo权限,没有的话在路径栏输入admin:///,然后会提示输入管理员密码,刷新后就有权限创建了。
sudo cp /mnt/hgfs/VD/app.zip /opt
解压app.zip,然后将文件拷贝到XiaoJing.App目录。通过下面命令,发布到指定的ip和端口,注意根据实际情况修改。配置文件修改,注意需要修改XiaoJing.App.dll.config,而不是app.config!
sudo ufw allow 5001
cd /opt/XiaoJing.App
dotnet XiaoJing.App.dll --urls "http://192.168.1.13:5001;"
在opt文件夹,创建XiaoJing.Service目录。需要sudo权限,没有的话在路径栏输入admin:///,然后会提示输入管理员密码,刷新后就有权限创建了。
sudo cp /mnt/hgfs/VD/service.zip /opt
解压app.zip,然后将文件拷贝到XiaoJing.Service目录。通过下面命令,发布到指定的ip和端口,注意根据实际情况修改。配置文件修改,注意需要修改XiaoJing.Service.dll.config,而不是app.config!
sudo ufw allow 5000
cd /opt/XiaoJing.Service
dotnet XiaoJing.Service.dll --urls "http://192.168.1.13:5000;"
增量发布,只需替换文件到对应程序发布目录。示例脚本如下。
sudo cp /mnt/hgfs/VD/XiaoJing.Service.dll /opt/XiaoJing.Service
sudo cp /mnt/hgfs/VD/XiaoJing.Service.pdb /opt/XiaoJing.Service
#!/bin/bash
# 启动 anything-llm服务
cd ~/anything-llm/server
yarn start &
# 返回 anything-llm根目录
cd ../
# 启动anything-llm前端
yarn dev:frontend &
# 启动anything-llm文档处理
yarn dev:collector &
# 进入 /opt/XiaoJing.Service 目录
cd /opt/XiaoJing.Service
# 启动小鲸服务
dotnet XiaoJing.Service.dll --urls "http://192.168.1.13:5000;" &
# 进入 /opt/XiaoJing.App 目录
cd /opt/XiaoJing.App
# 启动小鲸前端
dotnet XiaoJing.App.dll --urls "http://192.168.1.13:5001;" &
# 进入 home 目录下的 searxng 目录
cd ~/searxng
# 激活虚拟环境
source venv/bin/activate
# 设置 PYTHONPATH 环境变量
export PYTHONPATH=$PYTHONPATH:/home/dakui/searxng
# 启动 searxng
python searx/webapp.py &
deactivate
# 运行 vllm 服务,启动模型
vllm serve /home/dakui/models/Qwen2.5-VL-3B --tensor-parallel-size 1 --max-model-len 32768 --enforce-eager --port 8003 &
vllm serve /home/dakui/models/DeepSeek-R1-Distill-Qwen-1.5B --tensor-parallel-size 1 --max-model-len 32768 --enforce-eager --port 8002 &
# 初始化 conda
eval "$(conda shell.bash hook)"
# 启动 chattts
conda activate chattts
cd ~/ChatTTS
fastapi dev examples/api/main.py --host 0.0.0.0 --port 8001
conda deactivate
# 等待所有后台进程完成
wait
将以上脚本保存为home目录下run_xiaojing.sh,执行下面命令赋予脚本权限。
chmod +x run_xiaojing.sh
在脚本所在目录,运行脚本。
./run_xiaojing.sh