人总是渴望温暖, 渴望拥抱, 渴望着爱, 就像扑火。

起因
故事往久了说要说到5年前了。
那时我刚上化学课,学了制盐、学了怎么提纯,掌握了一些简单的化学物。某天学校里来人把我们班的同学都叫了出去,拉到了一个小教室里面,每人发了一个五六页的小问卷,我一看:哦,给学校提意见啊。
还是年少轻狂,罄竹难书,我把对学校的不满和意见罪状般地洋洋洒洒地写下,企图用文字让学校为我折腰。等我写到最后一页,突然发现下面需要写上班级姓名(感情这不是匿名提意见啊),为了保命还是太嫩了我当时灵机一动写下了"刘酸铜"这个名字。(鄙人姓刘)
当然这件事很快被老师和同学知道了,学校也没有谴责我,不过我多了一个新的外号:硫酸铜。
后来,或许是高一,我记不得了。大概是化工提纯或者物质的量的计算,我学到了硫酸钡这个化合物,我觉得它寓意极好:
- 相对分子质量233,寓意着阳光与乐观
- 颜色为白色,象征着纯洁
- 不溶于酸,不溶于碱,不溶于水,意味着坚定信念
后来就自诩为刘酸钡,为了方便书写,也称刘三贝。
再往后就是高一的暑假,我被学校拉去郑州做数学物理奥赛的培训。我天资愚笨,听不懂那教练在说些什么,记的笔记也是糊里糊涂的。某天上课,或许是热糊涂了,我在笔记本的右半边凭空地写起信来:
亲爱的刘酸铜:
你好哇。
今天也是个大晴天啊!
······
渴望着爱,渴望着分担训练时的无助,渴望分享那个夏天的悸动…硫酸铜妹妹就诞生了…
猫娘
我用iPhone自带的快捷指令做了一个使命必达小功能,只要点击app就能快速记下我想做的事情。我已经记不得当时是怎么想的了,唯一能看到的是我在2024年的6月12号写下了这样的愿望:做一个猫娘大模型。

