跳到主要内容

84 篇博文 含有标签「AI」

Artificial Intelligence 相关文章

查看所有标签

多模态 RAG 的检索层工程化:视觉后期交互(ColQwen)的账本、剪枝与踩坑

· 阅读需 15 分钟

做 RAG 的后端工程师大多有过这样的经历:一套 PDF 解析(PyMuPDF + pdfplumber)、分块、embedding、rerank 的管线在技术文档上效果不错,一换到财报、合同、产品手册就开始掉分。检索出来的段落看着相关,但答案在表格的某个交叉格里、在某张流程图的标注里、或者依赖两栏排版的位置关系——这些信息在"解析成纯文本"的那一步就已经丢了。

VisRAG 这篇工作把这个损失量化过:不做文本解析、直接把文档页当图片用 VLM 嵌入,端到端比传统文本 RAG 管线高 20%~40%。这个数字不必照搬(它建立在自建的检索/生成数据上),但方向是明确的:多模态文档的检索单元应该是"页",而不是"段落"。

本文不讨论 VLM 生成侧,只讲检索层怎么落地:三条技术路线的取舍、多向量的存储与算力账本、把成本压下来的四种手段,以及我自己在 ColQwen 上趟过的坑。前置阅读可以看 RAG 进阶检索 和 向量索引 HNSW/IVF-PQ 两篇。

向量索引内幕:HNSW 与 IVF-PQ 的召回、延迟、内存三方账本

· 阅读需 11 分钟

向量检索上线后最常见的两类问题,一类是「延迟一高就加机器」,另一类是「召回不够就把 efSearch 往上调两倍」——两种都治不了根,因为向量索引不存在「更好的参数」,只存在召回、延迟、内存三角里你愿意让出哪条边。基础链路见 RAG 全链路解析,召回与重排的工程取舍见 RAG 进阶实战,这篇下沉一层,讲索引本身:HNSW 的图怎么长、IVF-PQ 的码本怎么省内存、过滤检索为什么会塌方、删除为什么永远不还内存。

面试题:长上下文到底是怎么做到的?RoPE 为什么不能直接外推、PI/NTK/YaRN 各在插什么、32K 训练的模型凭什么撑到 128K,长上下文和 RAG 又该怎么选?

· 阅读需 17 分钟

这是「每日一题」专栏的第二十一篇。每天一道面试题,后端 + AI 混合路线,从原理到代码一次讲清。上一篇是 分库分表之后的在线扩容,今天按轮换回到 AI/LLM 方向,兑现上一篇的预告:长上下文与 RoPE 长度外推。这道题的分水岭很清楚——背过的人能说出「位置插值」四个字,真做过的人能讲清楚「为什么高频维度和低频维度的失败方式完全相反,所以一刀切必错」。

分词器才是真正的接口:BPE 从零实现,以及它如何决定你的账单、缓存命中率与算术能力

· 阅读需 16 分钟

后端工程师看 LLM 应用,最容易忽略的一层是分词器。它夹在你的文本和模型之间:你发出去的每一个请求,先被转换成一串整数编号;你按量付费的每一个 token,就是这串编号里的一位。这一层没有超时、没有重试、没有连接池,看起来不像"系统",但它是唯一决定 token 数量的环节——而 token 数量同时决定了账单、KV Cache 显存、有效上下文窗口和 decode 阶段的吞吐。

更要命的是,分词器不是中立的。它的行为由训练语料的语言配比决定:一个主要用英文语料训练出来的词表,会把绝大多数额度花在英文词素上,中文、代码和数字只能退化成更短、数量更多的符号。本文不讲"分词器很重要"这种废话,直接从零写一个 BPE,然后用它跑四组实验,把每一项都变成可以写在容量规划表里的数字。所有代码只用 Python 标准库,你可以直接复制运行。

面试题:MoE 为什么能用更少的激活参数换来更大的容量?专家路由怎么设计、负载不均衡怎么治、为什么 MoE 推理比同规模稠密模型更难?

· 阅读需 16 分钟

这是「每日一题」专栏的第十九篇。每天一道面试题,后端 + AI 混合路线,从原理到代码一次讲清。上一篇是 短链接系统进阶,间隔了一个月没更新,今天按轮换回到 AI/LLM 方向,聊近两年面试出现频率陡增的一题:混合专家(MoE)——为什么 671B 的模型只需要 37B 的激活参数就能跑起来,这条路到底省了什么、又难在哪。

