Game of Words, Episode II
| 11 min read | by Yugen Omer KoratYou’ve been sitting there for a while now. She just gave a giggle to your mediocre joke. You’ve already covered the “what do you do?” conversation, brought up your trip to the Philippines, and let her go on about her unruly yet adorable younger brother.
The dreaded Awkward Silence starts creeping in…
“Should we… should we get the bill?” you blurt out, having no choice but to accept your fate.
I say, no more! From now on, we can call upon our trusty wingman bot to keep the banter lively and the evening unforgettable!
How, you ask? Quite simple! Keep a topic model of the conversation so far, and whenever the conversation dies out, come up with a new topic that is either a subtopic of one of the topics from the topic model, or a merge of two of them, but is as different as possible from what has been recently discussed (to keep it engaging). This is the key innovation of the approach described in this article.
For a more detailed understanding of the proposal, here is the pseudocode for it (key steps in bold):
Keep 3 dynamically updated objects:
a. A topic model of your conversation so far.
b. A waitlist of topics.
c. A list of previous topic conclusions.
Every time a topic is resolved:
a. Add its conclusion to the list of conclusions.
b. If the waitlist is not empty, pop one of the topics out of it and circle back to it.
c. If the length of the list of topic conclusions reaches a preset threshold (like 3),
and if a new inference can be derived from the conclusions, bring it up.
d. If you have brought up new inferences more than a certain threshold of
times (like 2), generate a new topic based on the topic model. Look for
one which is as different as possible from those that have been recently
discussed, yet is still relevant to one or more topics.
e. Otherwise, continue the conversation normally as described in Episode I.
f. Whenever you switch a topic without resolving it (due to
the <address> action (see the code)), add it to the waitlist.
The bolded parts (actions (c) and (d)) are the key innovations of this article, into which we will go in most detail here. These steps are the essence of the method I’m presenting here to algorithmically keep a conversation alive.
If you’re not sure quite how to do all this, there’s nothing to worry about, as the rest of this article is all about unpacking this conversationalgorithm, how to implement it, and what are some of its use case (except dating of course). Note that the way this algorithm is described here differs slightly from its actual implementation, but this is only so for demo reasons. The way it is described here is the idealized version. We’ve uploaded the full code to our examples repo .
Setup
Eddard and Tyrion have been talking for a while since the last episode, and I’ve trained a topic model over their conversations. If you’re unfamiliar, a topic model is a method for analyzing the patterns of word distribution in a corpus to identify key recurring themes). It contains topics like “Tyrion’s story”, “Eddard’s story”, “The Threat Beyond the Wall”, “The Game of Thrones”, etc’. Note that in the pseudocode, I’ve mentioned a dynamically updated topic model, but in the full code I actually only did it once at the beginning. The reason is that I don’t want to go into the details of how to build topic models in this article, as it obfuscates the main ideas (in future articles, we’ll talk more in-depth about topic modeling for its own sake), so we’re just going to take it for granted. In a fuller implementation of this algorithm, the topic model gets dynamically updated with every k new messages, for some reasonable k.
In the previous episode, we’ve talked about Questions Under Discussions (QUDs), but they are essentially the same as “topics”, just phrased as a question. From now on, these terms will be used interchangeably. We need both of them here, because we talk about topic models on the one hand, and about the algorithm explained in Episode I on the other (which is explained using the terminology QUD due to independent considerations). I realize that this is a potential point of confusion, but I am trying to synthesize these two ideas here so there is bound to be some terminology conflation.
I’ve created a new conversation script with the same basic idea as in Episode I, but we’ve added a few extra modules to implement the pseudocode above. We’ve updated the code so that now, whenever one of the interlocutors changes the topic, if the topic has been declared as resolved, we ask the LLM to generate its main conclusion and add it to a list that’s being updated during the conversation. Otherwise, we add it to a waitlist to be kept for later.
We call such conclusions Answers Under Discussion (AUDs), because they are resolutions of QUDs. When the list of AUDs reaches a certain length threshold (I’ve set it to 3 by default), the program decides to move to the next phase of deducing inferences from the AUDs, as described above. This is the first bold step (step 2c) from the pseudocode, which is used to tie together conclusions that have been reached separately. For example, if you’ve been talking about how she has no time to take care of her flowers, and how she’s debating whether to switch to a hybrid job, you can bring up the fact that if she did switch, it might help with the flower situation, giving the conversation at two minutes worth of oxygen.
Until the length of the AUDs list reaches the threshold, whenever the current topic is resolved we randomly remove a topic from the waitlist and circle back to it. Instead of doing so randomly, it is very possible to prioritize the topics in some way, but we aren’t getting into that here.
But you can’t keep doing that forever. After deducing a new inference from the AUDs a certain number of times, the conversation starts getting repetitive, and this is where you get into Awkward Silence territory. To avoid that, we introduced the second bold step (step 2d), the one which utilizes the topic model – generating a new and interesting topic.
How do we do that, exactly?
Basically, the idea is that we are looking for a topic that is both relevant to the topic model in some way, but hasn’t been recently discussed, because our goal is to avoid repetitivity. So we prioritize topics to relate to based on how far back their previous occurrence in the conversation was recorded.
To generate a relevant topic, several simple ways are to come up with a subtopic of one of the topics from the topic model, generalize one of them into a broader topic, or merge two of them into a hybrid topic. This is important, as if the new topic is not relevant to anything you know you have in common, it might not be interesting to the other participant.
Examples
Let’s look at a few examples of this algorithm in action. At one point, Tyrion had the following QUD in mind:
Speaking of leadership and duty, Eddard, how do you perceive Daenerys Targaryen's rule in the East? Despite her noble intentions, her unfamiliarity with the local customs and power dynamics often led to chaos, didn't it?
But the conversation revolved around Jon’s leadership, so Tyrion had no chance to bring up the connection to Daenerys. This QUD gets pushed into the waitlist instead, and this is what they were discussing at the time: Tyrion: True, Eddard, Jon's unique perspective was valuable. But didn't his highborn upbringing also make it difficult for him to fully understand the struggles of his lowborn brothers?
Eddard: Indeed, Tyrion, Jon's highborn upbringing may have initially hindered his understanding. But don't you think his humility and willingness to learn allowed him to bridge this gap over time?
This discussion is finally resolved with the conclusion:
Jon is showing resilience and leadership at the Wall, which could potentially be used to sway the court's interest and support for the Wall.
This is added as an AUD to their memory (they don’t have a shared memory, but they reach similar conclusions because they are having the same discussion), so now he follows step 2b and pops the QUD about Daenerys out of the waitlist:
Tyrion: True, Eddard, Jon's highborn status did make him a target. But circling back to leadership styles, wouldn't you agree that Daenerys Targaryen's rule in the East, despite its chaos, also showcased her adaptability and resilience in unfamiliar territories?
The conversation continues, and eventually reaches another AUD which is added as well to their memory:
The court perceives the threat beyond the Wall with skepticism, focusing more on power games and self-interest.
Eventually, after he reaches 3 AUDs (the arbitrary threshold I set), and his waitlist is empty, Eddard is ready to proceed to step 2c and deduce implications that emerge by taking together all AUDs. This results in this new QUD:
"Considering the court's resistance to fund the Wall due to their focus on power games and comfort, and given the imminent threat of the awakened enemy, could we leverage the potential leadership of Jon Snow at the Wall to rally public support and pressure the court into reallocating resources for the Wall's fortification?"
The remarkable thing about this QUD is that it takes into account both AUDs we saw above, the one establishing Jon’s leadership skills and the one establishing the court’s skepticism about the threat beyond the wall. This leads to the following response:
Eddard: Indeed, Tyrion. But as you've shown, even amidst mixed reactions, one can still serve the realm effectively. Could not Jon do the same at the Wall?
Which basically claims that despite the difficulties established about the court’s attitude, Jon’s personality can still prevail and influence those in power to send support to the Wall. This sparks another two discussions, and after they conclude we reach the point simulating the conversation dying out. It is now that we need to utilize the topic model trained on their past conversations to come up with a new topic that would refresh the conversation, corresponding to step 2d. This is done using a dedicated prompt that triggers after the deduction step has taken place several times (as said above, I’ve set this threshold to 2), which takes a look at the conversation and the trained topic model, and then comes up with a new topic which is both relevant to the topic model and is different enough from what they have already spoken about during the conversation, which is a simple strategy to keep the conversation interesting. This is what it came up with:
QUD: Tyrion, how do you think the dynamics of the court will change now that Robert is no longer king and Joffrey is set to take the throne?
Eddard: Indeed, Tyrion. With Joffrey set to take the throne, how do you foresee the court's dynamics shifting?
As you can see, based on the conversation they were having, and the topic model of their shared topics, Eddard comes up with a new topic which refreshes the conversation based on the criteria described in the paragraph above, keeping the conversation afloat for a little while longer.
Use Cases
Except for saving your dating life, this algorithm is useful for many text-based applications where user engagement is important. Perhaps the easiest example is in the implementation of AI friends, and various other types of social bots, but I’m sure you can come up with more if you try.
In future episodes, we’ll add more features to the bots, including styles, word choice, and SQL integration for efficient memory management. Till then!