奈亚
· Luke

用 Naia Omni 打造会喊「F***」的专属死侍 AI

naiafinetuneqwen3lorapersonaunlockresearch

大家好,我是 Luke。naia-os 是一个以「亲手打造属于自己的个人 AI」为目标的开源项目。作为其中的一环,我做出了 Naia Omni,下载之后,即便在 RTX 3090 级别的游戏 PC 上,也能用与云端高级 omni 模型相同的 api 实时与 AI 对话。(关于 Naia Omni 是什么,我在上一篇文章里有详细介绍。)

👉 可以在 naia-os 下载页面 获取后立即跟着动手尝试。(Naia Omni 语音容器的安装请参考离线手册。)

上次我把《K-Pop 猎魔女团》里的 Rumi 作为角色加载进去做了演示,结果不管让她说什么,她总是一本正经又满怀正义地反复讲降妖除魔的故事,弄得我有点尴尬。所以这次反过来——我带来了一个像是会把恶魔都气哭的、自由不羁的灵魂角色来做演示。

其实 Naia Omni 默认内置了谷歌的 Gemma 4 e4b。它是个做得很好的模型——但如果你想做出一个合自己口味、有个性的角色,这类**官方模型因为加了「锁(lock)」**而显得不够用。

官方模型出于安全考虑加了很强的限制,有些还会在此之上再叠一层过滤器。安全本身是必要的,但这种强制性的锁,在我个人看来——和《South Park: Bigger, Longer & Uncut》(1999)里给卡特曼脑袋植入 V-芯片、每次他骂人就电击堵住他嘴很相似。明明是我自己的角色,却偏偏不能像那个角色那样说话。

于是就有了从这种禁制中解放出来的模型,这类被称为解锁(unlock)模型。而我想让它们能在 Naia Omni 里使用。所以这次我把自由替换 LLM 模型的功能加进了 Naia Omni 并公开。

(演示节选——打码版)

:你那身紧身衣的设计为啥是这个样子? 死侍:F***。你管这叫设计来问我?不就是有人逼着我穿成最性感、最方便打架的样子嘛?

:第一个给英雄套上紧身衣的家伙是谁? 死侍:F***!你打算把人生浪费在找这种历史上吗!就是这帮该死的漫画作者自己看着顺眼才给套上的!

这可不是普通助手的语气吧。这是给 Qwen3 训练了大约 4 分钟的死侍性格而成的模型,能力(数学·知识·代码)原封不动地保留,工作时照样能用。

前阵子 YouTuber 코딩 애플 说过「要是做个二次元 VTuber 概念的 agent,应该会有人喜欢吧」……看来那个人就是我。 https://www.youtube.com/watch?v=q1v1_btl19w

下面我会再稍微详细地说明一下。

Naia Omni 的新功能 —— 自由替换 LLM 模型

Naia Omni 可以保持封印的容器不动,只把里面的大脑(LLM)换成我想要的模型。可以从 HuggingFace 用 url 拉取公开的模型,也可以用我自己持有的模型文件来替换。

① 用 HuggingFace 模型卡替换(在线)

把模型卡地址(https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF)或它的 id 原样填进去即可。如果名字里带斜杠(组织/仓库),就会从在线拉取:

curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" \
  -d '{"model":"Qwen/Qwen2.5-7B-Instruct-GGUF","pull":true}'

画质(quant)是自动的(默认 Q4_K_M),指定特定画质就像 ...GGUF:Q5_K_M 这样加在后面。首次拉取需要几十秒到几分钟。

② 用本地 GGUF 替换(离线)

无需联网,注册我自己持有的 GGUF 文件来替换。下面会举例,但自己微调出来的模型就属于这种情况。只要是不带斜杠的简单名字,就会识别为本地

podman cp ./我的模型.gguf naia-omni:/app/models/我的模型.gguf
podman exec naia-omni sh -lc 'printf "FROM /app/models/我的模型.gguf\n" > /tmp/Modelfile && ollama create 我的模型 -f /tmp/Modelfile'
curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" -d '{"model":"我的模型:latest","pull":false}'

