使用DeepSeek API生成提示模板,并接入QQ群机器人

使用 DeepSeek 的 API 生成角色提示模板

原本是打算使用模型自己训练得到角色的模型的,但是实际操作后发现 24G 显存的 3090 显卡也有点吃力,没办法跑大的模型,于是只好另辟蹊径,使用 DeepSeek 生成角色的提示词,然后再使用这个模板模仿角色并接入到 QQ 聊天中

角色模板生成

材料准备

生成角色模板需要使用 DeepSeek 的 API,先去DeepSeek 开放平台购买一些 Token,再生成一个 API key 保存以备后续使用
再准备一份包含角色发言的 txt 文件,用来模拟角色发言,这个角色发言可以自己制作,也可以从游戏中提取文本获得,比如GalGame
如下是我从《千恋万花》中提取的丛雨的部分对话文本

1
2
3
4
5
6
7
8
9
10
11
丛雨:「快点,走快点呀,主人!」。
将臣:「不用走那么快也不会迟到啦」。
小雨兴高采烈地拉着我的手向前跑去。 我的手也清楚地感受到了她的体温。 身边许多人都看向了她,并向她问好。 他们都说“丛雨大人,路上小心”。 不过……
丛雨:「我希望你们能叫我小雨!」。
每次她都回报以光彩夺目的笑容。 是啊…… 那个作为祭品承受了五百年孤独的少女已经不在了。 她也再也不会仰望月亮,暗自哭泣了。 丛雨丸对她说…… “祝你幸福”。 我在悄悄心底起誓。 对得到供奉的战友起誓…… 小雨……
丛雨:「主人……!」。
我一定会给你幸福。 我要让你成为天下最幸福的人。 我要把你五百年来失去的幸福,全部赠予给你……
-----
这是小雨开始上学后的第一个星期天。 我约她午后一起前去外公的旅馆。
丛雨:「为什么要去找玄十郎啊,主人」。 「不是已经不用练刀了吗」。
将臣:「行了行了,跟我来」。

可以看到 txt 文件的格式就是对话的形式,其中的 ----- 为文本分隔符,这是为了防止一次性上传完整文本超出 API 的长度限制,是后续的模板获取中用来分批次上传的标志

模板获取代码

材料准备好后,就可以使用如下代码生成角色模板了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
import re
import requests
import json
import os
from tqdm import tqdm
import time

class AdvancedRolePromptGenerator:
def __init__(self, api_key, role_name):
"""
初始化角色Prompt生成系统
"""
self.api_url = "https://api.deepseek.com/v1/chat/completions"
self.api_key = api_key
self.role_name = role_name
self.headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}

# 创建角色专属目录
self.work_dir = f"{role_name}_Prompt_Dir"
os.makedirs(self.work_dir, exist_ok=True)

# 定义文件路径
self.chunk_summary_file = os.path.join(self.work_dir, "1_chunk_summaries.json")
self.stage_summary_file = os.path.join(self.work_dir, "2_stage_summaries.json")
self.final_prompt_file = os.path.join(self.work_dir, f"{role_name}_Final_Prompt.txt")

def read_text_file(self, file_path):
"""读取文本文件内容"""
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()

def split_text_by_separator(self, text, separator="-----"):
"""使用分隔符分割文本块"""
escaped_separator = re.escape(separator)
chunks = re.split(rf'{escaped_separator}+', text)
return [chunk.strip() for chunk in chunks if chunk.strip()]

def call_deepseek_api(self, prompt, max_tokens=1000, temperature=0.7):
"""调用DeepSeek API"""
messages = [{"role": "user", "content": prompt}]
data = {
"model": "deepseek-chat",
"messages": messages,
"max_tokens": max_tokens,
"temperature": temperature
}

try:
response = requests.post(
self.api_url,
headers=self.headers,
json=data,
timeout=60
)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
except Exception as e:
print(f"API调用失败: {str(e)}")
return None

def save_to_json(self, data, file_path):
"""保存数据到JSON文件"""
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)

def load_from_json(self, file_path):
"""从JSON文件加载数据"""
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
return None

