从云端语音助手到本地可替换模型服务:Qwen3-ASR + vLLM 部署实战记录

日期:2026-07-20
目标:在学校 GPU 服务器上完成本地 ASR 与小参数 LLM 的部署验证,并让 Windows 业务后端通过可配置 URL 调用。后续到甲方服务器时,只替换模型服务地址、Token 和必要的模型别名,不再大改业务代码。


一、项目背景

现有系统是一套 AI 语音助手,主要流程如下:

浏览器录音
→ Windows FastAPI 后端
→ 语音识别 ASR
→ LLM 判断意图
→ 调用业务 API/工具
→ LLM 总结结果
→ 前端显示回答

原始实现中:

  • 实时语音识别使用阿里云 DashScope Fun-ASR;
  • 中间 LLM 使用云端 OpenAI 兼容 API;
  • Windows FastAPI 后端核心接口为:
  • WebSocket /ws/asr
  • POST /api/chat/query
  • GET /health
  • 为了方便甲方私有化部署,希望改成:
  • ASR 可替换;
  • LLM 可替换;
  • 业务后端只读取 URL、Token 和模型名;
  • 学校测试环境与甲方生产环境使用同一套协议。

二、最终目标架构

本次确定的目标架构为:

浏览器
│
├─ WebSocket /ws/asr
│      ↓
│  Windows voice_api.py
│      ↓
│  ASR_REMOTE_WS_URL
│      ↓
│  Qwen3-ASR 服务
│
└─ POST /api/chat/query
       ↓
   Windows voice_api.py
       ↓
   LLM_BASE_URL
       ↓
   OpenAI 兼容 LLM 服务
       ↓
   工具调用 / API 查询
       ↓
   总结回答

配置统一放在 .env

ASR_REMOTE_WS_URL=ws://127.0.0.1:18095/v1/asr/stream
ASR_API_TOKEN=<ASR_TOKEN>

LLM_BASE_URL=http://127.0.0.1:18096/v1
LLM_API_KEY=<LLM_TOKEN>
LLM_MODEL=business-llm
LLM_TIMEOUT=60

以后甲方部署时,只需要改:

ASR_REMOTE_WS_URL=ws://<CUSTOMER_ASR_HOST>:10095/v1/asr/stream
LLM_BASE_URL=http://<CUSTOMER_LLM_HOST>:10096/v1
ASR_API_TOKEN=<CUSTOMER_ASR_TOKEN>
LLM_API_KEY=<CUSTOMER_LLM_TOKEN>

如果所有环境统一暴露模型名 business-llm,则 LLM_MODEL 也不需要修改。


三、服务器磁盘问题与目录规划

学校服务器根分区已经接近满载,数据盘 /app/hd/data01 仍有约 11 TB 空间。因此所有个人环境、模型、缓存和项目统一迁移到:

/app/hd/data01/username

目录规划:

/app/hd/data01/username/
├── conda_envs/
│   └── qwen3-asr/
├── conda_pkgs/
├── models/
│   ├── Qwen3-ASR-0.6B/
│   └── Qwen2.5-1.5B-Instruct-GPTQ-Int4/
├── projects/
│   ├── qwen-asr-service/
│   └── qwen-llm-service/
├── huggingface/
├── modelscope/
├── pip_cache/
├── tmp/
└── torch/

写入 ~/.bashrc

export AI_DATA_ROOT="/app/hd/data01/username"
export TMPDIR="$AI_DATA_ROOT/tmp"
export PIP_CACHE_DIR="$AI_DATA_ROOT/pip_cache"
export HF_HOME="$AI_DATA_ROOT/huggingface"
export HUGGINGFACE_HUB_CACHE="$AI_DATA_ROOT/huggingface/hub"
export TRANSFORMERS_CACHE="$AI_DATA_ROOT/huggingface/transformers"
export MODELSCOPE_CACHE="$AI_DATA_ROOT/modelscope"
export TORCH_HOME="$AI_DATA_ROOT/torch"

执行:

source ~/.bashrc

注意事项

  • 不要在根分区继续安装大型模型或 Python 环境;
  • 不要执行 sudo pip install
  • 不要对 /app/hd/data01 整盘递归 chown
  • 只处理个人目录:
sudo chown -R username:username /app/hd/data01/username

四、VS Code Remote-SSH 项目目录切换

项目目录创建在数据盘:

mkdir -p /app/hd/data01/username/projects/qwen-asr-service

VS Code 中执行:

File → Open Folder...

选择:

/app/hd/data01/username/projects/qwen-asr-service

或者在远程终端执行:

code -r /app/hd/data01/username/projects/qwen-asr-service

建议结构:

qwen-asr-service/
├── asr_service.py
├── requirements.txt
├── .env
├── .env.example
├── .gitignore
├── logs/
└── scripts/
    ├── start_asr.sh
    └── test_ws.py

五、Qwen3-ASR 部署

1. 环境与模型目录

Conda 环境:

/app/hd/data01/username/conda_envs/qwen3-asr

模型目录:

/app/hd/data01/username/models/Qwen3-ASR-0.6B

激活环境:

source ~/miniconda3/etc/profile.d/conda.sh
conda activate /app/hd/data01/username/conda_envs/qwen3-asr

2. GPU 选择

使用前检查:

nvidia-smi
nvidia-smi pmon -i <GPU_ID> -c 1

确认进程所属用户:

ps -o user,pid,etime,cmd -p <PID>

不要结束其他用户的任务。本次 ASR 最终使用物理 GPU 2。

注意:

CUDA_VISIBLE_DEVICES=2

后,程序日志里显示 cuda:0 是正常的:

物理 GPU 2 → 进程内逻辑 cuda:0

3. ASR 服务配置

.env 示例:

ASR_MODEL_PATH=/app/hd/data01/username/models/Qwen3-ASR-0.6B
ASR_GPU_MEMORY_UTILIZATION=0.60
ASR_CHUNK_SIZE_SEC=1.0

ASR_HOST=127.0.0.1
ASR_PORT=10095
CUDA_VISIBLE_DEVICES=2

ASR_API_TOKEN=<RANDOM_TOKEN>
PYTHONUNBUFFERED=1

Token 生成:

openssl rand -hex 32

设置权限:

chmod 600 .env

4. 启动脚本

scripts/start_asr.sh

#!/usr/bin/env bash

set -euo pipefail

PROJECT_ROOT="/app/hd/data01/username/projects/qwen-asr-service"
CONDA_ENV="/app/hd/data01/username/conda_envs/qwen3-asr"

cd "$PROJECT_ROOT"
source "/home/username/miniconda3/etc/profile.d/conda.sh"
conda activate "$CONDA_ENV"

if [[ ! -f "$PROJECT_ROOT/.env" ]]; then
    echo "错误:没有找到 $PROJECT_ROOT/.env"
    exit 1
fi

set -a
source "$PROJECT_ROOT/.env"
set +a

mkdir -p "$PROJECT_ROOT/logs"

exec env \
    CUDA_VISIBLE_DEVICES="$CUDA_VISIBLE_DEVICES" \
    PYTHONUNBUFFERED="${PYTHONUNBUFFERED:-1}" \
    python -m uvicorn asr_service:app \
        --host "$ASR_HOST" \
        --port "$ASR_PORT" \
        --workers 1

赋权并启动:

chmod +x scripts/start_asr.sh
./scripts/start_asr.sh

5. 健康检查

curl -sS http://127.0.0.1:10095/health | python -m json.tool

成功结果:

{
  "success": true,
  "service": "qwen3-asr",
  "version": "1.0.0",
  "model_loaded": true,
  "stream_endpoint": "/v1/asr/stream"
}

六、Windows 通过 SSH 隧道访问 ASR

远程服务监听:

127.0.0.1:10095

Windows 本地映射到:

127.0.0.1:18095

命令:

