fix: 解析 llama-cli 输出,只提取生成回复 (去除 banner/进度动画/统计)

This commit is contained in:
2026-07-04 17:18:58 +08:00
parent a3052ad6db
commit 1c037db0c2

View File

@@ -194,9 +194,18 @@ impl AiBackend for LocalCliBackend {
);
}
// llama-cli 输出到 stdout取生成部分
let reply = String::from_utf8_lossy(&output.stdout);
Ok(reply.trim().to_string())
// llama-cli --single-turn 输出格式:
// [banner/logo]
// > <user_input> ← 用户输入回显
// [进度动画 |/-\]
// <生成的回复> ← 我们要提取的部分
// [空行]
// [ Prompt: ... | Generation: ... ] ← 性能统计
//
// 解析策略:找到 "> " 开头的行,跳过进度动画行,取实际回复行
let raw = String::from_utf8_lossy(&output.stdout);
let reply = parse_llama_output(&raw);
Ok(reply)
}
fn tts(&self, text: &str, model_path: &Path, output_path: &Path) -> Result<()> {
@@ -275,3 +284,86 @@ impl AiBackend for CloudBackend {
"cloud"
}
}
/// 解析 llama-cli --single-turn 的 stdout 输出,提取生成的回复文本
///
/// 输出格式:
/// ```text
/// [banner/logo ASCII art]
/// build : ...
/// model : ...
/// ...
/// > <user_input>
///
/// [进度动画 |/-\]
/// <生成的回复>
///
/// [ Prompt: ... | Generation: ... ]
///
/// Exiting...
/// ```
fn parse_llama_output(raw: &str) -> String {
let lines: Vec<&str> = raw.lines().collect();
let mut reply_lines = Vec::new();
let mut after_user_input = false;
for line in &lines {
let trimmed = line.trim();
// 跳过空行(但在回复区内保留)
if trimmed.is_empty() && !after_user_input {
continue;
}
// 找到用户输入回显行 "> xxx"
if trimmed.starts_with('>') {
after_user_input = true;
continue;
}
if !after_user_input {
continue;
}
// 跳过进度动画行(只含 |/-\ 和空格)
let cleaned: String = trimmed.chars().filter(|c| !"|\\-/ ".contains(*c)).collect();
if cleaned.is_empty() {
continue;
}
// 遇到性能统计行 [ Prompt: ... ] 停止
if trimmed.starts_with('[') && trimmed.contains("Generation") {
break;
}
// 遇到 "Exiting..." 停止
if trimmed == "Exiting..." {
break;
}
reply_lines.push(trimmed);
}
reply_lines.join("\n").trim().to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_llama_output() {
let raw = " █ █ build : b1-fdb1db8\n\
model : model_store/llm/qwen2.5-0.5b-q4_k_m.gguf\n\
\n\
> hello\n\
\n\
|/-\\|/-\\ 汪!\n\
\n\
[ Prompt: 31.1 t/s | Generation: 26.3 t/s ]\n\
\n\
Exiting...\n";
let reply = parse_llama_output(raw);
assert_eq!(reply, "汪!");
}
}