news 2026/9/13 12:15:39

FunASR 应用选型实战:音频剪辑、实时语音识别与语音对话的完整落地指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FunASR 应用选型实战:音频剪辑、实时语音识别与语音对话的完整落地指南

FunASR 应用选型实战:音频剪辑、实时语音识别与语音对话的完整落地指南

【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR

本指南以 FunASR 官方参考文档《Speech Applications》为骨架,围绕"先选应用场景、再选模型与服务契约"的核心方法论展开,系统讲解音频剪辑(Audio Cut)、实时语音识别(Realtime Speech Recognition)、语音对话(Audio Chat)三大应用方向的选型依据、部署路径与底层原理。读完本文,你将掌握:如何借助 FunClip 与 MOSS-Transcribe-Diarize 完成带说话人标签的音频/视频剪辑;如何基于原生 C++ WebSocket 服务与实时基准方法学评估低延迟识别;以及如何通过 OpenAI 兼容 HTTP 服务把 FunASR 接入 Dify、n8n、LangChain 等 Agent 工作流。

应用先行:先选场景,再选模型与服务契约

FunASR 是一个覆盖训练、推理、流式识别、VAD、标点、说话人分离与 OpenAI 兼容/MCP 服务的开源语音工具包。面对如此多的模型与服务形态,最容易犯的错误是"先选模型再想用途"。官方参考文档 docs/reference/application.md 给出的核心方法论非常明确:

Choose the application first, then select its model and serving contract.(先选应用,再选模型与对外服务契约。)

也就是说,落地一个语音产品时,第一步应当明确"这个应用到底要完成什么任务"——是剪辑、实时转写还是对话交互——再根据任务性质决定使用哪条推理路径、暴露哪种服务接口。三条已维护的落地路径分别对应三大类应用:

应用方向典型任务首选技术路径
Audio Cut(音频剪辑)基于转写稿剪辑音频/视频、按说话人切分片段FunClip + MOSS-Transcribe-Diarize
Realtime Speech Recognition(实时识别)实时字幕、会议/呼叫中心流式转写原生 C++ WebSocket 服务 + 实时基准方法学
Audio Chat(语音对话)Agent 工作流中的文件/句子转写OpenAI 兼容 Python HTTP 服务 + 工作流集成

更丰富的场景速查可参考 docs/use_case_showcase.md(按目标给出去处与理由)与 docs/community_projects.md(社区已验证的第三方集成清单);部署形态的横向对比见 docs/deployment_matrix.md。

场景一:音频剪辑(Audio Cut):转写驱动的剪辑与说话人归属

两条互补的剪辑路径

