Emergent generative agents
Révision | 8e3dfeca90fc6433155193db83e7e9855306cd7d (tree) |
---|---|
l'heure | 2023-06-12 03:14:32 |
Auteur | Corbin <cds@corb...> |
Commiter | Corbin |
Split thoughts into two agents.
@@ -5,7 +5,6 @@ from datetime import datetime | ||
5 | 5 | import json |
6 | 6 | import os.path |
7 | 7 | import re |
8 | -import random | |
9 | 8 | from string import ascii_uppercase |
10 | 9 | import sys |
11 | 10 | from threading import Lock |
@@ -43,6 +42,7 @@ embedder = SentenceEmbed() | ||
43 | 42 | prologues = { |
44 | 43 | "clock": "Then I checked the time:", |
45 | 44 | "lbi": "Then I tried to interpret what just happened:", |
45 | + "memories": "Then I remembered:", | |
46 | 46 | "thoughts": "Then I thought to myself:", |
47 | 47 | "choice": "Then I chose what to do next:", |
48 | 48 | "irc": "Then I chatted on IRC, channel {subtag}:", |
@@ -100,8 +100,7 @@ class Mind: | ||
100 | 100 | def cb(): |
101 | 101 | d = self.switchTag(tag, subtag) |
102 | 102 | # Breaking the newline invariant... |
103 | - d.addCallback(lambda _: print(self.name, "~!", prefix) | |
104 | - or deferToThread(self.writeRaw, prefix)) | |
103 | + d.addCallback(lambda _: deferToThread(self.writeRaw, prefix)) | |
105 | 104 | # ...so that this inference happens before the newline... |
106 | 105 | d.addCallback(lambda _: force(tokens, self.logits)) |
107 | 106 | d.addCallback(lambda t: gen.tokenizer.decode([t])) |
@@ -136,19 +135,21 @@ class LeftBrainInterpreter(Agent): | ||
136 | 135 | self.mind = mind |
137 | 136 | self.idle = idle |
138 | 137 | def overhear(self, tag, subtag, s): |
139 | - if tag != "thoughts": self.events += 1 | |
140 | - if self.events >= 20: | |
138 | + self.events += 1 | |
139 | + if self.events >= 25: | |
141 | 140 | self.events = 0 |
142 | 141 | d = self.mind.infer(self.tag, self.subtag, "") |
143 | 142 | d.addCallback(lambda s: s.strip() or self.idle()) |
144 | 143 | |
144 | +# NB: Letter 'A' is extra-likely to appear as a token, so don't allow it. | |
145 | +uppercase = ascii_uppercase[1:] | |
145 | 146 | choices = { |
146 | 147 | "irc": "Chat on IRC, channel {subtag}", |
147 | 148 | "thoughts": "Think to myself for a moment", |
148 | 149 | } |
149 | 150 | def choicify(options): |
150 | - return " ".join(f"({c}) {o}" for c, o in zip(ascii_uppercase, options)) | |
151 | -def indexify(c): return ord(c[0].upper()) - ord('A') | |
151 | + return " ".join(f"({c}) {o}" for c, o in zip(uppercase, options)) | |
152 | +def indexify(c): return ord(c[0].upper()) - ord('B') | |
152 | 153 | |
153 | 154 | class ChoiceMaker(Agent): |
154 | 155 | tag = "choice" |
@@ -169,19 +170,31 @@ class ChoiceMaker(Agent): | ||
169 | 170 | prompt = choicify([choices[tag].format(subtag=subtag) |
170 | 171 | for tag, subtag in possibilities]) |
171 | 172 | d = self.mind.forceChoice(self.tag, self.subtag, prompt + " My choice: ", |
172 | - ascii_uppercase[:len(possibilities)]) | |
173 | + uppercase[:len(possibilities)]) | |
173 | 174 | d.addCallback(lambda s: self.dispatch(*possibilities[indexify(s)])) |
174 | 175 | return d |
175 | 176 | |
176 | 177 | class ChainOfThoughts(Agent): |
177 | 178 | tag = "thoughts" |
178 | - def __init__(self, mind, index, seed, idle): | |
179 | + def __init__(self, mind, idle): | |
179 | 180 | self.mind = mind |
181 | + self.idle = idle | |
182 | + | |
183 | + def go(self): | |
184 | + d = self.mind.infer(self.tag, self.subtag, "") | |
185 | + d.addCallback(lambda s: s.strip() or self.idle()) | |
186 | + return d | |
187 | + | |
188 | +class Recall(Agent): | |
189 | + tag = "memories" | |
190 | + def __init__(self, index, seed, idle): | |
180 | 191 | self.index = index |
181 | 192 | self.recentThoughts = deque([seed], maxlen=5) |
182 | 193 | self.idle = idle |
183 | 194 | |
184 | - def go(self): return random.choice([self.reflect, self.cogitate])() | |
195 | + def go(self): | |
196 | + new = self.addRelatedThoughts(self.recentThoughts[-1]) | |
197 | + if not new: return deferLater(reactor, 0.0, self.idle) | |
185 | 198 | |
186 | 199 | def overhear(self, tag, subtag, s): |
187 | 200 | prefix = f"{tag} ({subtag}): " if subtag else tag + ": " |
@@ -197,14 +210,6 @@ class ChainOfThoughts(Agent): | ||
197 | 210 | self.broadcast(thought) |
198 | 211 | return new |
199 | 212 | |
200 | - def cogitate(self): | |
201 | - new = self.addRelatedThoughts(self.recentThoughts[-1]) | |
202 | - if not new: return deferLater(reactor, 0.0, self.idle) | |
203 | - def reflect(self): | |
204 | - d = self.mind.infer(self.tag, self.subtag, "") | |
205 | - d.addCallback(lambda s: s.strip() or self.idle()) | |
206 | - return d | |
207 | - | |
208 | 213 | |
209 | 214 | IRC_LINE_HEAD = re.compile(r"\d{1,2}:\d{1,2}:\d{1,2} <") |
210 | 215 | def breakIRCLine(line): |
@@ -278,6 +283,7 @@ def go(): | ||
278 | 283 | firstStatement = f"I am {title}." |
279 | 284 | thoughtPath = os.path.join(logpath, "thoughts.txt") |
280 | 285 | thoughtIndex = SentenceIndex.fromPath(thoughtPath, embedder) |
286 | + print("~ Thought index:", thoughtIndex.size(), "thoughts") | |
281 | 287 | |
282 | 288 | mind = Mind(title_to_nick(title)) |
283 | 289 | with Timer("initial warmup"): |
@@ -288,12 +294,15 @@ def go(): | ||
288 | 294 | lbi = LeftBrainInterpreter(mind, cm.idle) |
289 | 295 | clock.listeners += lbi, mind |
290 | 296 | |
291 | - thoughts = ChainOfThoughts(mind, thoughtIndex, firstStatement, cm.idle) | |
292 | - thoughts.listeners = lbi, mind | |
293 | - LoopingCall(thoughts.go).start(60 * 3, now=True) | |
297 | + memories = Recall(thoughtIndex, firstStatement, cm.idle) | |
298 | + memories.listeners = lbi, mind | |
299 | + LoopingCall(memories.go).start(60 * 2, now=True) | |
294 | 300 | |
295 | - print("~ Thought index:", thoughtIndex.size(), "thoughts") | |
296 | - factory = IRCFactory(mind, cm, (thoughts, lbi, cm, mind), | |
301 | + thoughts = ChainOfThoughts(mind, cm.idle) | |
302 | + thoughts.listeners = memories, lbi, mind | |
303 | + LoopingCall(thoughts.go).start(60 * 2, now=False) | |
304 | + | |
305 | + factory = IRCFactory(mind, cm, (memories, lbi, cm, mind), | |
297 | 306 | title, |
298 | 307 | character["startingChannels"]) |
299 | 308 | print("~ Connecting factory for:", title) |