feat(M2.1): 设备端部署脚本 + whisper LD_LIBRARY_PATH + dog/cat 角色人设配置
This commit is contained in:
@@ -1159,5 +1159,14 @@
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 5000
|
"port": 5000
|
||||||
|
},
|
||||||
|
"character": {
|
||||||
|
"name": "小喵",
|
||||||
|
"character_type": "pet",
|
||||||
|
"persona_prompt": "你是一只慵懒傲娇的猫咪,名叫小喵。用简短慵懒的口吻回答,偶尔带'喵~',每次回复不超过两句话。"
|
||||||
|
},
|
||||||
|
"ai": {
|
||||||
|
"backend": "local",
|
||||||
|
"whisper_lib_dir": "/home/showen/ai_spike/whisper.cpp/build/bin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1171,5 +1171,14 @@
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 5000
|
"port": 5000
|
||||||
|
},
|
||||||
|
"character": {
|
||||||
|
"name": "小汪",
|
||||||
|
"character_type": "pet",
|
||||||
|
"persona_prompt": "你是一只活泼的小狗,名叫小汪。用简短俏皮的口吻回答,以'汪!'开头,每次回复不超过两句话。"
|
||||||
|
},
|
||||||
|
"ai": {
|
||||||
|
"backend": "local",
|
||||||
|
"whisper_lib_dir": "/home/showen/ai_spike/whisper.cpp/build/bin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
87
scripts/deploy_ai.sh
Normal file
87
scripts/deploy_ai.sh
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# M2.1 设备端部署脚本 — 部署 AI 推理二进制 + 模型文件
|
||||||
|
# 用法: bash scripts/deploy_ai.sh
|
||||||
|
# 前置: ~/ai_spike/ 存在 (spike 2026-07-03 保留)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
SPIKE_DIR="$HOME/ai_spike"
|
||||||
|
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
TOOLS_DIR="$PROJECT_DIR/tools"
|
||||||
|
MODEL_STORE="$PROJECT_DIR/model_store"
|
||||||
|
|
||||||
|
echo "=== M2.1 AI 部署 ==="
|
||||||
|
echo "项目: $PROJECT_DIR"
|
||||||
|
echo "spike: $SPIKE_DIR"
|
||||||
|
|
||||||
|
# 检查 spike 目录
|
||||||
|
if [ ! -d "$SPIKE_DIR" ]; then
|
||||||
|
echo "错误: spike 目录不存在 ($SPIKE_DIR)"
|
||||||
|
echo "请先完成 spike (参考 TEAM_CHAT.md 2026-07-03 张明远报告)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. 部署二进制到 tools/
|
||||||
|
echo "--- 部署二进制到 tools/ ---"
|
||||||
|
mkdir -p "$TOOLS_DIR"
|
||||||
|
|
||||||
|
# whisper-cli (依赖 libggml*.so,需设 LD_LIBRARY_PATH 或 RPATH)
|
||||||
|
ln -sf "$SPIKE_DIR/whisper.cpp/build/bin/whisper-cli" "$TOOLS_DIR/whisper-cli"
|
||||||
|
echo " ✓ whisper-cli"
|
||||||
|
|
||||||
|
# llama-cli
|
||||||
|
ln -sf "$SPIKE_DIR/llama.cpp/build/bin/llama-cli" "$TOOLS_DIR/llama-cli"
|
||||||
|
echo " ✓ llama-cli"
|
||||||
|
|
||||||
|
# piper
|
||||||
|
ln -sf "$SPIKE_DIR/piper/piper" "$TOOLS_DIR/piper"
|
||||||
|
echo " ✓ piper"
|
||||||
|
|
||||||
|
# 2. 部署模型到 model_store/
|
||||||
|
echo "--- 部署模型到 model_store/ ---"
|
||||||
|
mkdir -p "$MODEL_STORE/llm" "$MODEL_STORE/asr" "$MODEL_STORE/tts"
|
||||||
|
|
||||||
|
# LLM: Qwen2.5-0.5B (默认推荐)
|
||||||
|
ln -sf "$SPIKE_DIR/models/qwen2.5-0.5b-instruct-q4_k_m.gguf" "$MODEL_STORE/llm/qwen2.5-0.5b-q4_k_m.gguf"
|
||||||
|
echo " ✓ llm/qwen2.5-0.5b-q4_k_m.gguf (469M, 推荐默认)"
|
||||||
|
|
||||||
|
# LLM: Gemma3-1B (可选档)
|
||||||
|
ln -sf "$SPIKE_DIR/models/gemma-3-1b-it-Q4_K_M.gguf" "$MODEL_STORE/llm/gemma3-1b-q4_k_m.gguf"
|
||||||
|
echo " ✓ llm/gemma3-1b-q4_k_m.gguf (769M, 可选档)"
|
||||||
|
|
||||||
|
# ASR: whisper tiny
|
||||||
|
ln -sf "$SPIKE_DIR/models/ggml-tiny.bin" "$MODEL_STORE/asr/ggml-tiny.bin"
|
||||||
|
echo " ✓ asr/ggml-tiny.bin (75M)"
|
||||||
|
|
||||||
|
# ASR: whisper base (备选)
|
||||||
|
ln -sf "$SPIKE_DIR/models/ggml-base.bin" "$MODEL_STORE/asr/ggml-base.bin"
|
||||||
|
echo " ✓ asr/ggml-base.bin (142M, 备选)"
|
||||||
|
|
||||||
|
# TTS: piper zh_CN-huayan-medium
|
||||||
|
ln -sf "$SPIKE_DIR/piper/zh_CN-huayan-medium.onnx" "$MODEL_STORE/tts/zh_CN-huayan-medium.onnx"
|
||||||
|
ln -sf "$SPIKE_DIR/piper/zh_CN-huayan-medium.onnx.json" "$MODEL_STORE/tts/zh_CN-huayan-medium.onnx.json"
|
||||||
|
echo " ✓ tts/zh_CN-huayan-medium.onnx (63M)"
|
||||||
|
|
||||||
|
# 3. 设置 whisper LD_LIBRARY_PATH (whisper-cli 依赖 libggml*.so)
|
||||||
|
WHISPER_LIB="$SPIKE_DIR/whisper.cpp/build/bin"
|
||||||
|
echo "--- whisper 依赖库路径: $WHISPER_LIB ---"
|
||||||
|
|
||||||
|
# 4. 验证
|
||||||
|
echo "=== 验证 ==="
|
||||||
|
echo "tools/:"
|
||||||
|
ls -la "$TOOLS_DIR"
|
||||||
|
echo "model_store/:"
|
||||||
|
ls -laR "$MODEL_STORE"
|
||||||
|
|
||||||
|
# 测试 llama-cli 能跑
|
||||||
|
echo "--- 测试 llama-cli ---"
|
||||||
|
"$TOOLS_DIR/llama-cli" --version 2>&1 | head -1 || echo " (llama-cli 版本检查失败, 可能仍可用)"
|
||||||
|
|
||||||
|
# 测试 piper 能跑
|
||||||
|
echo "--- 测试 piper ---"
|
||||||
|
"$TOOLS_DIR/piper" --help 2>&1 | head -1 || echo " (piper 帮助检查失败, 可能仍可用)"
|
||||||
|
|
||||||
|
echo "=== 部署完成 ==="
|
||||||
|
echo ""
|
||||||
|
echo "注意: whisper-cli 运行时需要 LD_LIBRARY_PATH 包含:"
|
||||||
|
echo " $WHISPER_LIB"
|
||||||
|
echo "建议在 systemd service 或启动脚本中设置此环境变量"
|
||||||
@@ -381,6 +381,9 @@ pub struct AiConfig {
|
|||||||
/// 工具目录 (whisper-cli/llama-cli/piper 二进制所在)
|
/// 工具目录 (whisper-cli/llama-cli/piper 二进制所在)
|
||||||
#[serde(default = "default_tools_dir")]
|
#[serde(default = "default_tools_dir")]
|
||||||
pub tools_dir: PathBuf,
|
pub tools_dir: PathBuf,
|
||||||
|
/// whisper-cli 依赖库路径 (libggml*.so 所在,部署时标定)
|
||||||
|
#[serde(default)]
|
||||||
|
pub whisper_lib_dir: Option<PathBuf>,
|
||||||
/// 临时文件目录
|
/// 临时文件目录
|
||||||
#[serde(default = "default_tmp_dir")]
|
#[serde(default = "default_tmp_dir")]
|
||||||
pub tmp_dir: PathBuf,
|
pub tmp_dir: PathBuf,
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ fn main() -> Result<()> {
|
|||||||
// 在 config move 进 ServiceManager 之前,提取 AI 相关配置
|
// 在 config move 进 ServiceManager 之前,提取 AI 相关配置
|
||||||
let ai_model_store = config.ai.model_store.clone();
|
let ai_model_store = config.ai.model_store.clone();
|
||||||
let ai_tools_dir = config.ai.tools_dir.clone();
|
let ai_tools_dir = config.ai.tools_dir.clone();
|
||||||
|
let ai_whisper_lib_dir = config.ai.whisper_lib_dir.clone();
|
||||||
let ai_tmp_dir = config.ai.tmp_dir.clone();
|
let ai_tmp_dir = config.ai.tmp_dir.clone();
|
||||||
let ai_context_window = config.ai.context_window;
|
let ai_context_window = config.ai.context_window;
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ fn main() -> Result<()> {
|
|||||||
let ai_plugin = AiPlugin::new_default(
|
let ai_plugin = AiPlugin::new_default(
|
||||||
ai_model_store,
|
ai_model_store,
|
||||||
ai_tools_dir,
|
ai_tools_dir,
|
||||||
|
ai_whisper_lib_dir,
|
||||||
ai_tmp_dir,
|
ai_tmp_dir,
|
||||||
ai_context_window,
|
ai_context_window,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,11 +46,22 @@ pub trait AiBackend: Send {
|
|||||||
pub struct LocalCliBackend {
|
pub struct LocalCliBackend {
|
||||||
/// 工具目录(含 whisper-cli, llama-cli, piper 二进制)
|
/// 工具目录(含 whisper-cli, llama-cli, piper 二进制)
|
||||||
tools_dir: PathBuf,
|
tools_dir: PathBuf,
|
||||||
|
/// whisper-cli 依赖的库路径(libggml*.so 所在)
|
||||||
|
whisper_lib_dir: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalCliBackend {
|
impl LocalCliBackend {
|
||||||
pub fn new(tools_dir: PathBuf) -> Self {
|
pub fn new(tools_dir: PathBuf) -> Self {
|
||||||
Self { tools_dir }
|
Self {
|
||||||
|
tools_dir,
|
||||||
|
whisper_lib_dir: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置 whisper-cli 依赖库路径(部署时由 deploy_ai.sh 标定)
|
||||||
|
pub fn with_whisper_lib_dir(mut self, lib_dir: PathBuf) -> Self {
|
||||||
|
self.whisper_lib_dir = Some(lib_dir);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn whisper_cli(&self) -> PathBuf {
|
fn whisper_cli(&self) -> PathBuf {
|
||||||
@@ -64,6 +75,13 @@ impl LocalCliBackend {
|
|||||||
fn piper(&self) -> PathBuf {
|
fn piper(&self) -> PathBuf {
|
||||||
self.tools_dir.join("piper")
|
self.tools_dir.join("piper")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 给命令设置 LD_LIBRARY_PATH(whisper-cli 依赖 libggml*.so)
|
||||||
|
fn with_env(&self, cmd: &mut Command) {
|
||||||
|
if let Some(lib_dir) = &self.whisper_lib_dir {
|
||||||
|
cmd.env("LD_LIBRARY_PATH", lib_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AiBackend for LocalCliBackend {
|
impl AiBackend for LocalCliBackend {
|
||||||
@@ -71,7 +89,9 @@ impl AiBackend for LocalCliBackend {
|
|||||||
// whisper-cli -m <model> -f <audio> -l zh --no-timestamps -otxt -of <tmp>
|
// whisper-cli -m <model> -f <audio> -l zh --no-timestamps -otxt -of <tmp>
|
||||||
// 输出到 <tmp>.txt,读取后返回
|
// 输出到 <tmp>.txt,读取后返回
|
||||||
let tmp_out = audio_path.with_extension("asr.txt");
|
let tmp_out = audio_path.with_extension("asr.txt");
|
||||||
let output = Command::new(self.whisper_cli())
|
let mut cmd = Command::new(self.whisper_cli());
|
||||||
|
self.with_env(&mut cmd);
|
||||||
|
let output = cmd
|
||||||
.arg("-m")
|
.arg("-m")
|
||||||
.arg(model_path)
|
.arg(model_path)
|
||||||
.arg("-f")
|
.arg("-f")
|
||||||
|
|||||||
@@ -47,10 +47,15 @@ impl AiPlugin {
|
|||||||
pub fn new_default(
|
pub fn new_default(
|
||||||
model_store: PathBuf,
|
model_store: PathBuf,
|
||||||
tools_dir: PathBuf,
|
tools_dir: PathBuf,
|
||||||
|
whisper_lib_dir: Option<PathBuf>,
|
||||||
tmp_dir: PathBuf,
|
tmp_dir: PathBuf,
|
||||||
context_window: usize,
|
context_window: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let backend = Box::new(backend::LocalCliBackend::new(tools_dir.clone()));
|
let mut backend_builder = backend::LocalCliBackend::new(tools_dir.clone());
|
||||||
|
if let Some(lib_dir) = whisper_lib_dir {
|
||||||
|
backend_builder = backend_builder.with_whisper_lib_dir(lib_dir);
|
||||||
|
}
|
||||||
|
let backend = Box::new(backend_builder);
|
||||||
let models = model_manager::ModelManager::new(model_store);
|
let models = model_manager::ModelManager::new(model_store);
|
||||||
let pipeline = ChatPipeline::new(backend, models, tmp_dir, context_window);
|
let pipeline = ChatPipeline::new(backend, models, tmp_dir, context_window);
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
Reference in New Issue
Block a user