音频剪辑的核心需求是"找到音频里说了什么、谁在什么时候说的",再据此切分素材。官方文档给出两条路径:

  1. FunClip:转写驱动的音频/视频剪辑工具,适合按文本内容定位并裁剪片段;
  2. MOSS-Transcribe-Diarize:面向带说话人归属的录音,一次推理同时产出转写文本、时间戳、匿名说话人标签(如[S01],无需外部 VAD/说话人分离流水线。

MOSS 说话人标签的语义边界(必须理解)

MOSS 输出的标签是录音内匿名的:[S01]不代表某个已知人物的真实身份,不验证已登记的声纹,也不保证与另一段录音中的[S01]是同一人。在对接字幕、会议纪要或剪辑逻辑时,切勿把标签当做人名或跨录音身份 ID 使用。

另一个工程要点:接入 MOSS 前必须核对所选 FunClip 版本及其后端,不能假设每个 ASR 选项的输出字段一致(例如时间戳粒度、标签字段命名),否则下游剪辑逻辑会拿不到预期字段。

FunASR AutoModel 契约:一次生成转写 + 时间戳 + 说话人

MOSS 是 OpenMOSS 发布的 Apache-2.0 第三方模型(非 FunASR 自研模型),FunASR 为其提供适配层,保留原始模型名、许可证与上游版本。在本地 Transformers 后端下,调用方式如下(docs/moss_transcribe_diarize.md):

from funasr import AutoModel model = AutoModel( model="OpenMOSS-Team/MOSS-Transcribe-Diarize", model_revision="e8681d68e7042738ffca8ac8212bc8fcb1131ab8", backend="hf", device="cuda:0", dtype="bf16", attn_implementation="sdpa", disable_update=True, ) result = model.generate("audio.wav", max_new_tokens=5120)[0] print(result["text"]) for segment in result["sentence_info"]: print(segment["start"], segment["end"], segment["spk"], segment["text"])

关键约束:MOSS 在同一轮生成中完成长音频转写与说话人分离,因此严禁传入vad_modelspk_model——外部 VAD 切分会破坏跨 chunk 的匿名说话人一致性。

适配层保留原始带标签生成在raw_text,同时返回 FunASR 通用字段(源码 funasr/auto/auto_model.py 中generate的结果注释确认了这些字段):

  • text:去掉 MOSS 控制标签后的可读转写;
  • timestamp:段级[start_ms, end_ms]时间对;
  • sentence_info:每段的startendtextsentencespktimestamp
  • raw_text:供审计的精确[start][Sxx]text[end]原始生成。

安全设计上,如果解析器无法证明标签结构成立,它会把模型文本原样留在textraw_text,并返回空的时间戳/分段数组,绝不臆造说话人元数据——这是值得在对接时专门做一次负向测试的失败模式。

同样的结果契约还可以包装一个已运行的 vLLM 服务(无需下载本地权重):

from funasr import AutoModel model = AutoModel( model="OpenMOSS-Team/MOSS-Transcribe-Diarize", backend="vllm", vllm_base_url="http://127.0.0.1:8898/v1", vllm_model="moss-transcribe-diarize", vllm_response_format="diarized_json", disable_update=True, ) result = model.generate("audio.wav", max_completion_tokens=8192)[0]

服务化与容器化落地

内置离线 HTTP 服务把同一规范化结果通过/v1/audio/transcriptions暴露出来,加载固定版本的 Transformers 模型且不挂外部 VAD/说话人模型:

python -m pip install "transformers>=5.6,<6" fastapi uvicorn python-multipart funasr-server --model moss-transcribe-diarize --device cuda:0 --port 8000 curl -fsS http://127.0.0.1:8000/v1/audio/transcriptions \ -F file=@meeting.wav \ -F model=moss-transcribe-diarize \ -F response_format=verbose_json

响应包含text、音频duration以及带start/end/text/匿名speakersegments。请求不需要spk=true;即使通用客户端传了该字段,服务仍使用 MOSS 原生标签,不会启动第二条分离流水线。

MOSS 是离线长文模型,不会被 FunASR 的实时 WebSocket 服务暴露,请用 HTTP 端点处理完整文件;FunClip 消费的是同一个sentence_info契约来做说话人感知的字幕与剪辑。可复现的 GPU 容器可从仓库根目录用examples/openai_api/docker-compose.moss.yml构建,Kubernetes 操作者可构建同名镜像并应用examples/openai_api/kubernetes/funasr-moss-api.yaml(上线前把本地镜像引用替换为仓库里的不可变 digest)。

场景二:实时语音识别(Realtime Speech Recognition):协议、消息状态与延迟度量

先看部署矩阵,再选运行时

实时识别与离线转写的选型逻辑完全不同。官方文档建议从 docs/deployment_matrix.md 和 runtime/docs/websocket_protocol.md(WebSocket/gRPC 通信协议)入手。矩阵中与实时/流式相关的路径包括:

  • Runtime WebSocket 服务:适合实时字幕、会议、呼叫中心流,关注部分结果(partial results)、端点检测(endpointing)与长连接流;
  • ONNX/C++ 运行时:高并发 CPU 服务或嵌入式实时 ASR,适合延迟/并发已被验证的场景;
  • vLLM 加速:Fun-ASR-Nano 的文件转写或分流引擎解码(不适用于非自回归的 Paraformer)。

一个必须强调的边界:Python vLLM preview 会话与原生 C++ 流式服务使用不同的消息与状态迁移,客户端不可互换。上线前务必用真实音频验证 chunk 大小、VAD、端点检测、标点、说话人分离、断线重连与客户端背压。

原生 WebSocket 协议:offline / online / 2pass 三种模式

协议文档(runtime/docs/websocket_protocol.md)明确了配置参数与元信息走 JSON、音频数据走二进制字节的混合消息格式:

离线文件转写(offline)初始化消息

{"mode": "offline", "wav_name": "wav_name", "wav_format":"pcm", "is_speaking": True, "hotwords":"{"阿里巴巴":20,"通义实验室":30}", "itn":True}

实时识别(2pass)初始化消息

{"mode": "2pass", "wav_name": "wav_name", "is_speaking": True, "wav_format":"pcm", "chunk_size":[5,10,5],"hotwords":"{"阿里巴巴":20,"通义实验室":30}","itn":true}

关键参数语义:

参数含义
modeoffline单句识别;online实时识别;2pass实时识别 + 句尾离线模型纠正
wav_name待转写音频名
wav_format音频/视频扩展名(pcm、mp3、mp4 等;1.0 版实时流仅支持 PCM)
is_speakingFalse表示一句话结束(VAD 切分点或 WAV 文件末尾)
chunk_size流式模型延迟配置,[5,10,5]表示当前音频 600ms,含 300ms 前瞻与回看
audio_fsPCM 输入时需指定采样率参数
hotwords热词数据(字符串),如{"阿里巴巴":20,"通义实验室":30}
itn是否启用逆文本正则化,默认 true

PCM 格式直接发送音频数据;其他格式需连同头信息一起发送。发送完毕后必须发送结束标志{"is_speaking": False}。服务端返回消息中,2pass-online表示实时识别结果、2pass-offline表示 2-pass 纠正结果,时间戳模型会返回timestamp(如[[100,200], [200,500]])与stamp_sents字段。

MOSS 不是流式模型的澄清

MOSS 适合长文转写/分离,但不是原生低延迟流式模型的替代品:分块上传接收音频并不等于底层模型具备流式识别能力。如果产品需要实时字幕或逐字输出,应选择 WebSocket 服务而非 MOSS。

用实时基准方法学度量端到端延迟

离线RTFx与实时服务延迟是两回事。官方实时基准(docs/benchmark/realtime_ws_benchmark.md)聚焦首条更新延迟、STOP 后最终延迟、响应滞后与多客户端行为,基准客户端只接受 16kHz 单声道 PCM16 WAV 输入,以排除重采样与文件解码对测量的干扰。

启动服务(建议有界部分结果窗口 + 适中的部分刷新间隔):

CUDA_VISIBLE_DEVICES=0 python examples/industrial_data_pretraining/fun_asr_nano/serve_realtime_ws.py \ --port 10095 --language 中文 \ --partial-window-sec 8 --decode-interval 0.8 \ --vad-device cpu --vad-ncpu 1 \ --decode-batch-wait-ms 10 --decode-max-batch-size 16 \ --log-decode-profile

说明:说话人分离默认关闭,只有确实需要spk字段时才加--enable-spk并在结果中注明该设置;落在--decode-batch-wait-ms窗口内的兼容跨会话解码会合并为一个引擎 batch,对比版本时必须保持所有 batching 参数一致。

单客户端实时回放(按真实时间速度发送 100ms 帧,最接近麦克风/浏览器流):

python examples/industrial_data_pretraining/fun_asr_nano/realtime_ws_benchmark.py \ audio_16k_mono_pcm16.wav \ --server ws://localhost:10095 \ --clients 1 \ --output-jsonl realtime_ws_1c.jsonl

并发回放

python examples/industrial_data_pretraining/fun_asr_nano/realtime_ws_benchmark.py \ audio_16k_mono_pcm16.wav \ --server ws://localhost:10095 \ --clients 8 \ --loops 3 \ --chunk-ms 100 \ --client-ping-interval 20 \ --client-ping-timeout 0 \ --language 中文 \ --output-jsonl realtime_ws_8c.jsonl

<=0表示禁用对应客户端 ping 设置;--no-pace为非定速压力测试,其结果是吞吐压力信号而非面向用户的实时延迟。)