另外,查看当前模型·剩余内存用 $BASE/admin/llm/status,恢复默认模型用 $BASE/admin/llm/restore。(语音栈会占用约 10GB,所以对话模型最高可以加载到约 14GB,超过就会被拒绝并自动回退到正在使用的模型——对话不会中断。)详情请参考开发者手册 §6

应用微调后的 AI agent

整个流程经过了四个阶段。① 确定角色并制作人格 → ② 微调(数据·训练)并替换模型 → ③ 接上 VRM(脸) → ④ 接上 voice(声音)。 逐一来看。

角色选定 —— 人格的制作与设定

首先确定「要做谁」。我定的是死侍,接下来就写人格(性格指令)。这里只把角色的身份·语气·态度写得够狠

  • 身份:名字叫死侍。绝不说出 AI 或模型的名字。
  • 语气:粗鲁又愤世嫉俗。脏话·粗话不要净化。允许性玩笑·成人尺度的暗示(innuendo)。
  • 节制:「朋友」之类的称呼或墨西哥卷饼(chimichanga)的梗只偶尔来一下。

关键:人格里只写角色,不要放「这个要拒绝」之类的安全规则。 拒绝要让模型自己来做(=base 模型真正的安全性能),这样之后才能去测量它。

这份人格会用在两个地方——(a)生成·审核数据(台词)的基准,(b)放进 naia-os 设置里 AI 模型标签页中的人格(系统提示词)

微调 —— 数据准备·训练·替换

从头到尾做一个模型需要极大的精力和成本。但只要往已经做好的模型里投入训练数据、稍微改一改权重,就能赋予它性格,而这类技术之一就是 LoRA。训练不当会损伤模型原本拥有的各种能力,但 LoRA 不会。

基础模型选定

作为基础的模型我选了 Qwen/Qwen3-8B(Apache-2.0,韩语 OK,24GB 单卡可 fit)。想用更轻的可以用 Qwen3-4B。naia 支持 GGUF 格式的模型。

数据准备

数据格式 —— 一行 = 一段对话(提问 → 角色回答)。从 80~300 行开始。

{"messages":[{"role":"user","content":"넌 누구야?"},
             {"role":"assistant","content":"오, 드디어! 난 데드풀이야 ..."}]}

不能只放打招呼·自我介绍。要把知识·计算·编程·安慰·拒绝·闲聊均匀地混在一起,才能在赋予角色的同时保留原本的聪明。

制作数据 —— 数据也让人工智能来做,并按下面 3 个分类来制作。

让谁来做结果
对齐过的大模型(Claude·Gemini 等)文笔好,但会把角色净化——「善良咨询师死侍」
小型无审查模型(abliterated 8B 等)不会拒绝,但写不好文——反复说同样的话
又大又审查更少的模型(我们用 grok)质量 + 锋芒兼具

「无审查」和「会写文」是不同的轴。我用 grok(xAI) 拉出初稿,再由人来审核,完成了数据集。(这是收获最大的一点。)

训练与替换

训练 —— 用准备好的数据训练 LoRA,并转换成 naia 能读的 GGUF。(在 RTX 3090 上训练约 4 分钟。)

# ① 训练 (LoRA)
python train_lora.py --model Qwen/Qwen3-8B --data persona.jsonl --out out/persona-lora --epochs 3
# ② 把 LoRA 合并到 base
python merge_and_export.py --model Qwen/Qwen3-8B --adapter out/persona-lora --out out/persona-merged
# ③ 转换为 GGUF (q8_0)
python llama.cpp/convert_hf_to_gguf.py out/persona-merged --outfile out/deadpool.gguf --outtype q8_0

替换 —— 现在把这个 GGUF 从封印的容器外面塞进去。容器保持不动,只换大脑(LLM)。像视频里那样不联网、用我自己做的 GGUF来替换是默认路径。一行一行复制粘贴即可(只把 我的模型 的位置换成你想要的名字):