ssh -N `
  -o ExitOnForwardFailure=yes `
  -o ServerAliveInterval=30 `
  -o ServerAliveCountMax=3 `
  -L 127.0.0.1:18095:127.0.0.1:10095 `
  username@<GPU_SERVER_IP>

测试:

Test-NetConnection 127.0.0.1 -Port 18095
curl.exe http://127.0.0.1:18095/health

遇到的问题:Could not resolve hostname app01

原因:

  • app01 是服务器内部主机名;
  • Windows DNS 无法解析;
  • 与项目目录无关。

解决:

  • 使用真实 IP;
  • 或使用 Windows SSH 配置中的真实 Host 别名;
  • 不要把示例名 school-gpu 当成真实地址。

七、Windows ASR 测试

环境变量:

$env:TEST_ASR_URL = "ws://127.0.0.1:18095/v1/asr/stream"
$env:ASR_API_TOKEN = "<ASR_TOKEN>"

测试 WAV:

python .\test_remote_asr.py .\audio\1.wav

成功输出:

READY: {"type":"session.ready", ...}
transcript.partial : ...
transcript.completed : ...

完整链路:

Windows WAV
→ SSH 隧道
→ Qwen3-ASR
→ partial
→ completed

遇到的问题:输出繁体字

示例:

查詢二零二六年七月十六日的生化進水氨氮

处理思路:

  1. 提示词要求输出简体中文;
  2. 服务端使用 OpenCC 进行繁转简;
  3. 术语识别错误与繁简问题分开处理。

例如:

淨水 → 净水

属于繁简转换;

净水 → 进水

属于 ASR 识别错误,需要上下文词表或更好的模型处理。


八、Windows voice_api.py 改造成远程 ASR 代理

原始 /ws/asr

浏览器 PCM
→ DashScope Recognition
→ 阿里云 Fun-ASR

修改后:

浏览器 PCM
→ Windows /ws/asr
→ ASR_REMOTE_WS_URL
→ 远程 Qwen3-ASR

配置:

ASR_REMOTE_WS_URL=ws://127.0.0.1:18095/v1/asr/stream
ASR_API_TOKEN=<ASR_TOKEN>

WebSocket 参数:

ASR_REMOTE_OPEN_TIMEOUT = 10
ASR_REMOTE_PING_INTERVAL = 20
ASR_REMOTE_PING_TIMEOUT = 20
ASR_REMOTE_CLOSE_TIMEOUT = 5

单位均为秒:

  • 连接建立超时 10 秒;
  • 每 20 秒发送心跳;
  • 心跳响应等待 20 秒;
  • 关闭握手等待 5 秒。

这些不是一次语音识别的总时长限制。


九、业务核心接口

真正需要保留:

GET       /health
WebSocket /ws/asr
POST      /api/chat/query

/ws/asr

接收:

session.start
PCM16 二进制音频
session.stop

返回:

session.ready
transcript.partial
transcript.completed
error

/api/chat/query

负责:

识别文字
+ 页面上下文
+ 对话历史
→ Agent
→ 工具调用
→ 总结结果
→ answer_text

旧接口可在完整测试稳定后逐步删除:

/api/asr/transcribe
/api/tts/synthesize
/api/voice/query
/audio

十、小参数 LLM 选型与部署

由于学校服务器下载需要校园网流量,最终选择:

Qwen/Qwen2.5-1.5B-Instruct-GPTQ-Int4

模型目录约 1.1G,权重约 1.07 GiB:

/app/hd/data01/username/models/Qwen2.5-1.5B-Instruct-GPTQ-Int4

选型定位

  • 下载量低;
  • 可复用现有 vLLM 环境;
  • 适合作为链路测试模型;
  • 不适合作为最终生产模型。

不需要重新安装十几 GB 框架

直接复用:

/app/hd/data01/username/conda_envs/qwen3-asr

该环境已有:

PyTorch
CUDA 依赖
vLLM
Transformers

因此只新增模型权重,不再重复下载大型框架。


十一、模型下载问题

1. hf --dry-run 不支持

错误:

hf: error: unrecognized arguments: --dry-run

原因:服务器上的 Hugging Face CLI 版本较旧。

解决:去掉 --dry-run,不为了该参数升级整个环境,避免破坏已运行的 ASR。

2. Hugging Face 无法访问

错误:

OSError: [Errno 101] Network is unreachable

原因:学校服务器无法访问 huggingface.co:443

解决路径:

优先 ModelScope
→ 若仍不可用
→ Windows 下载
→ SCP 上传

3. 缺少 quantize_config.json

检查脚本误判模型不完整。实际上 GPTQ 配置位于:

config.json → quantization_config

因此不需要单独的 quantize_config.json


十二、vLLM LLM 服务部署

LLM 服务监听:

127.0.0.1:10096

Token 保存:

mkdir -p /app/hd/data01/username/projects/qwen-llm-service

openssl rand -hex 32 \
  > /app/hd/data01/username/projects/qwen-llm-service/.llm_token

chmod 600 \
  /app/hd/data01/username/projects/qwen-llm-service/.llm_token

载入 Token:

export LLM_API_TOKEN="$(
  cat /app/hd/data01/username/projects/qwen-llm-service/.llm_token
)"

启动:

MODEL_DIR="/app/hd/data01/username/models/Qwen2.5-1.5B-Instruct-GPTQ-Int4"

CUDA_VISIBLE_DEVICES=<LLM_GPU_ID> \
vllm serve "$MODEL_DIR" \
  --served-model-name business-llm \
  --host 127.0.0.1 \
  --port 10096 \
  --max-model-len 4096 \
  --gpu-memory-utilization 0.25 \
  --api-key "$LLM_API_TOKEN" \
  --enable-auto-tool-choice \
  --tool-call-parser hermes

关键参数:

--served-model-name business-llm

无论实际部署什么模型,对外统一叫 business-llm

问题:自动工具调用未启用

错误:

"auto" tool choice requires
--enable-auto-tool-choice
and
--tool-call-parser

解决:

--enable-auto-tool-choice
--tool-call-parser hermes

问题:模型别名不存在

错误:

The model `business-llm` does not exist.

原因:Windows 请求 business-llm,但 vLLM 暴露的是原始模型名。

推荐解决:重新启动 vLLM,并加入:

--served-model-name business-llm

十三、Windows 同时建立 ASR 与 LLM 隧道

服务器端口:

ASR:10095
LLM:10096

Windows 本地端口:

ASR:18095
LLM:18096

统一 SSH 命令:

ssh -N `
  -o ExitOnForwardFailure=yes `
  -o ServerAliveInterval=30 `
  -o ServerAliveCountMax=3 `
  -L 127.0.0.1:18095:127.0.0.1:10095 `
  -L 127.0.0.1:18096:127.0.0.1:10096 `
  username@<GPU_SERVER_IP>

映射关系:

Windows 18095 → 服务器 10095 → ASR
Windows 18096 → 服务器 10096 → LLM

测试:

Test-NetConnection 127.0.0.1 -Port 18095
Test-NetConnection 127.0.0.1 -Port 18096

十四、Windows LLM 配置

Windows 项目 .env

LLM_BASE_URL=http://127.0.0.1:18096/v1
LLM_API_KEY=<LLM_TOKEN>
LLM_MODEL=business-llm
LLM_TIMEOUT=60

通用客户端:

def get_chat_llm_base_url() -> str:
    base_url = (
        os.environ.get("LLM_BASE_URL")
        or ""
    ).strip()

    if not base_url:
        raise RuntimeError(
            "LLM_BASE_URL is not configured."
        )

    return base_url.rstrip("/")


def get_chat_llm_api_key() -> str:
    return (
        os.environ.get("LLM_API_KEY")
        or "local-llm"
    ).strip()


def get_chat_llm_model() -> str:
    model = (
        os.environ.get("LLM_MODEL")
        or ""
    ).strip()

    if not model:
        raise RuntimeError(
            "LLM_MODEL is not configured."
        )

    return model


def create_chat_llm_client() -> OpenAI:
    return OpenAI(
        api_key=get_chat_llm_api_key(),
        base_url=get_chat_llm_base_url(),
        timeout=float(
            os.environ.get(
                "LLM_TIMEOUT",
                "60",
            )
        ),
    )

/api/chat/query 中使用:

model_client = create_chat_llm_client()

answer_text = await run_in_threadpool(
    run_agent,
    model_client,
    user_text,
    page_context,
    get_chat_llm_model(),
)

十五、.env 没有读取的问题

错误:

RuntimeError: LLM_BASE_URL is not configured.

原因:

  • Uvicorn 进程启动前没有设置环境变量;
  • .env 没有主动加载;
  • PowerShell 中设置变量后没有重启 Uvicorn。

正确做法:

from pathlib import Path
from dotenv import load_dotenv

PROJECT_ROOT = Path(__file__).resolve().parent

load_dotenv(
    PROJECT_ROOT / ".env"
)

注意 import 必须在调用之前。

粘贴代码导致的错误

错误:

FiePROJECT_ROOT = Path(__file__).resolve().parentld,

正确:

from pydantic import BaseModel, Field

PROJECT_ROOT = Path(__file__).resolve().parent

语法检查:

python -m py_compile .\voice_api.py

配置检查:

python -c "
from voice_api import get_chat_llm_base_url, get_chat_llm_model
print(get_chat_llm_base_url())
print(get_chat_llm_model())
"

十六、Windows 中文乱码

现象:

answer_text = æ¨ç...

说明请求已经成功,只是 Windows PowerShell 5.1 解码 UTF-8 时出现乱码。

终端设置:

chcp 65001 > $null

$env:PYTHONUTF8 = "1"
$env:PYTHONIOENCODING = "utf-8"

[Console]::InputEncoding = [System.Text.UTF8Encoding]::new()
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
$OutputEncoding = [System.Text.UTF8Encoding]::new()

发送 UTF-8 请求体:

$json = $payload | ConvertTo-Json -Depth 10

$utf8Body = [System.Text.Encoding]::UTF8.GetBytes(
    $json
)

Invoke-RestMethod `
  -Uri "http://127.0.0.1:8001/api/chat/query" `
  -Method Post `
  -ContentType "application/json; charset=utf-8" `
  -Body $utf8Body

更稳定的方式是使用 Python 请求并显式 decode("utf-8")


十七、小模型效果差的原因

实际前端测试中,模型出现:

  • 用户已经提供完整查询条件,模型仍反复追问;
  • 只会复述需求;
  • 多轮理解较差;
  • 工具参数容易丢失;
  • “生化进水氨氮”缩成“氨氮”;
  • 单日查询未补齐 end_date

原因主要有两类。

1. 模型太小

当前模型:

Qwen2.5-1.5B-Instruct-GPTQ-Int4

定位应是:

链路测试模型

而不是最终生产模型。

2. 对话上下文可能未持续传回

后端的 append_conversation(...) 只更新本次响应中的 context,不会自动永久保存。

前端必须保存后端返回的 result.context,并在下一轮继续发送。不能每次都重新发送:

{
  "conversation": []
}

十八、切回云端 LLM API

当前架构已经是 OpenAI 兼容客户端,因此从本地 vLLM 切回云端 API,只改 .env

本地:

LLM_BASE_URL=http://127.0.0.1:18096/v1
LLM_API_KEY=<LOCAL_TOKEN>
LLM_MODEL=business-llm

云端示例:

LLM_BASE_URL=https://<WORKSPACE>.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
LLM_API_KEY=<CLOUD_API_KEY>
LLM_MODEL=qwen-plus
LLM_TIMEOUT=60

切回云端后:

LLM SSH 隧道不再需要
ASR SSH 隧道继续保留

十九、甲方生产环境建议

学校测试模型只验证:

URL 可替换
OpenAI 协议可替换
SSH/内网链路可替换
模型别名可统一

甲方正式部署建议:

  • 至少 4B~8B 级别指令模型;
  • 优先选择工具调用稳定的模型;
  • 生产环境统一 served-model-name=business-llm
  • 业务后端只读取 URL、Token 和模型名;
  • 生产模型不要直接裸露公网。

示例:

CUDA_VISIBLE_DEVICES=0 \
vllm serve /models/<BETTER_MODEL> \
  --served-model-name business-llm \
  --host 0.0.0.0 \
  --port 10096 \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.60 \
  --enable-auto-tool-choice \
  --tool-call-parser hermes \
  --api-key "$LLM_API_TOKEN"

网络建议:

业务后端与模型同机:127.0.0.1
业务后端与模型分机:内网 IP + 防火墙白名单
跨网络:Nginx / 网关 + TLS + WSS/HTTPS

二十、甲方部署检查清单

A. 硬件检查

nvidia-smi
df -h
free -h

确认:

  • GPU 型号与显存;
  • CUDA 驱动可用;
  • 模型盘空间充足;
  • 根分区不会被模型写满。

B. 目录规划

/data/ai/
├── conda_envs/
├── models/
├── projects/
├── logs/
└── cache/

C. ASR 服务

端口:10095
协议:WebSocket
路径:/v1/asr/stream
健康检查:/health

验证:

curl http://127.0.0.1:10095/health

D. LLM 服务

端口:10096
协议:OpenAI compatible
模型名:business-llm

验证:

curl \
  http://127.0.0.1:10096/v1/models \
  -H "Authorization: Bearer $LLM_API_TOKEN"

E. 业务后端 .env

ASR_REMOTE_WS_URL=ws://<ASR_HOST>:10095/v1/asr/stream
ASR_API_TOKEN=<ASR_TOKEN>

LLM_BASE_URL=http://<LLM_HOST>:10096/v1
LLM_API_KEY=<LLM_TOKEN>
LLM_MODEL=business-llm
LLM_TIMEOUT=60

F. 端到端测试顺序

1. ASR /health
2. LLM /v1/models
3. LLM /v1/chat/completions
4. 业务后端 /health
5. /api/chat/query
6. WebSocket /ws/asr
7. 浏览器麦克风
8. 工具调用
9. 最终回答

二十一、建议补充的 LLM 启动脚本

建议创建:

/app/hd/data01/username/projects/qwen-llm-service/start_llm.sh

示例:

#!/usr/bin/env bash

set -euo pipefail

CONDA_ENV="/app/hd/data01/username/conda_envs/qwen3-asr"
MODEL_DIR="/app/hd/data01/username/models/Qwen2.5-1.5B-Instruct-GPTQ-Int4"
TOKEN_FILE="/app/hd/data01/username/projects/qwen-llm-service/.llm_token"

source "/home/username/miniconda3/etc/profile.d/conda.sh"
conda activate "$CONDA_ENV"

export LLM_API_TOKEN="$(cat "$TOKEN_FILE")"

exec env CUDA_VISIBLE_DEVICES="<LLM_GPU_ID>" \
vllm serve "$MODEL_DIR" \
  --served-model-name business-llm \
  --host 127.0.0.1 \
  --port 10096 \
  --max-model-len 4096 \
  --gpu-memory-utilization 0.25 \
  --api-key "$LLM_API_TOKEN" \
  --enable-auto-tool-choice \
  --tool-call-parser hermes

赋权:

chmod +x start_llm.sh

二十二、今日最终成果

今天已经完成:

  • 数据盘目录规划;
  • Conda 环境、模型和缓存迁移到大容量数据盘;
  • Qwen3-ASR 本地服务部署;
  • 自定义 ASR WebSocket 协议;
  • Windows SSH 隧道连接;
  • Windows WAV 远程识别测试;
  • voice_api.py 从 DashScope 实时 ASR 改为远程 ASR 代理;
  • Qwen2.5 1.5B GPTQ Int4 模型下载;
  • vLLM OpenAI 兼容 LLM 服务部署;
  • 自动工具调用启用;
  • business-llm 统一模型别名;
  • Windows LLM 隧道;
  • /api/chat/query 完整链路验证;
  • 浏览器端 ASR → LLM → Agent → 回答链路验证;
  • 云端 API 与本地 LLM 的可切换配置。

最终验证的核心架构:

浏览器
→ Windows FastAPI
→ 远程 ASR URL
→ 远程 LLM URL
→ 工具调用
→ 前端回答

后续到甲方服务器时,不需要重新设计业务系统,只需要:

部署更好的 ASR/LLM 模型
→ 保持相同协议
→ 修改 .env
→ 重启服务
→ 执行验收测试

二十三、下一次部署时最关键的五条经验

  1. 模型、环境、缓存全部放数据盘,不要放根分区。
  2. ASR 与 LLM 对外协议固定,业务代码只读取 URL。
  3. vLLM 模型统一暴露为 business-llm
  4. 小模型只做链路测试,甲方验收必须换更好的模型。
  5. 先逐层测试,再测浏览器完整链路。

逐层顺序:

端口
→ 健康检查
→ 模型接口
→ 后端接口
→ WebSocket
→ 浏览器
→ 工具调用
→ 最终回答
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