核心指标

指标含义
aggregate_audio_per_wall所有客户端总输入音频秒数 ÷ 基准墙钟时间
first_update_ms_p50/p95从首帧音频到首个含sentences/partial/is_final结果消息的耗时
final_after_stop_ms_p50/p95从发送STOP到收到最终结果的耗时
client_response_lag_ms_p95_max各客户端 p95 中最大的非最终响应滞后(定速模式下用于观察预览/部分结果滞后)
partial_messages/final_messages非最终部分结果 / 最终结果消息计数
errors连接、超时、协议或客户端校验错误

基准脚本只能观测客户端侧时序与服务端返回字段。做性能排查时应加--log-decode-profile,让每次引擎调用输出一行结构化日志(请求/样本数、音频时长范围、队列等待 p50/max、引擎总延迟),并配合 GPU 利用率与服务端日志一起分析。注意:一个阻塞了 WebSocket 事件循环的服务可能显得"更快"——因为它处理的部分解码更少,这不是引擎吞吐提升,用户获得的下发更新也更少。

官方仓库还给出了 v1.4.3 与引入并发解码批处理默认值后的回归参考(12/16 客户端、47 秒循环中文录音、定速 100ms 帧、单 H100 80GB),显示批处理后final_after_stop与响应滞后大幅下降。该数据只是回归参考,不是普适容量承诺;长语音段会产生同步且昂贵的最终解码,不代表所有会议或语音 Agent 负载形态。发布基准或提交 issue 时,请按报告模板记录数据(音频时长/采样率/语言/静音比)、负载(--clients/--loops/--chunk-ms/定速与否/ping 参数)、服务(完整命令与全部--参数)、硬件(GPU 型号/驱动/CUDA/CANN)与软件版本(funasr/PyTorch/torchaudio/vLLM/Python/OS)。