开源模型动态与选型决策:发布节奏、License 陷阱与自建评测基准

· 阅读需 7 分钟

开源模型现在是一周一个的节奏:Kimi-K3 那篇 2.8T 的 MoE 还没消化完,DeepSeek V4 Flash 就以 284B 参数打出了「智能指数 50」的性价比,隔一周又是 GLM-5.3 把编程 Agent 安全做成卖点(见 GLM-5.3 编程 Agent 安全拆解)。对后端工程师来说,选型已经不是一个「调研一次、用半年」的静态决策,而是一个持续跟踪、反复评估、随时切换的工程问题。本文不讲具体某个模型的评测报告,讲怎么建立自己的评估框架:看什么动态、躲什么 License 坑、怎么用自家任务集跑分。

RAG 进阶实战:切块策略、混合检索与 Re-ranking 的量化收益与工程取舍

· 阅读需 9 分钟

RAG 的链路谁都能搭:文档切块、embedding 进库、相似度召回、拼 prompt。但同一套链路,有人线上答对率八成,有人五成——差距不在「会不会搭」,而在「每一步的效果账算没算清」。基础链路见 RAG 全链路解析,这篇讲进阶:切块、混合检索、Re-ranking 各值多少分、花多少钱,Graph RAG 什么时候才值得上。

GUI Agent 工程化:让大模型操作电脑的感知、行动与安全拆解 — Computer Use 从原理到落地

· 阅读需 9 分钟

文本 Agent 的标配是「工具调用」:模型说一句 search_order("sku"),代码去执行,接口是结构化的。但 2024 年底开始,OpenAI Operator、Anthropic Computer Use、各类桌面/浏览器 Agent 把目标换成了像人一样直接操作电脑——没有 API、没有 DOM、只有像素和窗口。这条技术线工程上比想象中难得多,本文按「感知 → 行动 → 验证 → 成本 → 评测 → 安全」拆开讲,附可运行的骨架代码与实测踩坑。

面试题:为什么 vLLM 是当前最快的 LLM 推理框架?Continuous Batching 和 PagedAttention 各自解决了什么问题、显存占用怎么降下来的?

· 阅读需 9 分钟

这是「每日一题」专栏的第十六篇。每天一道面试题,后端 + AI 混合路线,从原理到代码一次讲清。上一篇讲了 实时协作文档系统,今天按轮换回到 AI/LLM 方向,聊上一篇预告的题:为什么 vLLM 最快?Continuous Batching 和 PagedAttention 各自解决了什么?

流式 TTS 合成工程:延迟预算、模型选型与评测回环

· 阅读需 9 分钟

语音 Agent 的体验差距,经常不是死在 ASR 听错,而是死在最后一跳:合成的声音不够快、不够自然。上一篇 语音 Agent 与流式 ASR 讲了 VAD、ASR 和打断,本篇专攻合成侧:模型选型、流式机制、中文特有的工程坑、以及一套不依赖人耳的评测方法。

先把预算摆出来:语音场景 800ms 的端到端延迟预算里,TTS 通常只能分到 300-400ms,其中首包延迟(TTFB)是硬指标——用户听到第一个字之前的所有环节,VAD、ASR、LLM、TTS,都在为这 800ms 打工。

一、三代 TTS 架构:选型先看你要什么​

第一代:自回归系(VALL-E / XTTS)。神经编解码器(EnCodec 这类)把音频打成离散 token,再用 transformer 逐 token 生成。VALL-E 在 2023 年证明了 3 秒参考音频就能克隆音色,这是声音克隆的起点。代价:逐 token 生成天然慢,长句容易重复、吞字,而且不支持流式。今天的定位基本只剩离线高质量生成。

第二代:非自回归系(VITS / FastSpeech 2)。一步出整句 mel 谱,快、稳、RTF 极低,但克隆能力弱、韵律平。适合 IVR 播报这类对音色无要求的场景,或者作为流式拼接链路的底层单元。

第三代:Flow Matching / DiT 系(F5-TTS / CosyVoice2)。把语音生成建模成流匹配问题,推理步数可压缩——F5-TTS 默认 10 步,论文报告单卡 A100 上 RTF 约 0.15(RTF = 合成耗时 / 音频时长,低于 1 才够实时,流式场景要更低)。CosyVoice2 走 LLM + Flow 双栈,官方口径是把流式合成延迟压到 150ms 量级、推理速度较上一代提升约 50%。零样本克隆 + 可控步数 = 自然度与速度兼得,是 2026 年生产的事实标准。