def summarize_chunk(self, chunk, chunk_id):
"""生成单个文本块的总结"""
prompt = f"""请从以下文本中提取角色'{self.role_name}'的关键信息(限300字内):
1. 性格表现
2. 语言特征
3. 重要行为
4. 关系互动

文本内容:
{chunk[:2000]}"""

summary = self.call_deepseek_api(prompt, max_tokens=400)
time.sleep(1.5)
return summary if summary else f"{self.role_name}在此片段无显著特征"

def process_chunk_summaries(self, chunks):
"""处理所有文本块生成初级总结"""
cached = self.load_from_json(self.chunk_summary_file) or {}

summaries = []
for idx, chunk in enumerate(tqdm(chunks, desc="处理文本块")):
if str(idx) in cached:
summaries.append(cached[str(idx)])
continue

summary = self.summarize_chunk(chunk, idx)
cached[str(idx)] = summary
summaries.append(summary)

# 每处理5个块保存一次缓存
if idx % 5 == 0:
self.save_to_json(cached, self.chunk_summary_file)

self.save_to_json(cached, self.chunk_summary_file)
return summaries

def summarize_stage(self, summaries, stage_id):
"""生成阶段总结(每5个块总结一次)"""
cached = self.load_from_json(self.stage_summary_file) or {}
if str(stage_id) in cached:
return cached[str(stage_id)]

prompt = f"""请整合以下关于角色'{self.role_name}'的多个片段分析:
要求:
1. 合并相似特征
2. 识别性格发展
3. 提炼核心特质
4. 分析语言变化

内容:
{"-".join(summaries)}"""

stage_summary = self.call_deepseek_api(prompt, max_tokens=1500)
time.sleep(2)

if stage_summary:
cached[str(stage_id)] = stage_summary
self.save_to_json(cached, self.stage_summary_file)

return stage_summary if stage_summary else "未生成有效阶段总结"

def process_stage_summaries(self, chunk_summaries):
"""处理所有阶段总结"""
stage_size = 5
stage_summaries = []