场景三:语音对话(Audio Chat):把 FunASR 接入 Agent 工作流

场景定位:识别是组件,对话是组合

在 Agent 工作流中做文件或句子的转写时,官方文档指向 examples/openai_api/README.md(Python HTTP 服务)与 examples/openai_api/WORKFLOWS.md(工作流集成指南)。这里有一条重要的架构边界:FunASR 只提供识别(speech recognition);对话生成、语音合成、轮次管理(turn-taking)与打断处理是独立的应用组件。上线前必须联合验证组合延迟与隐私要求——识别快了 100ms,如果下游 LLM 与 TTS 链路慢 2 秒,用户体验仍然由最慢环节决定。

快速启动一个 OpenAI 兼容转写服务

在干净 checkout 与 Python 3.11 环境(examples/openai_api/README.md 提供完整步骤)下:

python -m venv .venv source .venv/bin/activate python -m pip install -e . python -m pip install fastapi uvicorn python-multipart python -m pip check cd examples/openai_api python server.py --host 127.0.0.1 --model sensevoice --device cpu --port 8000

(该命令固定的是源码而非依赖、模型权重或 CUDA;有 CUDA 能力后用--device cuda替换,且不要在同一端口启动两个服务。等待模型加载完成后再检查GET /health,健康检查本身不能证明声学推理正确。)

curl 转写:

curl http://localhost:8000/v1/audio/transcriptions \ -F file=@audio.wav \ -F model=sensevoice \ -F response_format=verbose_json

OpenAI Python SDK 用法:

from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed") with open("meeting.wav", "rb") as audio: result = client.audio.transcriptions.create(model="sensevoice", file=audio) print(result.text)

API 契约的关键差异(示例服务 vs 打包服务)