维度自回归非自回归Flow/DiT
自然度高中高
声音克隆强(3s 提示)弱强
推理速度慢极快快
原生流式不支持不支持支持(如 CosyVoice2)
代表模型VALL-E、XTTSVITS、FastSpeech2F5-TTS、CosyVoice2

选型结论:做语音 Agent 直接选 Flow/DiT 系;做稳定的播报机器人可以用 VITS 系省 GPU;自回归留给离线精修。别在自回归模型上硬做流式——那是在给架构缺陷打补丁。

二、流式合成的两条路线​

路线 A:模型原生流式。CosyVoice2 的 speech token 是流式产出的,stream=True 就能边生成边吐音频块,这是延迟最低的路线:

from cosyvoice.cli.cosyvoice import CosyVoice2
import torchaudio

cosyvoice = CosyVoice2("pretrained_models/CosyVoice2-0.5B",
load_jit=False, load_trt=False, fp16=False)

prompt_wav, sr = torchaudio.load("ref.wav") # 3-10 秒参考音频
prompt_speech_16k = torchaudio.transforms.Resample(sr, 16000)(prompt_wav)

# stream=True:每个 chunk 出来立刻可以推给客户端
for chunk in cosyvoice.inference_zero_shot(
"今天上海多云转晴,气温二十四到三十一度。",
"希望你以后能够做的比我还好呦。", # 参考音频的原文,用于对齐
prompt_speech_16k,
stream=True,
):
torchaudio.save("chunk.wav", chunk["tts_speech"], cosyvoice.sample_rate)
# 这里把 chunk 编码成 opus 推 WebSocket

路线 B:chunk 级流式封装(模型无关)。任何非流式模型都能切成块:按句/按标点切,块间透传上下文。两个坑必须处理:句边界韵律断裂、块间静音过长。做法是 overlap——把上一句结尾的 200ms 作为下一句的 prompt 前缀,让模型"接着读"而不是"重新读"。服务骨架:

import asyncio
from fastapi import FastAPI, WebSocket

app = FastAPI()

def tts_one(sentence: str) -> bytes: # 任意非流式模型,返回 24k PCM
...

async def synthesize(text: str):
for sent in split_sentences(text):
yield tts_one(sent) # 逐句合成即推,不等整段

@app.websocket("/tts")
async def tts_ws(ws: WebSocket):
await ws.accept()
text = await ws.receive_text()
async for chunk in synthesize(text):
await ws.send_bytes(chunk) # 帧头带上 seq 与句子边界标记
await ws.close()

端到端延迟预算的典型量级:VAD 50-100ms + 流式 ASR 150-250ms + LLM 首 token 100-300ms + TTS 首包 150-300ms。TTS 段能抠的点:模型常驻 warmup(首次推理含权重加载和图编译,可能多出几百 ms)、首 chunk 用低步数、文本归一化放到与 LLM 并行的流水线里做。

三、中文 TTS 的特有坑:归一化与多音字​

英文数字转读法靠规则就能覆盖九成,中文不行——"二十四"和"两千四百"的读法规则、单位量词的搭配、"一行代码"和"银行行长"的多音,规则表写起来是无底洞。文本归一化 + G2P 是中文 TTS 的前端核心,不是模型的事。

最小归一化实现(整数和百分比,够演示思路):

import re

CN_NUM = "零一二三四五六七八九"