for stage_id in range(0, len(chunk_summaries), stage_size):
stage_chunks = chunk_summaries[stage_id:stage_id+stage_size]
stage_summary = self.summarize_stage(stage_chunks, stage_id//stage_size)
stage_summaries.append(stage_summary)
print(f"完成阶段{stage_id//stage_size + 1}总结")

return stage_summaries

def generate_final_prompt(self, stage_summaries):
"""生成最终角色Prompt"""
prompt = f"""请根据以下所有分析结果,创建角色'{self.role_name}'的完整人设Prompt:
要求:
1. 包含完整性格分析(2000字+)
2. 详细语言风格解析(1500字+)
3. 行为模式深度解读(1500字+)
4. 人际关系网络(1000字+)
5. 背景故事推断(1000字+)
6. 提供20+典型对话示例
7. 模仿指南(500字+)

分析内容:
{"-".join(stage_summaries)}

请按以下格式组织:
# 角色设定
## 世界观
[...]
## 核心性格
[...]
## 语言特征
[...]
## 行为模式
[...]
## 人际关系
[...]
## 典型对话
[...]
## 模仿指南
[...]"""

final_prompt = self.call_deepseek_api(prompt, max_tokens=4000)
time.sleep(3)

# 确保达到字数要求
if final_prompt and len(final_prompt) < 10000:
expansion_prompt = f"""请扩展以下角色Prompt到10000字以上(当前{len(final_prompt)}字):
需要补充:
1. 不同情境下的反应模式
2. 心理动机深度分析
3. 文化背景影响
4. 补充10个对话示例

当前内容:
{final_prompt[:2000]}..."""

expansion = self.call_deepseek_api(expansion_prompt, max_tokens=3000)
if expansion:
final_prompt += "\n\n" + expansion

return final_prompt if final_prompt else "未能生成完整Prompt"

def process_text(self, input_file):
"""完整的处理流程"""
# 读取并分割文本
text = self.read_text_file(input_file)
chunks = self.split_text_by_separator(text)
print(f"发现{len(chunks)}个文本块")

# 第一阶段:生成块总结
print("\n=== 第一阶段:生成块总结 ===")
chunk_summaries = self.process_chunk_summaries(chunks)

# 第二阶段:生成阶段总结
print("\n=== 第二阶段:生成阶段总结 ===")
stage_summaries = self.process_stage_summaries(chunk_summaries)

# 第三阶段:生成最终Prompt
print("\n=== 第三阶段:生成最终Prompt ===")
final_prompt = self.generate_final_prompt(stage_summaries)

# 保存最终结果
with open(self.final_prompt_file, 'w', encoding='utf-8') as f:
f.write(final_prompt)

return final_prompt

if __name__ == "__main__":
# 配置参数
API_KEY = "替换为生成得到的 API Key"
ROLE_NAME = "丛雨" # 需要获取得到角色的模板的名字
INPUT_FILE = "Murasame.txt" # 包含角色发言的txt文件路径

try:
# 初始化生成器
generator = AdvancedRolePromptGenerator(API_KEY, ROLE_NAME)

# 运行处理流程
print(f"开始处理角色'{ROLE_NAME}'...")
final_prompt = generator.process_text(INPUT_FILE)

# 输出结果
print(f"\n处理完成!最终文件保存在: {generator.work_dir}/")
print(f"- 块总结: {generator.chunk_summary_file}")
print(f"- 阶段总结: {generator.stage_summary_file}")
print(f"- 最终Prompt: {generator.final_prompt_file}")

if final_prompt:
print(f"\nPrompt长度: {len(final_prompt)}字")

except Exception as e:
print(f"处理失败: {str(e)}")

材料准备好后,启动代码,会在代码的同级目录下生成目录,目录名称为 角色名_Prompt_Dir,目录下会生成三个文件,包括两个 Json 文件和一个 txt 文件,Json 文件是模板提取的中间文件,txt 就是需要的角色模板文件了
至此,角色模板提取成功,可以直接将这个角色模板上传给网页版 DeepSeek 来让 DeepSeek 扮演角色了

接入 QQ 群机器人

修改配置文件

前段时间刚 使用 webhook 方式搭建QQ群机器人的开发环境,这下可以让机器人接入 DeepSeek 在群里玩角色扮演了
首先在机器人的配置文件 .env 中添加一行配置 DEEPSEEK_API_KEY,修改后的配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
HOST=0.0.0.0
PORT=自定义端口,与nginx配置文件中的端口一致
COMMAND_START=["/", ""]
COMMAND_SEP=[".", " "]
DRIVER=~fastapi+~httpx
LOG_LEVEL=DEBUG
API_TIMEOUT=10.0
NICKNAME=["机器人昵称1", "机器人昵称2"]
SESSION_EXPIRE_TIMEOUT=00:02:00
QQ_IS_SANDBOX=true

QQ_BOTS=[{"id":"xxx","token":"xxx","secret":"xxx","intent":{"c2c_group_at_messages":true},"use_websocket":false}]
DEEPSEEK_API_KEY='sk-xxxxxxxxxx'

编写插件程序

修改好配置文件后,在 plugins 目录下创建一个插件目录,我的模板是丛雨的模板,所以就创建了一个 Murasame_Chat 目录,将前面得到的角色模板 txt 文件放在这个目录下面
再在这个目录下创建两个 Python 文件,分别为 DeepSeepChat.pyChatWithMurasame.py
文件目录结构如下

1
2
3
4
5
plugins/
├── Murasame_Chat/
│ ├── ChatWithMurasame.py
│ ├── DeepSeepChat.py
│ └── Murasame_Prompt.txt

DeepSeepChat.py 文件中写入如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import requests
import json
import os
from typing import List, Dict, Optional

class DeepSeekChat:
def __init__(
self,
api_key: str,
character_file: str,
conversation_file: Optional[str] = None,
model: str = "deepseek-chat",
default_user: str = "user"
):
self.api_key = api_key
self.model = model
self.default_user = default_user
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
self.api_url = "https://api.deepseek.com/v1/chat/completions"

# 加载角色设定
with open(character_file, 'r', encoding='utf-8') as f:
self.character = f.read().strip()

# 初始化对话历史
self.conversation = []

# 如果对话文件不存在,添加角色设定
if conversation_file and not os.path.exists(conversation_file):
self.conversation.append({"role": "system", "content": self.character + "每次回答只需回答一段话"})
elif conversation_file:
# 加载现有对话历史
with open(conversation_file, 'r', encoding='utf-8') as f:
self.conversation = json.load(f)

self.conversation_file = conversation_file

def _save_conversation(self) -> bool:
# 保存对话历史到文件
if not self.conversation_file:
return False

try:
with open(self.conversation_file, 'w', encoding='utf-8') as f:
json.dump(self.conversation, f, ensure_ascii=False, indent=2)
return True
except IOError as e:
print(f"保存对话历史失败: {e}")
return False

def chat(
self,
user_input: str,
user_name: Optional[str] = None,
save: bool = True,
**kwargs
) -> Optional[str]:
# 添加用户消息
user_msg = {"role": "user", "content": user_input}
if user_name:
user_msg["name"] = user_name
self.conversation.append(user_msg)

# 准备API请求
data = {
"model": self.model,
"messages": self.conversation,
"temperature": 0.7,
"max_tokens": 2000,
**kwargs
}

try:
# 发送请求
response = requests.post(self.api_url, headers=self.headers, json=data)
response.raise_for_status()
result = response.json()

# 获取助手回复
assistant_reply = result['choices'][0]['message']['content']
self.conversation.append({"role": "assistant", "content": assistant_reply})

# 保存对话历史
if save:
self._save_conversation()

return assistant_reply

except requests.exceptions.RequestException as e:
print(f"API请求失败: {e}")
return None
except (KeyError, IndexError) as e:
print(f"API响应解析失败: {e}")
return None

def reset(self, keep_character: bool = True) -> None:
if keep_character:
# 保留系统消息
system_msgs = [msg for msg in self.conversation if msg.get('role') == 'system']
self.conversation = system_msgs
else:
self.conversation = []
self._save_conversation()

再在 ChatWithMurasame.py 文件中写入如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import nonebot
from nonebot import on_command
from nonebot.adapters.qq import MessageSegment,Message
from nonebot.params import CommandArg
from plugins.Murasame_Chat.DeepSeepChat import DeepSeekChat

# 初始化
config = nonebot.get_driver().config

deepseek_api_key = config.deepseek_api_key
character_file = "plugins/Murasame_Chat/Murasame_Prompt.txt" # 替换为角色模板 txt 文件
conversation_file = "plugins/Murasame_Chat/conversation_history.json" # 保存对话历史文件
chat = DeepSeekChat(deepseek_api_key, character_file, conversation_file)

chat_with_ai = on_command("chat",aliases={"聊天","与丛雨聊天"},block=True)

@chat_with_ai.handle()
async def _(args : Message = CommandArg()):
if user_text := args.extract_plain_text():
if user_text == "重置对话历史":
chat.reset()
await chat_with_ai.finish(MessageSegment.text("我们的对话已重置,以后有缘再见吧!苟修金!"))
response = chat.chat(user_text)
await chat_with_ai.finish(response)
else:
await chat_with_ai.finish(MessageSegment.text("八嘎苟修金!你要说什么!?"))

插件效果

以上代码实现了在 QQ 中使用 chat 命令与角色聊天的功能,聊天的历史会保存在 conversation_history.json 文件中,方便 AI 理解上下文
当使用 chat 命令后跟着 重置对话历史 时,会重置除了设定以外的对话历史,也就是刚才跟你聊天的角色已经不存在了
使用效果如下
正常使用
什么话都不说
重置对话历史


为了搭建这个简单的 QQ 群聊天程序,前前后后居然用了两周,其中一半时间用于搭建模型训练环境,训练之后发现得到的模型根本没什么用
也是限制于硬件的条件吧,只能退而求其次,使用了这个方法,不过最后的效果还挺不错的,努力没白费
-=≡ヘ(*・ω・)ノ