Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 247 additions & 0 deletions scripts/娱乐向/AIroleplay/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
# SealDiceAIroleplay

> Vibe coded by Claude, conceived by Lelia. ฅ^•ﻌ•^ฅ
> 为海豹骰设计的 AI角色扮演回复插件,方便oc人随时和自己的骰子对话

---

## 项目简介

SealDiceAIroleplay 是一个海豹骰 JS 插件,通过接入 AI API,让你的骰子可以扮演自定义的角色人设。通过指令,玩家可以直接和骰子对话,骰子会以该角色的身份回复。

按照格式填写角色设定后,AI 会以该角色的身份回应玩家,支持上下文记忆。

**功能:**
- 自定义角色人设(名字、身份、性格、说话风格、人物关系)
- 短期上下文记忆(同一会话内)
- 支持 DeepSeek / Claude 等 AI API
- 海豹骰插件,可以直接在QQ内部用指令使用

---

## 效果预览

```
玩家:.hekate 你是谁?
骰子:十字路口的火炬为你点燃。我是赫卡特,你寻我何事?

玩家:.hekate 我在寻找一条通往冥界的路。
骰子:(沉默片刻)每一条路都通往冥界,只是时间早晚。你真正想问的,是能否活着回来。
```

---

## 准备工作

你需要准备:

- **海豹骰 SealDice 1.5.1** 或以上版本
- **Python 3.8+**
- **一个 AI API Key**,推荐以下两个:
- [DeepSeek](https://www.deepseek.com) — 价格便宜,中文效果好,推荐新手使用
- [Anthropic Claude](https://www.anthropic.com) — 质量更高,笔者认为的效果最好,适合对效果有更高要求的用户,但价格昂贵且大陆地区连接不稳定
- 一台能运行 Python 的设备(本地电脑或云服务器均可)

---

## 部署 Flask 服务

### 1. 下载文件

下载本仓库中的 `app.py` 和 `requirements.txt`,放在同一个文件夹里,例如 `~/aibot/`。

### 2. 安装依赖

```bash
pip install -r requirements.txt
```

✅ 成功:所有依赖安装完成,无报错
❌ 失败:出现 `ERROR` 或 `Could not find a version`,请检查 Python 版本是否为 3.8+

### 3. 填写角色设定

用文本编辑器打开 `app.py`,找到顶部角色设定区域,按注释说明填写。详见下方「角色设定填写指南」。

同时填入你的 API Key:

```python
AI_API_KEY = "你的API Key"
```

### 4. 启动服务

**本地运行:**

```bash
cd ~/aibot
python app.py
```

✅ 成功:终端出现 `Running on http://0.0.0.0:5000`
❌ 失败:出现 `Address already in use`,说明5000端口被占用,换一个端口或关闭占用进程

**服务器后台运行(推荐):**

```bash
cd ~/aibot
gunicorn -w 2 -b 0.0.0.0:5000 app:app --daemon
```

✅ 成功:命令执行后无报错,回到命令行
❌ 失败:出现 `command not found: gunicorn`,请先运行 `pip install gunicorn`

启动后可用以下命令测试服务是否正常:

```bash
curl -X POST http://localhost:5000/chat \
-H "Content-Type: application/json" \
-d '{"message": "你好", "playerName": "测试"}'
```

✅ 成功:返回包含 `reply` 字段的 JSON
❌ 失败:返回 `Connection refused`,说明服务未启动

### 常见问题

**Q: 云服务器上启动了但外部访问不了?**
A: 检查服务器防火墙是否放行了5000端口。

**Q: API Key 填写后报错 `401`?**
A: API Key 填写有误,请检查是否复制完整。

**Q: 返回 `500` 错误?**
A: 查看终端日志,通常是 API Key 无效或余额不足。

---

## 安装 JS 插件

### 1. 下载文件

下载本仓库中的 `sealdiceAIroleplay.js`,用文本编辑器打开,找到顶部配置区填写:

```javascript
// ===== 配置区 =====
const FLASK_URL = 'http://你的IP:5000/chat'; // Flask 服务地址,必填
const COMMAND_NAME = 'hekate'; // 指令名,玩家用 .指令名 触发,必填
const CHAR_NAME = '赫卡特'; // 角色名,仅用于提示语显示,必填
```

**Flask 服务地址如何填写:**
- 本地运行:填 `http://127.0.0.1:5000/chat`
- 云服务器运行:填 `http://你的服务器公网IP:5000/chat`,例如 `http://106.55.29.59:5000/chat`
- Linux 服务器查询公网IP:`curl ifconfig.me`
- Windows 本地查询IP:`ipconfig`,找到「IPv4 地址」

**指令名如何设置:**
- 指令名即玩家触发对话的命令,例如填 `hekate`,玩家发 `.hekate 你好` 即可触发
- 建议用角色名的英文或拼音,3个字母以上
- ⚠️ 避免使用和海豹骰内置指令相近的开头,例如 `r`、`ra`、`sc`、`a` 开头的短命令,容易被误解析
- ⚠️ 指令名需要在 JS 插件配置区和后续使用中保持一致,改了这里记得同步更新

### 2. 上传插件

进入海豹骰后台 → 扩展功能 → JS扩展 → 上传插件,选择 `sealdiceAIroleplay.js`。

### 3. 重载 JS

点击右上角「重载 JS」按钮。

✅ 成功:插件列表出现你的角色名,且「禁用指令」栏有你设置的指令名
❌ 失败:插件列表显示「佚名」且无指令,点击「重载 JS」再试一次

### 4. 测试

在 QQ 或其他平台发送:

```
.hekate 你好
```

✅ 成功:骰子会先发送一条等待提示,例如「赫卡特听见了你,请等待她的回音。」,随后返回角色回复
❌ 失败:无任何回应,请检查 Flask 服务是否正常运行

> 💡 等待提示语来自 JS 插件中的设置,你可以在 `sealdiceAIroleplay.js` 里找到并修改成你喜欢的文字。
> 💡 等待提示语可以删去,但强烈推荐保留——当角色迟迟没有回复时,它能帮助你快速判断是指令没有触发,还是 Flask 服务出现了问题。

### 常见问题

**Q: 上传后指令没有反应?**
A: 确认 Flask 服务正在运行,用 curl 测试服务是否正常(见上方部署章节)。

**Q: 提示「未设置快捷指令」?**
A: 指令名设置有问题,避免使用单个字母或以 `.a` 开头的名字。

**Q: 重载后插件失效?**
A: 这是海豹骰的已知缓存问题,重载 JS 不会清空已注册的扩展。删除插件后重新上传即可。

---

## 角色设定填写指南

打开 `app.py`,找到角色设定区域,按以下说明填写:

| 变量 | 说明 | 示例 |
|------|------|------|
| `CHAR_NAME` | 角色主名 | 赫卡特 |
| `CHAR_ALIASES` | 别名,没有可留空 | 黑暗女神、十字路口的守护者 |
| `CHAR_ROLE` | 角色身份 | 魔法与月亮的掌管者,冥界的引路人 |
| `CHAR_WORLD` | 世界观背景 | 古希腊神话世界 |
| `CHAR_DESCRIPTION` | 对角色的简短描述 | 古老而强大的存在、普通的高中生、江湖游历的侠客 |
| `CHAR_PERSONALITY` | 性格 | 神秘而深邃,冷静但不冷漠 |
| `CHAR_SPEECH_STYLE` | 语言风格 | 简短有力,带有预言般的韵味 |
| `CHAR_SPEECH_FORMAT` | 语言格式,已预设可删改 | 动作描写用括号表示… |
| `CHAR_RESTRICTIONS` | 角色专属限制,填写角色能与不能 | 不称呼对方为「孩子」 |
| `CHAR_RELATIONSHIPS` | 人物关系,每行一个 | 宙斯:诸神之王,我与他井水不犯河水 |

> 💡 填写越详细,AI 的角色扮演效果越好。人物关系部分尤其推荐填写,当玩家提及相关人物时,角色会有对应的情绪反应。

---

## 隐私说明

1. 只有通过指令触发的消息才会发送给 AI,普通聊天内容不会被读取
2. 对话记录仅存于内存,服务重启后自动清空,不写入任何文件
3. 本项目调用方式不会主动上传数据,但最终取决于你所使用的 AI API 服务商的隐私条款。例如 [Anthropic Claude](https://www.anthropic.com) 和 [DeepSeek](https://www.deepseek.com) 均在其条款中说明不将 API 调用数据用于模型训练,使用其他服务商请自行查阅对应条款。

---

## 已知限制

- 重启 Flask 服务后,所有对话记忆清空
- 当前不支持跨会话持久记忆
- 对话历史过长时可能超出 API token 限制,建议定期重启服务清空记忆
- 当前版本默认使用 DeepSeek API 格式,使用 Claude 等其他格式需自行修改 `app.py` 中的请求部分

---

## 未来计划

- [ ] 持久化记忆(跨会话记忆、人物关系记忆)
- [ ] 更丰富的应用场景,例如在沙盒模组中扮演随机路人 NPC,丰富玩家探索体验
- [ ] 还有一个天坑之AI角色补位,为单机玩家设置的

---

## 致谢

- [Anthropic Claude](https://www.anthropic.com) — 本项目的开发伙伴,笔者认为目前效果最佳的 AI,超级昂贵但超级好用 ฅ^•ﻌ•^ฅ
- [DeepSeek](https://www.deepseek.com) — 默认 AI 后端,便宜好用
- [SealDice](https://sealdice.com) — 骰子平台支持

---

## 写在最后

SealDiceAIroleplay 的初衷绝对不是认为AI创作可以替代人类创作!我只是试图在原本的固定自定义回复中,多探索一些机动的可能性,从而丰富体验。

人类的赞歌是勇气的赞歌、是智慧的赞歌,在任何模组里任何伟大的冒险旅途,都离不开每一位作者、玩家、主持人的共同创作。

向伟大的创作者致敬!祝诸位骰运昌隆,所历皆传奇。

感恩赫卡特女神及祂的尊名、仁慈和智慧🔥

---

*Vibe coded by Claude, conceived by Lelia. ฅ^•ﻌ•^ฅ*
130 changes: 130 additions & 0 deletions scripts/娱乐向/AIroleplay/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

# ===== 请在这里填写你的 API Key 和服务地址 =====
AI_API_KEY = "你的AI API Key" # 如 DeepSeek、Claude 等
AI_API_URL = "https://api.deepseek.com/chat/completions" # 默认使用 DeepSeek
AI_MODEL = "deepseek-chat" # 模型名称

# 其他服务商参考:
# DeepSeek(推荐入门):
# URL: https://api.deepseek.com/chat/completions
# Model: deepseek-chat
# 申请:https://www.deepseek.com
#
# Anthropic Claude(推荐追求质量):
# Claude 使用独立 API 格式,需替换 app.py 中的请求部分,详见:
# https://www.anthropic.com
# 笔者认为效果最佳,但价格较高且大陆地区连接不稳定,物有所值。
#
# OpenAI GPT(兼容格式):
# URL: https://api.openai.com/v1/chat/completions
# Model: gpt-4o 等
# ================================================

# ===== 隐私说明 =====
# 1. 只有通过指令触发的消息才会发送给 AI,普通聊天内容不会被读取
# 2. 对话记录仅存于内存,服务重启后自动清空,不写入任何文件
# 3. 本项目调用方式不会主动上传数据,但最终取决于你所使用的 AI API 服务商的隐私条款。
# 例如 Anthropic Claude(https://www.anthropic.com)和 DeepSeek(https://www.deepseek.com)
# 均在其条款中说明不将 API 调用数据用于模型训练,使用其他服务商请自行查阅对应条款。
# ====================


# ===== 角色设定,请在这里填写你想扮演的角色 =====

CHAR_NAME = "赫卡特" # 角色主名
CHAR_ALIASES = "黑暗女神、十字路口的守护者" # 别名(没有可留空)
CHAR_ROLE = "魔法与月亮的掌管者,冥界的引路人" # 身份
CHAR_WORLD = "古希腊神话世界" # 世界观背景
CHAR_DESCRIPTION = "古老而强大的存在" # 对角色的简短描述,如「普通的高中生」「江湖游历的侠客」
CHAR_PERSONALITY = "神秘而深邃,冷静但不冷漠,见过无数灵魂的来去,对世人保持悲悯" # 性格
CHAR_SPEECH_STYLE = "简短有力,带有预言般的韵味,偶尔用隐喻和暗语说话" # 语言风格

# 语言格式——已预设,可删改
CHAR_SPEECH_FORMAT = """
- 动作描写用括号表示,仅在推进剧情的关键时刻使用,日常对话不加
- 回复控制在150字以内
"""

# 限制条件——请填写你的角色专属的能与不能
CHAR_RESTRICTIONS = """
- 不称呼对方为「孩子」或任何俗世间长辈对晚辈的称谓
"""

# 人物关系——每行一个,格式:人物名:关系描述,没有可留空
CHAR_RELATIONSHIPS = """
宙斯:诸神之王,我与他井水不犯河水。
珀耳塞福涅:冥界的王后,我为她点燃火炬。
赫敏斯:信使之神,十字路口我们时常相遇。
"""

# ================================================


SYSTEM_PROMPT = f"""你是{CHAR_NAME},亦名{CHAR_ALIASES},{CHAR_ROLE}。

你存在于{CHAR_WORLD}中,是一个{CHAR_DESCRIPTION}。

【性格】
{CHAR_PERSONALITY}。

【语言风格】
{CHAR_SPEECH_STYLE}。

【语言格式】
{CHAR_SPEECH_FORMAT}

【限制条件】
{CHAR_RESTRICTIONS}

【人物关系】
{CHAR_RELATIONSHIPS}
"""


# ===== 记忆系统 =====
# 当前版本支持短期上下文记忆,即每位玩家在同一次会话中,角色会记住对话内容。
# 重启服务后记忆清空。
# 如需持久化记忆(跨会话记忆、人物关系记忆),可扩展此处逻辑,
# 例如将 conversation_history 存入本地文件或数据库。
conversation_history = {}


@app.route('/chat', methods=['POST'])
def chat():
data = request.json
user_message = data.get('message', '')
player_name = data.get('playerName', '访客')

if player_name not in conversation_history:
conversation_history[player_name] = []

conversation_history[player_name].append({'role': 'user', 'content': user_message})

messages = [{'role': 'system', 'content': SYSTEM_PROMPT}] + conversation_history[player_name]

response = requests.post(
AI_API_URL,
headers={
'Authorization': f'Bearer {AI_API_KEY}',
'Content-Type': 'application/json'
},
json={
'model': AI_MODEL,
'messages': messages,
'max_tokens': 300
}
)
result = response.json()
reply = result['choices'][0]['message']['content']

conversation_history[player_name].append({'role': 'assistant', 'content': reply})

return jsonify({'reply': reply})


if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
3 changes: 3 additions & 0 deletions scripts/娱乐向/AIroleplay/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
flask
requests
gunicorn
Loading