在godot中接入大模型api,实现npc的自动对话
在godot中接入大模型api,实现npc的自动对话
目录
概述
最近计划利用godot做一个游戏demo,其中会随机产生各种npc,然后npc之间会相互对话。 大概思路就是给每个角色一个状态机,控制其idle、moving、chatting等状态。通过Area2D的碰撞判断范围内有没有其他角色,然后发起对话请求。被接受后调用大模型的API进行对话。 初次用godot也是头回通过https去访问大模型的api(毕竟gdscript没有python的包),感谢大模型一直帮我解疑答惑顺便给我埋坑让我疯狂debug
实现思路
架构
1 | |
其中状态机部分基本和一般的游戏一样,此处不在赘述,主要写写接入大模型踩的坑。DialogueManager.gd代码附在最后
1 | |
对话模块(DialogueManager.gd)
实现要点:
异步HTTP请求
上下文记忆管理
错误重试机制
其流程为 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
111graph TD
A[生成对话请求] --> B[构建消息体]
B --> C[创建HTTPRequest]
C --> D[发送POST请求]
D --> E{成功?}
E -->|是| F[解析响应]
E -->|否| G[错误处理]
F --> H[更新对话历史]
G --> I[重试机制]
```
目前做了一个简易版本,两个角色之间可以通过调用大模型的api来实现对话。
写的很烂,之后再重构吧。
后面会把这个小demo放到github上。
### 代码
```gdscript
# DialogueManager.gd
class_name DialogueManager
extends Node
## 设置APIKEY 和请求URL以及模型
const BASE_URL = "https://open.bigmodel.cn/api/paas/v4/chat/completions"
const API_KEY = "YOUR_API_KEY"
const MODEL = "glm-4-flash"
const TIMEOUT = 10
var history: Array # character_id -> Array[Message]
var character : CharacterBody2D
var is_getting : bool = false
func _init(parent:CharacterBody2D) -> void:
character = parent
func generate_response(prompt: String = "hello") -> void:
if is_getting:
return
var messages = prepare_messages(prompt)
var headers = [
"Authorization: Bearer " + API_KEY,
"Content-Type: application/json"
]
print(headers)
var payload = {
"model": MODEL,
"messages": messages,
#"tags": ["glm-4", "tool"],
"temperature": 0.7 # You can adjust this parameter as needed
}
var body = JSON.stringify(
{
"model": "glm-4",
"messages": [
{"role": "user", "content": "你好"}
],
"temperature": 0.7
})
#var body = JSON.stringify(payload)
print("body:")
print(body)
var request = HTTPRequest.new()
add_child(request)
# 等待节点初始化完成
await get_tree().process_frame
request.set_timeout(TIMEOUT)
request.request_completed.connect(_on_response_received)
var result = request.request(BASE_URL, headers, HTTPClient.METHOD_POST, body)
print(result)
is_getting = true
print("request send")
func prepare_messages(prompt: String) -> Array:
var context = [
{"role": "system", "content": "假设你是一个普通人,模拟和其他人的对话"},
{"role": "user", "content": prompt}
]
context = history + context
return context
func _on_response_received(result: int, code: int, headers: Array, body: PackedByteArray):
print("response received")
if result != HTTPRequest.RESULT_SUCCESS:
character.receive_message("(思考中...)")
return
var response = JSON.parse_string(body.get_string_from_utf8())
print("response.error:")
print(response.error)
print("content:")
print(response)
if response.error != null:
character.receive_message("(解析API响应失败,请稍后再试...)")
is_getting = false
return
var message = response.choices[0].message.content
character.receive_message(message)
print("message:")
print(message)
# 保存历史记录
var history_entry = {"role": "assistant", "content": message}
history.append(history_entry)
print(history)
is_getting = false
func _clear_history():
history.clear()