今天突然发现发现Live2DViewerEx提供了api,于是便有了这么个有趣的小练习。
前置准备
pip install websocket-client
pip install openai
发送消息
根据ExAPI的说法,如果想要给桌宠发消息,只需要通过WebSocket向指定端口(通常是本地的10086端口)发送一个JSON。
于是我们先写一个函数用来发送消息:
def send_json_message(json_message: str):
# 通信
uri = "ws://127.0.0.1:10086/api"
data = {
"msg": 11000,
"msgId": 1,
"data": {
"id": 0,
"text": json_message,
"textFrameColor": 0x000000,
"textColor": 0xFFFFFF,
"duration": 3000
}
}
# 尝试发出json消息
try:
ws = create_connection(uri)
ws.send(json.dumps(data))
except Exception as e:
print("发送错误:", str(e))
uri是指向的地址,data是发送的数据。在这里,data虽然是字典,但通过json.dumps()转换为json数据格式。这里data是[11000](显示气泡文本)。所以这个函数向本地10086端口发送了一个包含json_message的json。
接下来,我们需要接入kimi了
接入Kimi
参考Kimi API
准备system_prompt
这个相当于先塑造ai的初始人设。为了方便后期更改,这里我们把初始人设放入一个txt文本,再让程序读取:
# 读取小米塔预设
file_path = "prompt.txt"
try:
with open(file_path, "r", encoding="utf-8") as file:
system_prompt = file.read()
except FileNotFoundError:
print(f"读取文件错误:文件 {file_path} 未找到。")
except Exception as e:
print(f"读取文件错误:{e}")
# 预设信息
system_messages = [
{"role": "system", "content": system_prompt},
]
接入Kimi api
# 调用kimi api
client = OpenAI(
api_key="", # kimi api密钥
base_url="https://api.moonshot.cn/v1",
)
准备多轮对话需要的message
因为我们想要让Kimi记住我们说了什么,所以需要准备多轮对话。
参考kimi官方的代码:
# 聊天记录
messages = []
# 限制记忆消息数小于等于10条
def make_new_messages(input: str , n: int = 10) -> list[dict]:
# 先将input内容加入到messages
global messages
messages.append({
"role": "user",
"content": input
})
# 创建新消息列表
new_messages = []
new_messages.extend(system_messages)
# 处理messages,保留小于等于10条消息到new_messages里
if len(messages) > n:
messages = messages[-n:]
new_messages.extend(messages)
return new_messages
这段代码在Kimi API详细解释过了,这里我就不在赘述。简单来说,就是通过messages = messages[-n:]让每次对话发送的new_messages只“记住”messages的最近十条,当然system_prompt肯定包含。
对话函数
准备结束,开始对话!
# 小米塔的聊天函数
def chat(input: str) -> str:
# 携带 messages 与 小米塔(Kimi 大模型)对话
completion = client.chat.completions.create(
model="moonshot-v1-8k", # 选择模型为moonshoot-v1-8k
messages=make_new_messages(input),
temperature=0.3,
)
# 通过 API 获得 Kimi 大模型的回复消息(role=assistant)
mita_message = completion.choices[0].message.content
# 保证记忆完整,将回复mita_message加入messages
messages.append({
"role": "assistant",
"content": mita_message
})
# 返回消息
return mita_message
这段代码也在Kimi API详细解释过了。
但这要注意一点,在加入回复时,官方代码似乎有问题:
# 通过 API 我们获得了 Kimi 大模型给予我们的回复消息(role=assistant)
assistant_message = completion.choices[0].message
# 为了让 Kimi 大模型拥有完整的记忆,我们必须将 Kimi 大模型返回给我们的消息也添加到 messages 中
messages.append(assistant_message)
一直报错,显示messages格式不对。由于我也不清楚其中细节,所以直接简单粗暴的把回复手动加上去了,效果还行。
到这里,程序的主体部分就写好了。
完整代码
我们把对话写到一个死循环里,同时拼装一下以上代码:
import json
from websocket import create_connection
from openai import OpenAI
# 读取小米塔预设
file_path = "prompt.txt"
try:
with open(file_path, "r", encoding="utf-8") as file:
system_prompt = file.read()
except FileNotFoundError:
print(f"错误:文件 {file_path} 未找到。")
except Exception as e:
print(f"读取文件时发生错误:{e}")
# 调用kimi api
client = OpenAI(
api_key="", # kimi api密钥
base_url="https://api.moonshot.cn/v1",
)
# 预设信息
system_messages = [
{"role": "system", "content": system_prompt},
]
# 聊天记录
messages = []
# 限制记忆消息数小于等于10条
def make_new_messages(input: str , n: int = 10) -> list[dict]:
# 先将input内容加入到messages
global messages
messages.append({
"role": "user",
"content": input
})
# 创建新消息列表
new_messages = []
new_messages.extend(system_messages)
# 处理messages,保留小于等于10条消息到new_messages里
if len(messages) > n:
messages = messages[-n:]
new_messages.extend(messages)
return new_messages
#给小米塔发json格式的消息,通过Live2DViewerEX api [11000]
def send_json_message(json_message: str):
# 通信
uri = "ws://127.0.0.1:10086/api"
data = {
"msg": 11000,
"msgId": 1,
"data": {
"id": 0,
"text": json_message,
"textFrameColor": 0x000000,
"textColor": 0xFFFFFF,
"duration": 3000
}
}
# 尝试发出json消息
try:
ws = create_connection(uri)
ws.send(json.dumps(data))
except Exception as e:
print("错误:", str(e))
# 小米塔的聊天函数
def chat(input: str) -> str:
# 携带 messages 与 小米塔(Kimi 大模型)对话
completion = client.chat.completions.create(
model="moonshot-v1-8k", # 选择模型为moonshoot-v1-8k
messages=make_new_messages(input),
temperature=0.3,
)
# 通过 API 获得 Kimi 大模型的回复消息(role=assistant)
mita_message = completion.choices[0].message.content
# 保证记忆完整,将回复mita_message加入messages
messages.append({
"role": "assistant",
"content": mita_message
})
# 返回消息
return mita_message
# 死循环,一直重复对话
while True:
try:
saying = input("Player:")
if saying == "exit loop":
break
send_json_message(chat(saying))
except Exception as e:
print(f"kimi出现错误:{e}")