魔搭 ms-swift 框架

发布于 2025-8-22 08:41
浏览
0收藏

一、什么是 ms-swift?为什么选它?

二、核心能力:这些技术让微调效率翻倍

2.1 轻量训练技术全家桶

2.2 人类偏好对齐训练方法

2.3 推理与量化加速

三、5 分钟上手

3.1 安装

3.2 环境要求

3.3 10 分钟微调实战:以 Qwen2.5-7B 为例

3.3.1 命令行方式

3.3.2 训练后推理

3.3.3 模型推送到ModelScope3

3.3.4 零代码微调:Web-UI 界面

3.3.5 使用python训练和推理

四、从训练到部署完整流程

4.1 支持的训练方法

4.2 预训练

4.2 微调

4.3 RLHF

4.4 推理

4.5 界面推理

4.6 部署

4.7 采样

4.8 评测

4.9 量化

4.10 模型推送

五、参数

六、接入SwanLab

6.1 安装swanlab

6.2 CLI微调

6.3 WebUI微调

6.4 Python代码微调

如何让通用大模型快速适配特定场景?如何用有限硬件资源实现高效微调?今天给大家推荐一款由魔搭社区打造的轻量级微调神器 ——ms-swift,它能帮你搞定从训练到部署的全流程,哪怕是新手也能快速上手!

一、什么是 ms-swift?为什么选它?

ms-swift(Scalable lightWeight Infrastructure for FineTuning)是魔搭社区推出的大模型与多模态大模型微调部署框架,简单来说,它是一款能让你 “花小钱办大事” 的大模型工具集。

为什么推荐它?这几个核心优势太强了:

  • 模型覆盖超全:支持 450 + 纯文本大模型(如 Qwen2.5、InternLM3、GLM4、Llama3.3 等)和 150 + 多模态大模型(如 Qwen2.5-VL、Llava、InternVL2.5 等),从文本到图文、语音全场景覆盖。
  • 全流程一站通:从预训练、微调、人类对齐(RLHF)到推理、评测、量化、部署,无需切换工具,一套框架走到底。
  • 轻量训练省资源:集成了 LoRA、QLoRA、DoRA、GaLore 等轻量化训练技术,小显卡也能微调大模型(比如单卡 3090 就能搞定 7B 模型)。
  • 硬件兼容性强:支持 CPU、RTX 系列、A100/H100、Ascend NPU、MPS 等各类硬件,不用为设备发愁。
  • 开箱即用门槛低:提供命令行、Web-UI、Python 三种使用方式,零代码也能上手。

二、核心能力:这些技术让微调效率翻倍

ms-swift 之所以强大,离不开对前沿技术的集成,无论是训练、推理还是量化,都有 “黑科技” 加持:

2.1 轻量训练技术全家桶

无需全量参数训练,用少量资源就能优化模型:

  • LoRA/QLoRA:参数高效微调,显存占用直降 90%;
  • DoRA/GaLore:进一步提升微调效果,兼顾效率与性能;
  • UnSloth/Liger:加速训练过程,让单卡训练更快完成。

2.2 人类偏好对齐训练方法

让模型输出更符合人类偏好:

  • 支持 DPO/GRPO/ORPO 等主流对齐算法;
  • 适配纯文本和多模态模型,对话、工具调用场景都能用。

2.3 推理与量化加速

模型训练完,部署效率也不能落下:

  • 推理加速:集成 vLLM、LMDeploy 引擎,生成速度提升数倍;
  • 量化技术:支持 GPTQ、AWQ、BNB 等量化方法,模型体积减半,精度损失小。

三、5 分钟上手

3.1 安装

  • pip 一键安装:

pip install ms-swift
  • 源码安装(适合开发者):

# pip install git+https://github.com/modelscope/ms-swift.git


git clone https://github.com/modelscope/ms-swift.git
cd ms-swift
pip install -e .

3.2 环境要求


范围

推荐

备注

python

>=3.9

3.10


cuda


cuda12

使用cpu、npu、mps则无需安装

torch

torch>=2.0



transformers

>=4.33

4.50


modelscope

>=1.19



peft

>=0.11,<0.16



trl

>=0.13,<0.17

0.16

RLHF

deepspeed

>=0.14

0.14.5

训练

vllm

>=0.5.1,<0.8