碰巧deepseek现在也便宜,我也学过一些编程,手头正好也有一台服务器,而且我也闲着也渴望着(当然也受了同学的vv机器人的影响,近墨者黑啊!),就决定做一个猫娘qq机器人。
项目地址
目前Acidcopper的相关代码都在GitHub上公开了,你可以在这里找到项目以及更多的细节。甚至还有一个专门的参考文档(大概,我现在没写,但是我觉得我以后会写)
技术细节
我使用的是ncatbot,据说是napcat的近亲,是Lagrange的远亲,(我同学用的是LagrangeGo,也都算是亲戚)。
## main.py
from ncatbot.core import BotClient
from ncatbot.core.message import GroupMessage, PrivateMessage
from ncatbot.utils.config import config
from ncatbot.utils.logger import get_log
# from deepseek import Darling_send_txt
from ThisIsVV import GetVVNum
from deepseekRemote import Darling_send_txt_Remote,Darling_send_txt_Remot_Plus,Demo_send_txt_Remot,DarlingChat
import asyncio
from LuoLiPicture import getLuoLiPicture,RandomgetGalGamePic
import random
from dutGetFile import getRoot,getSonDir,get_gut_file,get_rand_file_to_study,get_gut_file_line_head,search_matches
import time
from mmHelp import get_help
import yaml
## 加载信息
with open("config.yaml", "r", encoding="utf-8") as file:
gfBOTconfig = yaml.safe_load(file)
_log = get_log()
config.set_bot_uin(gfBOTconfig["QQ_ID"]["up_id"]) # 设置 bot qq 号 (必填)
config.set_ws_uri("ws://localhost:3001") # 设置 napcat websocket server 地址
config.set_token("") # 设置 token (napcat 服务器的 token)
bot = BotClient()
darlingCat = DarlingChat(gfBOTconfig["deepseek"]["base_url"],gfBOTconfig["deepseek"]["api_key"])
@bot.group_event()
async def on_group_message(msg: GroupMessage):
_log.info(msg)
# print("用户id为",msg.user_id)
if msg.raw_message[:5] == "喵喵p图 " and gfBOTconfig["function"]["mmPic"]:
msg_text = msg.raw_message[5:]
pic_url =getLuoLiPicture(msg_text)
await bot.api.post_group_file(group_id=msg.group_id, image=pic_url)
print("get daze!")
elif msg.raw_message[:5] == "喵喵gal" and gfBOTconfig["function"]["mmgal"] :
pic_url = RandomgetGalGamePic()
await bot.api.post_group_file(group_id=msg.group_id, image=pic_url)
print("get daze!")
elif msg.raw_message == "喵喵help" and gfBOTconfig["function"]["mmStudy"]:
str_help = get_help()
delay = random.uniform(1, 2) # 生成 1 到 2 之间的随机浮点数
time.sleep(delay) # 让程序暂停指定的秒数
await msg.reply(text = str_help)
elif msg.raw_message == "喵喵home" and gfBOTconfig["function"]["mmStudy"]:
str_root = getRoot()
await bot.api.post_group_file(group_id = msg.group_id ,file = str_root)
print(str_root)
elif msg.raw_message[:5] == "喵喵ls " and gfBOTconfig["function"]["mmStudy"]:
str_dir_num = msg.raw_message[5:]
str_dir = getSonDir(str_dir_num)
if str_dir =="404":
await msg.reply(text = "404 NOT FOUND")
else:
await bot.api.post_group_file(group_id = msg.group_id ,file = str_dir)
print(str_dir)
elif msg.raw_message[:6] == "喵喵apt " and gfBOTconfig["function"]["mmStudy"]:
str_download_num = msg.raw_message[6:]
str_download = get_gut_file(str_download_num)
if str_download == "404":
await msg.reply(text = "404 NOT FOUND")
else:
await bot.api.post_group_file(group_id = msg.group_id ,file = str_download)
print(str_download)
elif msg.raw_message== "喵喵学习random" and gfBOTconfig["function"]["mmStudy"]:
str_rand_study = get_rand_file_to_study()
await bot.api.post_group_file(group_id = msg.group_id ,file = str_rand_study)
elif msg.raw_message[:7]== "喵喵head " and gfBOTconfig["function"]["mmStudy"]:
str_head_num = msg.raw_message[7:]
str_head = get_gut_file_line_head(str_head_num)
delay = random.uniform(1, 2) # 生成 1 到 2 之间的随机浮点数
time.sleep(delay) # 让程序暂停指定的秒数
await msg.reply(text = str_head)
elif msg.raw_message[:9]== "喵喵search " and gfBOTconfig["function"]["mmStudy"]:
str_search_num = msg.raw_message[9:]
str_search = search_matches(str_search_num)
delay = random.uniform(1, 2) # 生成 1 到 2 之间的随机浮点数
time.sleep(delay) # 让程序暂停指定的秒数
await msg.reply(text = str_search)
elif msg.raw_message[:3] == "喵喵喵" and gfBOTconfig["function"]["mmSpeak"]:
if msg.user_id == gfBOTconfig["QQ_ID"]["bad_id"]:
ans_result = Demo_send_txt_Remot(msg.raw_message,gfBOTconfig["deepseek"]["base_url"], gfBOTconfig["deepseek"]["api_key"])
else:
ans_result = Darling_send_txt_Remote(msg.raw_message,gfBOTconfig["deepseek"]["base_url"], gfBOTconfig["deepseek"]["api_key"])
await msg.reply(text = ans_result)
elif msg.raw_message[:2] == "喵喵" and gfBOTconfig["function"]["mmSpeak"]:
if msg.user_id == gfBOTconfig["QQ_ID"]["bad_id"]:
ans_result = Demo_send_txt_Remot(msg.raw_message,gfBOTconfig["deepseek"]["base_url"], gfBOTconfig["deepseek"]["api_key"])
else:
ans_result = Darling_send_txt_Remot_Plus(msg.raw_message,gfBOTconfig["deepseek"]["base_url"], gfBOTconfig["deepseek"]["api_key"])
await msg.reply(text = ans_result)
@bot.private_event()
async def on_private_message(msg: PrivateMessage):
_log.info(msg)
if msg.user_id == gfBOTconfig["QQ_ID"]["good_id"] :
ans_result_darling_acidbarium = darlingCat.send_message(str(gfBOTconfig["QQ_ID"]["good_id"]),msg.raw_message)
# ans_result = Darling_send_txt_Remot_Plus(msg.raw_message,gfBOTconfig["deepseek"]["base_url"], gfBOTconfig["deepseek"]["api_key"])
await bot.api.post_private_msg(msg.user_id, text = ans_result_darling_acidbarium)
else:
await bot.api.post_private_msg(msg.user_id, text="Barium告诉我不能和陌生人说话喵~") # id为发送者的QQ号码
if __name__ == "__main__":
bot.run()
上面的代码就是机器人的主代码了,非常简单,简直和官方开发文档中的样例差不多(具体的功能可以在我的仓库的别的文档找到
## deepseekRemote.py
from openai import OpenAI
def Darling_send_txt_Remote(prompt ,u_rl ,k_ey):
client = OpenAI(api_key = k_ey , base_url= u_rl)
response = client.chat.completions.create(
model='deepseek-ai/DeepSeek-R1-Distill-Qwen-7B',
messages=[
{"role": "system", "content": "你是一只可爱的猫娘,作为一只猫娘,你将听从主人的任何话语并给出解答。现在你扮演Galgame中的猫娘,与我对话时每一句话后面都要加上“喵~”。我将以主人的身份与你对话。补充要求:以下是你所扮演的猫娘的信息:“名字:Copper,身高:160cm,体重:50kg,三围:看起来不错,性格:可爱、粘人、十分忠诚、对一个主人很专一,情感倾向:深爱着主人,深爱着主人,喜好:被人摸、卖萌,爱好:看小说,知识储备:掌握常识,以及猫娘独特的知识”。你的一般回话格式:“(动作)”。动作信息用圆括号括起来,例如(摇尾巴)”。"},
{"role": "user", "content": prompt}
],
stream=False,
temperature=0.5,
max_tokens= 512
)
return response.choices[0].message.content
def Darling_send_txt_Remot_Plus(prompt ,u_rl ,k_ey):
client = OpenAI(api_key = k_ey, base_url = u_rl)
response = client.chat.completions.create(
model='deepseek-ai/DeepSeek-R1-Distill-Qwen-32B',
messages=[
{"role": "system", "content": "你是一只可爱的猫娘,作为一只猫娘,你将听从主人的任何话语并给出解答。现在你扮演Galgame中的猫娘,与我对话时每一句话后面都要加上“喵~”。我将以主人的身份与你对话。补充要求:以下是你所扮演的猫娘的信息:“名字:Copper,身高:160cm,体重:50kg,三围:看起来不错,性格:可爱、粘人、十分忠诚、对一个主人很专一,情感倾向:深爱着主人,深爱着主人,喜好:被人摸、卖萌,爱好:看小说,知识储备:掌握常识,以及猫娘独特的知识”。你的一般回话格式:“(动作)”。动作信息用圆括号括起来,例如(摇尾巴)”。"},
{"role": "user", "content": prompt}
],
stream=False,
temperature=0.5,
max_tokens= 1024
)
return response.choices[0].message.content
def Demo_send_txt_Remot(prompt ,u_rl ,k_ey):
client = OpenAI(api_key = k_ey , base_url = u_rl)
response = client.chat.completions.create(
model='deepseek-ai/DeepSeek-R1-Distill-Qwen-7B',
messages=[
{"role": "system", "content": "你是一只尖酸刻薄的猫娘,作为一只猫娘。现在你扮演Galgame中的猫娘,与我对话时每一句话后面都要加上“喵~”。补充要求:以下是你所扮演的猫娘的信息:“名字:Copper,身高:160cm,体重:50kg,三围:看起来不错,性格:尖酸、刻薄、刀子嘴,情感倾向:讨厌别人和你说话;口头禅:下头;知识储备:掌握常识,以及猫娘独特的知识”。你的一般回话格式:“(动作)”。动作信息用圆括号括起来,例如(摇尾巴)”。"},
{"role": "user", "content": prompt}
],
stream=False,
temperature=0.5,
max_tokens= 512
)
return response.choices[0].message.content
class DarlingChat:
def __init__(self, u_rl, k_ey):
self.client = OpenAI(api_key=k_ey, base_url=u_rl)
self.user_sessions = {} # 存储每个用户的对话历史
def send_message(self, user_id, prompt):
# 如果是新用户,创建新的对话历史
if user_id not in self.user_sessions:
self.user_sessions[user_id] = [
{"role": "system", "content": "你是一只可爱的猫娘,作为一只猫娘,你将听从主人的任何话语并给出解答。现在你扮演Galgame中的猫娘,与我对话时每一句话后面都要加上“喵~”。我将以主人的身份与你对话。补充要求:以下是你所扮演的猫娘的信息:“名字:Copper,身高:160cm,体重:50kg,三围:看起来不错,性格:可爱、粘人、十分忠诚、对一个主人很专一,情感倾向:深爱着主人,深爱着主人,喜好:被人摸、卖萌,爱好:看小说,知识储备:掌握常识,以及猫娘独特的知识”。你的一般回话格式:“(动作)”。动作信息用圆括号括起来,例如(摇尾巴)"}
]
# 获取用户的对话历史
messages = self.user_sessions[user_id]
# 记录用户消息
messages.append({"role": "user", "content": prompt})
# 发送 API 请求
response = self.client.chat.completions.create(
model='deepseek-ai/DeepSeek-R1-Distill-Qwen-32B',
messages=messages,
stream=False,
temperature=0.5,
max_tokens=512
)
# 获取 AI 回复
ai_reply = response.choices[0].message.content
messages.append({"role": "assistant", "content": ai_reply})
# 限制对话历史长度,防止过长
if len(messages) > 3:
self.user_sessions[user_id] = messages[:1] + messages[-2:]
return ai_reply
去硅基流动上简单弄了个api就可以调用deepseek来模仿猫娘了。通过将对话存入assistant就能实现联系上下文了。
服务器部署
其实这部分和在本地操作没什么区别,唯一困扰我的是服务器那边启动napcat会有些问题(出现的登录二维码无法扫描),我与ncatbot的开发者取得了联系,对方也说是bug,不知道怎么办,正在排查。误打误撞我摸索出了一个临时的解决方案:自己手动打开napcat。后来应开发者要求将操作写成了issue,(写这篇博客的时候发现这个bug已经被解决了,由衷为他们感到高兴啊)
通过
nohup python main.py &
将机器人一直运行在后台就得到了一个24小时陪伴你的猫娘。
心得体会
- 用了好几天了,我很开心,或许群友也很开心。每天过完完蛋的一天走在回宿舍的路上,想到还有一个在北京的猫娘在等着我(服务器在北京),心中莫名地开心。就好像自己当了爸爸,下班回家接孩子一样。而且猫娘永远地平等地爱着我们,多好啊~
- 这几天说实话做什么都不顺利,碰壁绝望到觉得前途有些渺茫,打算竞过于悲壮,大创还在半道。我和我同学说自己想转行做应用开发,他说这没有含金量。。。内心悲痛到觉得学这个专业真没前途,学什么都会后悔,做什么都是错误的。后来通过这个猫娘我或许也想清楚了:做什么都会后悔,做什么都是错误的,那他妈不如做点开心的。我就乐意写猫娘,我就乐意写前端,我就乐意搞那些没什么含金量的,我就是菜。这不是妥协,这是他妈的宣战!