仓库中examples/openai_api/server.py的实现与 PyPI 打包的funasr-server(funasr/bin/_server_app.py 对应的不同实现)在契约上存在多处差异,对接前务必核对:

  • response_format=verbose_json只是选择响应形态,不会启用说话人分离或强制生成时间戳(源码 examples/openai_api/server.py 的MODEL_CONFIGS中,SenseVoice 别名未配置外部spk_model);
  • 示例服务的durationgenerate()的墙钟耗时(不含首次模型加载),不是音频时长;打包服务的duration是音频时长(元数据缺失时可能为 0);
  • 两边的start/end都使用秒;示例服务把模型自带的sentence_info拷贝进segments,否则返回segments=[],而打包服务可通过文本与音频时长合成粗粒度分段——那不是词级强制对齐;
  • 示例服务接受filemodellanguageresponse_format表单字段,SDK 选项(use_itn、热词、原始数组、spk不是它的表单字段;
  • 请求中显式指定model:启动预加载与请求默认值是两套设置,示例服务启动与缺省 multipartmodel均默认sensevoice,打包服务则按设备字符串选择fun-asr-nano/sensevoice

模型别名(docs/model_selection.md 中有完整对照):sensevoice(iic/SenseVoiceSmall + FSMN-VAD)、paraformer(paraformer-zh + VAD + CT 标点)、paraformer-en(示例服务独有别名)、fun-asr-nano(FunAudioLLM/Fun-ASR-Nano-2512,示例中走 AutoModel 而非 vLLM 路由)、moss-transcribe-diarize(第三方 MOSS 适配,需独立依赖环境)。

端点一览

端点方法说明
/v1/audio/transcriptionsPOST转写音频(OpenAI 兼容)
/v1/modelsGET列出可用模型
/healthGET健康检查 + 已加载模型
/docsGETSwagger 交互式文档

低代码工作流集成:Dify / n8n / LangChain

所有工作流引擎最终都要发送同一个 multipart 请求形态(examples/openai_api/WORKFLOWS.md):

  • 方法POSTURLhttp://<funasr-host>:8000/v1/audio/transcriptions
  • Bodymultipart/form-data;文件字段file;文本字段model=sensevoiceresponse_format=verbose_json
  • 超时:按最长音频时长设置,长文件建议 300 秒

Dify 中可配置 HTTP 请求节点或自定义工具,把text映射为转写结果;使用segments前必须确认其来源与语义(示例服务的segments仅来自模型自带的sentence_info)。若工作流工具传的是文件 URL,要注意:URL 字符串放在 multipartfile字段中不是音频上传requests.get会跟随重定向并整块缓冲响应,其 timeout 不是字节上限,不要直接把用户提供的 URL 交给该 helper,需要先建立经过评审的下载边界(目标白名单、私网访问策略、重定向校验、字节上限、认证),"在可信网络内"并不是 SSRF 的防御。

n8n 的推荐流程为 trigger → 二进制音频数据 → HTTP Request → 转写消费方;LangChain 场景可直接把client.audio.transcriptions.create(...)封装成 Agent 的工具函数(见 examples/openai_api/CLIENTS.md)。

安全边界:示例服务无内置认证

无论是示例server.py还是打包funasr-server都没有实现网关认证或应用级总上传大小限制,默认监听0.0.0.0api_key="not-needed"不代表经过认证。官方安全指南(examples/openai_api/SECURITY.md)推荐如下拓扑:

OpenAI SDK / Dify / n8n / 浏览器 UI | v TLS + 认证 + 上传限制 + 日志 (反向代理 / API 网关 / ingress / service mesh) | v FunASR OpenAI 兼容 API(私有主机 / VM / 容器 / K8s ClusterIP)

共享前的最小控制项:TLS(音频常含隐私数据)、认证(Basic/Bearer/OAuth/OIDC 网关对)、上传大小限制(防多 GB 上传与内存压力)、超时(长录音需更长超时但卡死客户端不应永久挂起)、限流(保护 GPU/CPU 容量)、私有运维路由(/health/v1/models、schema/UI 在共享监听器上应被拒绝)、日志与留存策略(原始音频可能敏感)。

仓库提供了 NGINX 与 Caddy 两套只放行POST /v1/audio/transcriptions的反向代理示例:NGINX 用limit_except POST { deny all; }+auth_basic+client_max_body_size,Caddy 用basic_auth+request_body max_size+reverse_proxy,两者都在转发前移除Authorization头(FunASR 不需要网关的 Basic 凭据)。代理超时不会取消已运行的模型推理;ClusterIP也不是认证或命名空间隔离,需配合强制NetworkPolicy并验证实际网络路径。把 FunASR 留在私有网络,把公开 TLS、身份、请求限制与审计日志放在团队已运维的边界上。

选型后的检查清单与排障

完成三大场景选型后,官方文档建议(docs/deployment_matrix.md 的 Readiness checklist):

  1. 选定模型别名并在部署文档中固定版本(FunASR 版本、模型版本、设备、CUDA/PyTorch 版本、Docker 镜像 tag、完整命令行);
  2. 先跑一个短的公开冒烟样本,再跑至少一个贴近业务的实际样本;
  3. 为每个请求记录音频时长、模型、设备、延迟、响应格式与错误类型;
  4. 在暴露到不可信网络前配置上传大小限制、认证、TLS 与限流;
  5. 热词类需求先明确是"确定性文本后处理"还是"解码期偏置",再决定是否更换运行时;
  6. 流式场景必须用真实音频测试静音、噪声、重叠说话人、长会话、重连与慢客户端;
  7. 基准声明必须包含输入时长、硬件、batch、模型、运行时路径,并说明是否排除模型下载/预热时间。

模型选择上,多语言快速转写优先 SenseVoice-Small、普通话生产 ASR 优先 Paraformer-Large、LLM 类 ASR 实验用 Fun-ASR-Nano(吞吐敏感时配合 docs/vllm_guide.md)、带说话人的离线长文转写用 MOSS-Transcribe-Diarize,详细对照见 docs/model_selection.md。遇到运行时、Docker、vLLM、Triton、Android、浏览器或 Agent 集成问题,可参考 docs/troubleshooting.md;提交 issue 时务必带上部署路径、精确命令/配置、日志、模型、设备与音频特征。

最后回到方法论本身:无论是音频剪辑、实时识别还是语音对话,落地顺序都应是"场景 → 模型 → 服务契约 → 延迟/隐私验证"。FunASR 提供的是一条条已被文档、源码与测试确认的路径,而非一个万能模型——先选对场景,后续的每一层选型才有意义。

【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 12:15:22

pip conda python的包管理 虚拟环境env

增删改查包pip install <path> # 安装本地包whl 或 文件夹中有setup.py pip install torch1.13.0cu116 # 安装包 pip install --upgrade websockets # 更新包 pip install -r requirements.txt # 根据文件安装包pip show numpy …

作者头像 李华
网站建设 2026/9/13 12:15:07

企业级私有AI助手Dify部署与优化指南

1. 项目概述&#xff1a;企业级私有AI助手的价值定位在数字化转型浪潮中&#xff0c;知识管理已成为企业核心竞争力的关键要素。传统知识库系统存在检索效率低、交互体验差等痛点&#xff0c;而公有云AI服务又面临数据安全风险。Dify作为开源AI应用开发平台&#xff0c;恰好填补…

作者头像 李华
网站建设 2026/9/13 12:14:45

SPC统计过程控制:原理、实施与应用指南

1. SPC基础概念解析 统计过程控制&#xff08;Statistical Process Control&#xff0c;简称SPC&#xff09;是一套运用统计方法对生产过程进行监控与改进的质量管理技术。它的核心思想是通过收集和分析生产过程中的数据&#xff0c;识别异常波动&#xff0c;从而实现对质量的预…

作者头像 李华
网站建设 2026/9/13 12:14:18

海外工程项目数字化交付体系构建与实施指南

1. 海外工程项目数字化交付的行业背景与挑战海外工程项目通常具有周期长、参与方多、标准体系复杂等特点。以某中东地区炼油厂项目为例&#xff0c;项目周期长达5-8年&#xff0c;涉及20多个国家的承包商&#xff0c;需要同时满足ISO、ASTM、API等多重标准体系。传统纸质交付模…

作者头像 李华
网站建设 2026/9/13 12:14:06

用 gs-quant 3 步跑通期货价差均值回归策略:快速教程

用 gs-quant 3 步跑通期货价差均值回归策略&#xff1a;快速教程 【免费下载链接】gs-quant Python toolkit for quantitative finance 项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant 某商品近月与远月期货的价差&#xff0c;在一个月内从 20 元拉大到 18…

作者头像 李华