0.7.3

推理/部署/评测

lmdeploy

>=0.5

0.7.2.post1

推理/部署/评测

evalscope

>=0.11


评测

更多依赖参考 ​​https://github.com/modelscope/ms-swift/blob/main/requirements/install_all.sh​

3.3 10 分钟微调实战:以 Qwen2.5-7B 为例

用单卡 3090(22GB 显存)就能搞定自我认知微调,步骤超简单:

3.3.1 命令行方式

# 22GB
CUDA_VISIBLE_DEVICES=0 \
swift sft \
    --model Qwen/Qwen2.5-7B-Instruct \
    --train_type lora \
    --dataset 'AI-ModelScope/alpaca-gpt4-data-zh#500' \
              'AI-ModelScope/alpaca-gpt4-data-en#500' \
              'swift/self-cognition#500' \
    --torch_dtype bfloat16 \
    --num_train_epochs 1 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --learning_rate 1e-4 \
    --lora_rank 8 \
    --lora_alpha 32 \
    --target_modules all-linear \
    --gradient_accumulation_steps 16 \
    --eval_steps 50 \
    --save_steps 50 \
    --save_total_limit 5 \
    --logging_steps 5 \
    --max_length 2048 \
    --output_dir output \
    --system 'You are a helpful assistant.' \
    --warmup_ratio 0.05 \
    --dataloader_num_workers 4 \
    --model_author swift \
    --model_name swift-robot

3.3.2 训练后推理

使用训练后的权重推理命令:

# 交互式推理
CUDA_VISIBLE_DEVICES=0 \
swift infer \
    --adapters output/vx-xxx/checkpoint-xxx \
    --stream true \
    --temperature 0 \
    --max_new_tokens 2048

# merge-lora并使用vLLM进行推理加速
CUDA_VISIBLE_DEVICES=0 \
swift infer \
    --adapters output/vx-xxx/checkpoint-xxx \
    --stream true \
    --merge_lora true \
    --infer_backend vllm \
    --max_model_len 8192 \
    --temperature 0 \
    --max_new_tokens 2048

adapters文件夹中包含训练的参数文件​​args.json​​​,不需要额外指定​​​--model​​​,​​​--system​​​,swift会自动读取这些参数。如果要关闭此行为,可以设置​​​--load_args false​​​。

3.3.3 模型推送到ModelScope

CUDA_VISIBLE_DEVICES=0 \
swift export \
    --adapters output/vx-xxx/checkpoint-xxx \
    --push_to_hub true \
    --hub_model_id '<your-model-id>' \
    --hub_token '<your-sdk-token>' \
    --use_hf false

3.3.4 零代码微调:Web-UI 界面

Web-UI是基于gradio界面技术的零门槛训练、部署方案。更多信息可以查看 ​​https://swift.readthedocs.io/zh-cn/latest/GetStarted/Web-UI.html​

只需一行命令启动可视化界面,点点鼠标就能训练:

swift web-ui

3.3.5 使用python训练和推理

ms-swift也支持使用python的方式进行训练和推理。下面给出训练和推理的关键代码,更多详情可以查看 ​​https://github.com/modelscope/ms-swift/blob/main/examples/notebook/qwen2_5-self-cognition/self-cognition-sft.ipynb​

训练

# 获取模型和template,并加入可训练的LoRA模块
model, tokenizer = get_model_tokenizer(model_id_or_path, ...)
template = get_template(model.model_meta.template, tokenizer, ...)
model = Swift.prepare_model(model, lora_config)


# 下载并载入数据集,并将文本encode成tokens
train_dataset, val_dataset = load_dataset(dataset_id_or_path, ...)
train_dataset = EncodePreprocessor(template=template)(train_dataset, num_proc=num_proc)
val_dataset = EncodePreprocessor(template=template)(val_dataset, num_proc=num_proc)


# 进行训练
trainer = Seq2SeqTrainer(
    model=model,
    args=training_args,
    data_collator=template.data_collator,
    train_dataset=train_dataset,
    eval_dataset=val_dataset,
    template=template,
)
trainer.train()

推理

# 使用原生pytorch引擎进行推理
engine = PtEngine(model_id_or_path, adapters=[lora_checkpoint])
infer_request = InferRequest(messages=[{'role': 'user', 'content': 'who are you?'}])
request_config = RequestConfig(max_tokens=max_new_tokens, temperature=temperature)