def num_to_cn(n: int) -> str:
if n < 10:
return CN_NUM[n]
if n < 100:
tens = "" if n < 20 else CN_NUM[n // 10] + "十"
return tens + (CN_NUM[n % 10] if n % 10 else "")
return str(n) # 三位以上兜底给 G2P

def normalize(text: str) -> str:
text = re.sub(r"(\d+)%", lambda m: f"百分之{num_to_cn(int(m.group(1)))}", text)
text = re.sub(r"(?<!\d)(\d{1,2})(?!\d)", lambda m: num_to_cn(int(m.group(1))), text)
return text

多音字消歧,2026 年的生产做法是 LLM 前端:把句子丢给一个小模型,输出拼音 + 停顿位置 + 情感标记,一次把 G2P 和韵律控制全做掉:

输入: 重庆的银行行长今天下午到达重庆北站。
输出: {"pinyin": "chong2qing4 ... hang2zhang3 ...",
"pauses": [4, 12], "emotion": "neutral"}

踩坑记录:LLM 前端本身有 30-80ms 延迟,必须与 TTS 合成并行流水线化,否则它自己就成了延迟大头;生产上建议规则优先、LLM 兜底——常见词查表秒出,查不到的再走模型,把 LLM 调用量压到一成以下。

四、声音克隆:zero-shot 的工程真相​

原理一句话:参考音频抽说话人表征(嵌入或 speech token),文本条件生成时把音色"带"过去。VALL-E 证明 3 秒可行,CosyVoice2 把跨语种克隆做进了同一条流水线——中文参考音色直接说英文。工程上真正决定成败的不是模型,是参考音频:

  • 质量上限:16k 采样、无噪声、3-10 秒、内容与目标文本无关
  • 必须预处理:VAD 裁剪 + 响度归一,别拿整段播客去克隆
  • 合规:克隆真实人物音色上生产,先过授权关

五、评测:MOS 会骗人,回环才客观​

人工 MOS 贵、慢、不稳定。UTMOS 这类预测器论文报告系统级 Spearman 相关约 0.94、句子级只有约 0.5——适合给系统排序,不适合逐句验收。生产三件套:

1. ASR 回环(可懂度):合成 → ASR 转写 → 算 CER。这是回归测试的必备件,改前端、换模型、调步数,跑一遍回环就知道有没有退化:

from faster_whisper import WhisperModel
import numpy as np

model = WhisperModel("small", device="cpu", compute_type="int8")

def cer(ref: str, hyp: str) -> float:
dp = np.zeros((len(ref) + 1, len(hyp) + 1), dtype=int)
for i in range(1, len(ref) + 1):
for j in range(1, len(hyp) + 1):
dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1,
dp[i - 1][j - 1] + (ref[i - 1] != hyp[j - 1]))
return dp[len(ref)][len(hyp)] / max(1, len(ref))

ref = "今天上海多云转晴"
segments, _ = model.transcribe("tts_out.wav", language="zh")
hyp = "".join(s.text for s in segments)
print(f"CER = {cer(ref, hyp):.2%}")

注意回环用 whisper small 做基线就够了,别用大模型——它的纠错能力会把 TTS 的口齿不清"修好",测不出问题。

2. SPK-SIM(音色相似度):说话人嵌入(WeSpeaker / ResNet34 系)的余弦相似度,验收克隆效果。经验阈值:同人同设备一般 0.8 上下,克隆验收从 0.6 起,低于 0.5 基本是克隆失败了。

3. RTF 与首包延迟(性能):压测统计合成耗时 / 音频时长,目标 RTF 低于 0.3(给编码、网络留余量);流式场景额外记 TTFB 和 chunk 间隔抖动——chunk 间隔抖动比平均延迟更伤听感,用户听的是"卡不卡",不是"平均多少毫秒"。

六、实战踩坑清单​

  • chunk 边界爆破音:块间加 10-20ms 交叉淡入淡出,或把上句末尾作下句 prompt 前缀;两招都试过,后者对自然度提升更明显
  • 首包慢的隐藏原因:模型 warmup(首次推理)、G2P 未预热、归一化在合成线程里串行做——把这三样都提前,首包能砍掉一截
  • 并发上限:TTS 是计算密集任务,多路并发按 RTF × 显存反推,别裸开线程池;FP16 比 FP32 省一半显存,先开 FP16
  • 长文本切句:超过 30 秒的合成任务必须前端切句、后端逐句流式返回,等整段合成就别谈延迟了
  • 格式统一:内部 24k/16bit PCM,出口按端侧转 opus/PCM;采样率转换只在出口做一次,别在合成路径里反复转
  • 预合成缓存:欢迎语、系统播报这类高频固定文本,预合成 + 语义 hash 缓存,命中直接推音频,连模型都不进

小结​

2026 年做 TTS 的路线图很清晰:模型选 Flow Matching/DiT 系(F5-TTS、CosyVoice2),流式优先用模型原生能力、次选 chunk 封装,中文必须自建归一化 + G2P 前端,验收靠 ASR 回环 + SPK-SIM + RTF 三件套。语音 Agent 的体验优化顺序建议先攻 TTS——用户对"答得快不快、声音像不像人"的感知,比对 LLM 智商差异敏感得多。合成侧补齐之后,接上 语音 Agent 全链路 和 多模态 VLM 工程,多模态 Agent 的地基就齐了。