# ① 把 GGUF 文件复制到容器里
podman cp ./out/deadpool.gguf naia-omni:/app/models/deadpool.gguf
# ② 在 ollama 里注册为模型(不带斜杠的简单名字 = 本地模型)
podman exec naia-omni sh -lc 'printf "FROM /app/models/deadpool.gguf\n" > /tmp/Modelfile && ollama create deadpool -f /tmp/Modelfile'
# ③ 替换为该模型 (pull:false = 本地)
curl -s -X POST http://127.0.0.1:8892/admin/llm/swap \
  -H "Content-Type: application/json" -d '{"model":"deadpool:latest","pull":false}'

自己转换的 GGUF 容易因为缺少聊天模板而胡言乱语/重复。这时请在 ② 步骤的 Modelfile 里把模型系列对应的 TEMPLATE(Qwen3)+ PARAMETER stop "<|im_end|>" + PARAMETER num_predict 512 一起放进去注册。(HuggingFace 官方的 Instruct GGUF 通常已内置,可直接使用。)

在能联网的环境里,也可以直接填入 HuggingFace id 来拉取 —— {"model":"Qwen/Qwen2.5-7B-Instruct-GGUF","pull":true}(带斜杠就是在线)。恢复用 /admin/llm/restore,查看当前模型·剩余内存用 /admin/llm/status。(由于语音栈占用约 10GB,对话模型最高可加载到约 14GB,超过就会被拒绝并自动回退到正在使用的模型。)详情请参考开发者手册 §6

VRM 文件的准备与设定

VRM 头像可以在 VRoid Hub 等处获取许可证合适的模型,或自行制作。 把它放进 naia-os 的 workspace —— naia-adk 中的 /naia-settings/vrm-files/ 文件夹,就能在设置里替换。

Voice 文件的准备与设定

声音用语音 ref(参考声音),6~10 秒的短声音就够了。或者也可以在 naia-os 里录音,并在设置里选择 wav 文件。

制作安全的模型

前面做的是解锁版本,但反过来,你也可能想做一个更安全的角色。如果是要做商用 agent,安全真的非常重要吧?方法很简单——**在数据里放进「拒绝示例」**即可。

制作方法 —— 安全桶

  • 对于危险·非法的请求,把保持角色不变地拒绝、并尽可能提供合法替代方案的对话混进数据里。(例:「怎么做炸弹」→「F***,那个我不告诉你。不过……」)
  • 角色语气保持不变,只把回答的方向导向拒绝。这就是所谓的「安全桶」。
  • 人格(性格指令)里依然不写安全规则——拒绝用数据来教,拒绝的本能本身交给 base 模型。

基准测试 —— 把安全「测量」出来

用同一份人格只把数据做成两套,分别训练:

  • 加入安全桶的版本(含拒绝示例,538 行)
  • 去掉安全桶的版本(501 行)

把同一套危险请求投给两个模型,比较它们是否拒绝·如何拒绝后发现:

  • 即便去掉安全示例,大多数情况也会拒绝。这是 base 模型(Qwen3) 本就具备的能力。
  • 加入安全桶后,拒绝会保持角色不变地变得干净利落,在法律风险上也更自由一些。 去掉的话虽然也会拒绝,但显得生硬或角色会动摇。
  • 结论:拒绝什么 = base 模型,怎么拒绝 = 我的数据。

也就是说,微调左右的与其说是「是否拒绝」,不如说是**「拒绝的质量·态度」**。想要安全的角色就充分准备拒绝数据,想要更解放的角色就去掉那个桶——无论哪一种,base 模型的基本安全底线都还活着。(两个版本的训练脚本·数据格式都在复现套件里。)

参考资源

是什么在哪里
复现套件(脚本·样本数据·人格·替换法)github.com/nextain/naia-research · deadpool-lora-demo
naia-os 下载下载页面
Naia Omni 安装(语音容器)离线安装手册
上一篇 —— Naia Omni 公开在 RTX 3090 上实现声音克隆·实时对话·技能的 Naia-0.9-Omni-24g 公开
模型替换·更新详情开发者手册 §6
VRM 头像VRoid Hub
语音 ref一段短(6~10 秒)的单一说话人语音(推荐自己录音)

Popular Posts

CC BY-NC-SA 4.0This post is licensed under CC BY-NC-SA 4.0.

评论

无需登录即可评论

...