resp_list = engine.infer([infer_request], request_config)
print(f'response: {resp_list[0].choices[0].message.content}')

四、从训练到部署完整流程

完整流程从训练到部署demo可查看 ​​https://github.com/modelscope/ms-swift/tree/main/examples​

4.1 支持的训练方法

魔搭 ms-swift 框架-AI.x社区

4.2 预训练

# 8*A100
NPROC_PER_NODE=8 \
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
swift pt \
    --model Qwen/Qwen2.5-7B \
    --dataset swift/chinese-c4 \
    --streaming true \
    --train_type full \
    --deepspeed zero2 \
    --output_dir output \
    --max_steps 100000 \
    ...

4.2 微调

CUDA_VISIBLE_DEVICES=0 swift sft \
    --model Qwen/Qwen2.5-7B-Instruct \
    --dataset AI-ModelScope/alpaca-gpt4-data-zh \
    --train_type lora \
    --output_dir output \
    ...

4.3 RLHF

CUDA_VISIBLE_DEVICES=0 swift rlhf \
    --rlhf_type dpo \
    --model Qwen/Qwen2.5-7B-Instruct \
    --dataset hjh0119/shareAI-Llama3-DPO-zh-en-emoji \
    --train_type lora \
    --output_dir output \
    ...

4.4 推理

CUDA_VISIBLE_DEVICES=0 swift infer \
    --model Qwen/Qwen2.5-7B-Instruct \
    --stream true \
    --infer_backend pt \
    --max_new_tokens 2048


# LoRA
CUDA_VISIBLE_DEVICES=0 swift infer \
    --model Qwen/Qwen2.5-7B-Instruct \
    --adapters swift/test_lora \
    --stream true \
    --infer_backend pt \
    --temperature 0 \
    --max_new_tokens 2048

4.5 界面推理

CUDA_VISIBLE_DEVICES=0 swift app \
    --model Qwen/Qwen2.5-7B-Instruct \
    --stream true \
    --infer_backend pt \
    --max_new_tokens 2048 \
    --lang zh

4.6 部署

CUDA_VISIBLE_DEVICES=0 swift deploy \
    --model Qwen/Qwen2.5-7B-Instruct \
    --infer_backend vllm

4.7 采样

CUDA_VISIBLE_DEVICES=0 swift sample \
    --model LLM-Research/Meta-Llama-3.1-8B-Instruct \
    --sampler_engine pt \
    --num_return_sequences 5 \
    --dataset AI-ModelScope/alpaca-gpt4-data-zh#5

4.8 评测

CUDA_VISIBLE_DEVICES=0 swift eval \
    --model Qwen/Qwen2.5-7B-Instruct \
    --infer_backend lmdeploy \
    --eval_backend OpenCompass \
    --eval_dataset ARC_c

4.9 量化

CUDA_VISIBLE_DEVICES=0 swift export \
    --model Qwen/Qwen2.5-7B-Instruct \
    --quant_bits 4 --quant_method awq \
    --dataset AI-ModelScope/alpaca-gpt4-data-zh \
    --output_dir Qwen2.5-7B-Instruct-AWQ

4.10 模型推送

swift export \
    --model <model-path> \
    --push_to_hub true \
    --hub_model_id '<model-id>' \
    --hub_token '<sdk-token>'

五、 参数

  • num_train_epochs

定义:训练的 epoch 数(默认值为 3)。1 个 epoch 指模型完整遍历一次训练数据集

示例:若有 1000 条训练样本,​​num_train_epochs=3​​ 表示模型会遍历数据 3 次

作用:逐步优化模型参数,提升拟合能力

注意:过多 epoch 可能导致过拟合(模型记住训练数据而非泛化)

  • split_dataset_ratio

定义:训练集与验证集的划分比例(训练集占比)

示例:​​split_dataset_ratio=0.8​​ 表示 1000 条样本中,800 条用于训练,200 条用于验证

作用:区分训练数据(模型学习)和验证数据(调参、选最优模型),部分场景含测试集(最终评估)

  • per_device_train_batch_size

定义:每个设备的训练批量大小(GRPO 中特指 completion 批次大小)

总批量计算:​​总 batch size = per_device_train_batch_size × 设备数量​​(如多 GPU 场景)

示例:​​per_device_train_batch_size=16​​ 表示单设备每次用 16 个样本更新参数,2 个 GPU 总批量为 32

影响与建议如下表:

方面

影响 / 建议

训练速度

批量越大,硬件并行能力利用率越高,训练越快。

内存占用

批量过大会导致 OOM(显存不足),建议从小值(2→4→8)逐步尝试。

模型效果

太小易导致梯度噪声大(不稳定),太大可能降低泛化能力。

场景适配

微调大模型推荐 1~8(配合梯度累积);多卡训练可适当放大;BatchNorm 任务不宜过小。

  • gradient_accumulation_steps

定义:参数更新前累积的 batch 数量(模拟大批量训练,不增加显存占用)

示例:​​per_device_train_batch_size=4 + gradient_accumulation_steps=8​​ 等效总批量为 32(显存仅占 4 样本的量)

推荐值如下表:

硬件场景(单卡)

推荐值

A10/RTX 3090/L40(24G)

1~4

T4/V100(16G)

4~8

RTX 3060/2080(12G)

8~16

显存紧张

32 或更高(牺牲速度)

  • learning_rate

学习率大小

效果

问题

太大

更新快

易跳过最优解,导致震荡 / 发散

太小

稳定但慢

收敛慢,可能陷入局部最小值

推荐值:

定义:参数更新的 “力度”(梯度调整系数),公式为 ​​newParameter = oldParameter - learningRate × gradient​

影响:

任务类型

推荐学习率

图像分类(SGD+momentum)

0.1(配合衰减策略)

图像分类(Adam)

3e-4(0.0003)

NLP(Transformer 微调)

2e-5 ~ 5e-5

强化学习

1e-4 ~ 1e-5

大模型微调

2e-5 ~ 5e-5

  • warmup_ratio

定义:学习率预热阶段占总训练步数的比例(预热阶段学习率从 0 逐步增至目标值)

公式:​​num_warmup_steps = warmup_ratio × total_training_steps​​(总步数 = epoch 数 × 每 epoch 批次数)

作用:避免初始随机权重因大学习率导致的训练不稳定

推荐范围:0.01 ~ 0.2

  • lora_rank

秩越小:参数越少,内存占用低,但拟合能力有限;

秩越大:拟合能力强,但易过拟合

定义:LoRA 低秩矩阵的秩,控制适配矩阵的复杂度与表达能力

影响:

推荐值:

模型规模

推荐值

小模型(<1B 参数)

8 ~ 64

中等模型(1B~10B)

8 ~ 32

大模型(>10B)

8 ~ 16

  • lora_alpha

默认:​​lora_alpha = 2 × lora_rank​​(如 8→16、16→32);

小模型:8 ~ 32;大模型:16 ~ 64。

定义:LoRA 适配矩阵的缩放因子,公式为 ​​ΔW = (α/r) × A×B^T​​(r 为 lora_rank)

作用:放大低秩矩阵的影响(如 ​​lora_rank=8 + lora_alpha=16​​ 时缩放因子为 2)

推荐值:

  • use_chat_template

[
 {"role": "user", "content": "介绍一下你自己"},
 {"role": "assistant", "content": "我是 InternLM,由上海人工智能实验室开发的语言模型..."},
 {"role": "user", "content": "你能帮我写一篇关于夏天的文章吗?"}
]

<|User|>: 介绍一下你自己
<|Assistant|>: 我是 InternLM...
<|User|>: 你能帮我写一篇关于夏天的文章吗?

如果启用了 ​​use_chat_template = true​​,系统会自动将这段对话转换为类似下面的字符串:

定义:决定是否在推理或训练过程中自动应用模型内置的“对话格式模板”来组织输入文本

值:true 和 false

例如,你传入如下对话历史:

  • target_modules

LLM 中自动选择除 ​​lm_head​​ 外的线性层并附加tuner;

多模态 LLM 默认仅在 LLM 附加调优器,可通过 ​​freeze_llm​​​ 、​​​freeze_vit​​​、​​​freeze_aligner​​​等参数控制

  • 定义:指定接入 LoRA 或其他调优器的模块(默认 ​​all-linear​​)
  • 行为:
  • save_steps

定义:每隔多少步保存一次模型状态(权重、优化器等,默认 500)

作用:支持中断后从最近 checkpoint 续训,便于筛选最优模型

推荐值:

场景

推荐值

小数据集 / 快速实验

50 ~ 100

中等规模训练

500 ~ 1000

大规模预训练

1000 ~ 10000

  • save_total_limit

定义:最多保留的模型 checkpoint 数量(超出自动删除较早文件)

作用:节省磁盘空间,例如 ​​save_total_limit=2​​ 仅保留最近 2 个 checkpoint

  • logging_steps

定义:每隔多少步打印训练状态(loss、学习率等,默认 5)

作用:实时监控训练进度,辅助调试模型表现

  • gradient_checkpointing_kwargs

显存不足时启用;

新版推荐 ​​use_reentrant: false​​(效率更高);

老模型兼容用 ​​use_reentrant: true​​;

大模型微调强烈建议开启

定义:梯度检查点的高级配置(默认 None),用于节省显存

原理:前向传播仅保留部分激活值,反向传播时重新计算,以时间换内存

建议:

  • dataloader_num_workers

定义:PyTorch DataLoader 用于数据加载的子进程数量

作用:并行处理数据读取、预处理等任务,加速数据供给主训练进程

六、接入SwanLab

6.1 安装swanlab

pip install swanlab

6.2 CLI微调

只需要在ms-swift的CLI中添加​​--report_to​​​和​​​--swanlab_project​​​两个参数,即可使用SwanLab进行实验跟踪与可视化:

swift sft \
    ...
    --report_to swanlab \ 
    --swanlab_project swift-robot \ 
    ...

运行指令后,可以在SwanLab看到训练过程:

魔搭 ms-swift 框架-AI.x社区

支持的完整参数:

  • ​swanlab_token​​: SwanLab的api-key
  • ​swanlab_project​​: swanlab的project
  • ​swanlab_workspace​​: 默认为None,会使用api-key对应的username
  • ​swanlab_exp_name​​: 实验名,可以为空,为空时默认传入--output_dir的值
  • ​swanlab_mode​​: 可选cloud和local,云模式或者本地模式

6.3 WebUI微调

启动WebUI

swift web-ui

启动后,会自动打开浏览器,显示微调界面(或者访问 ​​http://localhost:7860/​​ ):

魔搭 ms-swift 框架-AI.x社区

在下方的「训练记录」模块中,在​​训练记录方式​​​部分选择​​​swanlab​​​:

魔搭 ms-swift 框架-AI.x社区

还可以在「训练记录」模块的其他填写更细致的swanlab参数,包括:

  • ​swanlab_token​​: SwanLab的api-key
  • ​swanlab_project​​: swanlab的project
  • ​swanlab_workspace​​: 默认为None,会使用api-key对应的username
  • ​swanlab_exp_name​​: 实验名,可以为空,为空时默认传入--output_dir的值
  • ​swanlab_mode​​: 可选cloud和local,云模式或者本地模式

6.4 Python代码微调

这部分更详细的可以查阅 ​​https://docs.swanlab.cn/guide_cloud/integration/integration-swift.html#_3-python%E4%BB%A3%E7%A0%81%E5%BE%AE%E8%B0%83​

引入SwanLabCallback

from swanlab.integration.transformers import SwanLabCallback

引入Trainer

from swanlab.integration.transformers import SwanLabCallback
from swift import Seq2SeqTrainer, Seq2SeqTrainingArguments


···


#实例化SwanLabCallback
swanlab_callback = SwanLabCallback(project="swift-visualization")


trainer = Seq2SeqTrainer(
    ...
    callbacks=[swanlab_callback],
    )


trainer.train()

使用SwanLabCallback

# Lora微调一个Qwen2-0.5B模型


from swanlab.integration.transformers import SwanLabCallback
from swift import Seq2SeqTrainer, Seq2SeqTrainingArguments
from swift.llm import get_model_tokenizer, load_dataset, get_template, EncodePreprocessor
from swift.utils import get_logger, find_all_linears, get_model_parameter_info, plot_images, seed_everything
from swift.tuners import Swift, LoraConfig
from swift.trainers import Seq2SeqTrainer, Seq2SeqTrainingArguments
from functools import partial
import os


logger = get_logger()
seed_everything(42)


# Hyperparameters for training
# model
model_id_or_path = 'Qwen/Qwen2.5-3B-Instruct'  # model_id or model_path
system = 'You are a helpful assistant.'
output_dir = 'output'


# dataset
dataset = ['AI-ModelScope/alpaca-gpt4-data-zh#500', 'AI-ModelScope/alpaca-gpt4-data-en#500',
           'swift/self-cognition#500']  # dataset_id or dataset_path
data_seed = 42
max_length = 2048
split_dataset_ratio = 0.01  # Split validation set
num_proc = 4  # The number of processes for data loading.
# The following two parameters are used to override the placeholders in the self-cognition dataset.
model_name = ['小黄', 'Xiao Huang']  # The Chinese name and English name of the model
model_author = ['魔搭', 'ModelScope']  # The Chinese name and English name of the model author


# lora
lora_rank = 8
lora_alpha = 32


# training_args
training_args = Seq2SeqTrainingArguments(
    output_dir=output_dir,
    learning_rate=1e-4,
    per_device_train_batch_size=1,
    per_device_eval_batch_size=1,
    gradient_checkpointing=True,
    weight_decay=0.1,
    lr_scheduler_type='cosine',
    warmup_ratio=0.05,
    logging_first_step=True,
    save_strategy='steps',
    save_steps=50,
    eval_strategy='steps',
    eval_steps=50,
    gradient_accumulation_steps=16,
    num_train_epochs=1,
    metric_for_best_model='loss',
    save_total_limit=5,
    logging_steps=5,
    dataloader_num_workers=1,
    data_seed=data_seed,
)


output_dir = os.path.abspath(os.path.expanduser(output_dir))
logger.info(f'output_dir: {output_dir}')


# Obtain the model and template, and add a trainable Lora layer on the model.
model, tokenizer = get_model_tokenizer(model_id_or_path)
logger.info(f'model_info: {model.model_info}')
template = get_template(model.model_meta.template, tokenizer, default_system=system, max_length=max_length)
template.set_mode('train')


target_modules = find_all_linears(model)
lora_config = LoraConfig(task_type='CAUSAL_LM', r=lora_rank, lora_alpha=lora_alpha,
                         target_modules=target_modules)
model = Swift.prepare_model(model, lora_config)
logger.info(f'lora_config: {lora_config}')


# Print model structure and trainable parameters.
logger.info(f'model: {model}')
model_parameter_info = get_model_parameter_info(model)
logger.info(f'model_parameter_info: {model_parameter_info}')


# Download and load the dataset, split it into a training set and a validation set,
# and encode the text data into tokens.
train_dataset, val_dataset = load_dataset(dataset, split_dataset_ratio=split_dataset_ratio, num_proc=num_proc,
        model_name=model_name, model_author=model_author, seed=data_seed)


logger.info(f'train_dataset: {train_dataset}')
logger.info(f'val_dataset: {val_dataset}')
logger.info(f'train_dataset[0]: {train_dataset[0]}')


train_dataset = EncodePreprocessor(template=template)(train_dataset, num_proc=num_proc)
val_dataset = EncodePreprocessor(template=template)(val_dataset, num_proc=num_proc)
logger.info(f'encoded_train_dataset[0]: {train_dataset[0]}')


# Print a sample
template.print_inputs(train_dataset[0])


# Get the trainer and start the training.
model.enable_input_require_grads()  # Compatible with gradient checkpointing


swanlab_callback = SwanLabCallback(
    project="swift-visualization",
    experiment_name="lora-qwen2-0.5b",
    descriptinotallow="Lora微调一个Qwen2-0.5B模型"
)


trainer = Seq2SeqTrainer(
    model=model,
    args=training_args,
    data_collator=template.data_collator,
    train_dataset=train_dataset,
    eval_dataset=val_dataset,
    template=template,
    callbacks=[swanlab_callback],
)
trainer.train()


last_model_checkpoint = trainer.state.last_model_checkpoint
logger.info(f'last_model_checkpoint: {last_model_checkpoint}')

运行可视化结果:

魔搭 ms-swift 框架-AI.x社区

笔者能力有限,欢迎批评指正或者在留言区讨论

参考:

本文转载自​鸿煊的学习笔记​,作者:乘风破浪jxj


已于2025-8-22 10:27:08修改
收藏
回复
举报
回复
相关推荐