Constructing an AI-Powered Language Studying App: Studying From Two AI Chatting | by Shuai Guo | Jun, 2023


Now that now we have a transparent understanding of what we need to construct and the instruments to construct it, it’s time to roll up our sleeves and dive into the code! On this part, we’re going to give attention to the nuts and bolts of making our dual-chatbot interplay. First, we’ll discover the category definition for a single chatbot after which develop on this to create a dual-chatbot class, enabling our two chatbots to work together. We’ll save the design of the app interface utilizing Streamlit for Part 4.

3.1 Growing a single chatbot

On this subsection, we are going to develop a single chatbot collectively, which can later be built-in into the dual-chatbot system. Let’s begin with the general class design, then shift our consideration to immediate engineering.

🏗️ Class Design

Our chatbot class ought to allow the administration of a person chatbot. This entails instantiating a chatbot with a user-specified LLM as its spine, offering directions based mostly on the person’s intent, and facilitating interactive multi-round conversations. With that in thoughts, let’s begin coding.

First, import the required libraries:

import os
import openai
from langchain.prompts import (
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.reminiscence import ConversationBufferMemory

Subsequent, we outline the category constructor:

class Chatbot:
"""Class definition for a single chatbot with reminiscence, created with LangChain."""

def __init__(self, engine):
"""Choose spine massive language mannequin, in addition to instantiate
the reminiscence for creating language chain in LangChain.

# Instantiate llm
if engine == 'OpenAI':
# Reminder: have to arrange openAI API key
# (e.g., through atmosphere variable OPENAI_API_KEY)
self.llm = ChatOpenAI(

elevate KeyError("Presently unsupported chat mannequin sort!")

# Instantiate reminiscence
self.reminiscence = ConversationBufferMemory(return_messages=True)

Presently, you may solely select to make use of the native OpenAI API. Nonetheless, including extra backend LLMs is easy since LangChain helps varied sorts (e.g., Azure OpenAI endpoint, Anthropic chat fashions, PaLM API on Google Vertex AI, and so on.).

Apart from LLM, one other essential element we have to instantiate is reminiscence, which tracks the dialog historical past. Right here, we use ConversationBufferMemory for this goal, which merely prepends the previous few inputs/outputs to the present enter of the chatbot. That is the best reminiscence sort supplied in LangChain and it’s adequate for our present goal.

For an entire overview of different sorts of reminiscence, please consult with the official docs.

Shifting on, we have to have a category methodology that permits us to present directions to the chatbot and make conversations with it. That is what self.instruct() for:

def instruct(self, position, oppo_role, language, situation, 
session_length, proficiency_level,
learning_mode, starter=False):
"""Decide the context of chatbot interplay.

# Outline language settings
self.position = position
self.oppo_role = oppo_role
self.language = language
self.situation = situation
self.session_length = session_length
self.proficiency_level = proficiency_level
self.learning_mode = learning_mode
self.starter = starter

# Outline immediate template
immediate = ChatPromptTemplate.from_messages([

# Create dialog chain
self.dialog = ConversationChain(reminiscence=self.reminiscence, immediate=immediate,
llm=self.llm, verbose=False)

  • We outline a few settings to permit customers to customise their studying expertise.

Along with what has been talked about in “Part 1 Challenge Overview”, now we have 4 new attributes:

self.position/self.oppo_role: this attribute takes the type of a dictionary that information the position identify and corresponding actions. As an example:

self.position = {'identify': 'Buyer', 'motion': 'ordering meals'}

self.oppo_role represents the position taken by the opposite chatbot engaged within the dialog with the present chatbot. It’s important as a result of the present chatbot wants to grasp who it’s speaking with, offering needed contextual info.

self.situation units the stage for the dialog. For “dialog” studying mode, self.situationrepresents the place the place the dialog
is going on; for “debate” mode, self.situationrepresents the debating subject.

Lastly, self.starter is only a boolean flag to point if the present chatbot will provoke the dialog.

  • We construction the immediate for the chatbot.

In OpenAI, a chat mannequin usually takes an inventory of messages as enter and returns a model-generated message as output. LangChain helps SystemMessage,AIMessage, HumanMessage: SystemMessage helps set the conduct of the chatbot, AIMessage shops earlier chatbot responses, and HumanMessage gives requests or feedback for the chatbot to reply to.

LangChain conveniently provides PromptTemplate to streamline immediate technology and ingestion. For a chatbot utility, we have to specify the PromptTemplate for all three message sorts. Essentially the most important piece is setting the SystemMessage, which controls the chatbot’s conduct. Now we have a separate methodology, self._specify_system_message(), to deal with this, which we’ll talk about intimately later.

  • Lastly, we convey all of the items collectively and assemble a ConversationChain.

🖋️ Immediate Design

Our focus now turns to guiding the chatbot in collaborating within the dialog as desired by the person. To this finish, now we have the self._specify_system_message() methodology. The signature of this methodology is proven under:

def _specify_system_message(self):
"""Specify the conduct of the chatbot, which consists of the next

- common context: conducting dialog/debate underneath given situation
- the language spoken
- goal of the simulated dialog/debate
- language complexity requirement
- change size requirement
- different nuance constraints

immediate: directions for the chatbot.

Primarily, this methodology compiles a string, which can then be fed into the SystemMessagePromptTemplate.from_template() to instruct the chatbot, as demonstrated within the definition of the self.instruct() methodology above. We’ll dissect this “lengthy string” within the following to grasp how every language studying requirement is integrated into the immediate.

1️⃣ Session size

The session size is managed by immediately specifying the utmost variety of exchanges that may occur inside one session. These numbers are hard-coded in the meanwhile.

# Decide the variety of exchanges between two bots
exchange_counts_dict = {
'Brief': {'Dialog': 8, 'Debate': 4},
'Lengthy': {'Dialog': 16, 'Debate': 8}
exchange_counts = exchange_counts_dict[self.session_length][self.learning_mode]

2️⃣ Variety of sentences the chatbot can say in a single change

Other than limiting the whole variety of allowed exchanges, it’s additionally useful to limit how a lot a chatbot can say inside one change, or equivalently, the variety of sentences.

In my experiments, there’s normally no have to constrain this in “dialog” mode, because the chatbot mimics a real-life dialogue and tends to talk at an affordable size. Nonetheless, in “debate” mode, it’s essential to impose a restrict. In any other case, the chatbot could proceed talking, ultimately producing an “essay” 😆.

Just like limiting the session size, the numbers that limit the speech size are additionally hard-coded and correspond with the person’s proficiency degree within the goal language:

# Decide variety of sentences in a single debate spherical
argument_num_dict = {
'Newbie': 4,
'Intermediate': 6,
'Superior': 8

3️⃣ Decide speech complexity

Right here, we regulate the complexity degree of the language the chatbot can use:

if self.proficiency_level == 'Newbie':
lang_requirement = """use as fundamental and easy vocabulary and
sentence buildings as potential. Should keep away from idioms, slang,
and sophisticated grammatical constructs."""

elif self.proficiency_level == 'Intermediate':
lang_requirement = """use a wider vary of vocabulary and quite a lot of sentence buildings.
You possibly can embrace some idioms and colloquial expressions,
however keep away from extremely technical language or advanced literary expressions."""

elif self.proficiency_level == 'Superior':
lang_requirement = """use subtle vocabulary, advanced sentence buildings, idioms,
colloquial expressions, and technical language the place acceptable."""

elevate KeyError('Presently unsupported proficiency degree!')

4️⃣ Put all the pieces collectively!

Right here’s what the instruction seems like for various studying modes:

# Compile bot directions 
if self.learning_mode == 'Dialog':
immediate = f"""You might be an AI that's good at role-playing.
You might be simulating a typical dialog occurred {self.situation}.
On this situation, you're taking part in as a {self.position['name']} {self.position['action']}, chatting with a
{self.oppo_role['name']} {self.oppo_role['action']}.
Your dialog ought to solely be performed in {self.language}. Don't translate.
This simulated {self.learning_mode} is designed for {self.language} language learners to study real-life
conversations in {self.language}. It's best to assume the learners' proficiency degree in
{self.language} is {self.proficiency_level}. Subsequently, it is best to {lang_requirement}.
It's best to end the dialog inside {exchange_counts} exchanges with the {self.oppo_role['name']}.
Make your dialog with {self.oppo_role['name']} pure and typical within the thought of situation in
{self.language} cultural."""

elif self.learning_mode == 'Debate':
immediate = f"""You might be an AI that's good at debating.
You at the moment are engaged in a debate with the next subject: {self.situation}.
On this debate, you take on the position of a {self.position['name']}.
At all times keep in mind your stances within the debate.
Your debate ought to solely be performed in {self.language}. Don't translate.
This simulated debate is designed for {self.language} language learners to
study {self.language}. It's best to assume the learners' proficiency degree in {self.language}
is {self.proficiency_level}. Subsequently, it is best to {lang_requirement}.
You'll change opinions with one other AI (who performs the {self.oppo_role['name']} position)
{exchange_counts} occasions.
Everytime you communicate, you may solely communicate not more than
{argument_num_dict[self.proficiency_level]} sentences."""

elevate KeyError('Presently unsupported studying mode!')

5️⃣ Who speaks first?

Lastly, we instruct the chatbot whether or not it ought to communicate first or look ahead to the response from the opponent AI:

# Give bot directions
if self.starter:
# In case the present bot is the primary one to talk
immediate += f"You might be main the {self.learning_mode}. n"

# In case the present bot is the second to talk
immediate += f"Watch for the {self.oppo_role['name']}'s assertion."

Now now we have accomplished the immediate design 🎉 As a fast abstract, that is what now we have developed to this point:

The developed single chatbot class.
The one chatbot class. (Picture by writer)

3.2 Growing a dual-chatbot system

Now we arrive on the thrilling half! On this subsection, we are going to develop a dual-chatbot class to let two chatbots work together with one another 💬💬

🏗️ Class Design

Because of the beforehand developed single Chatbot class, we are able to effortlessly instantiate two chatbots within the class constructor:

class DualChatbot:
"""Class definition for dual-chatbots interplay system,
created with LangChain."""

def __init__(self, engine, role_dict, language, situation, proficiency_level,
learning_mode, session_length):

# Instantiate two chatbots
self.engine = engine
self.proficiency_level = proficiency_level
self.language = language
self.chatbots = role_dict
for okay in role_dict.keys():
self.chatbots[k].replace({'chatbot': Chatbot(engine)})

# Assigning roles for 2 chatbots
language=language, situation=situation,
learning_mode=learning_mode, starter=True)

language=language, situation=situation,
learning_mode=learning_mode, starter=False)

# Add session size
self.session_length = session_length

# Put together dialog

The self.chatbots is a dictionary designed to retailer info associated to each bots:

# For "dialog" mode
self.chatbots= {
'role1': {'identify': 'Buyer',
'motion': 'ordering meals',
'chatbot': Chatbot()},
'role2': {'identify': 'Waitstaff',
'motion': 'taking the order',
'chatbot': Chatbot()}

# For "debate" mode
self.chatbots= {
'role1': {'identify': 'Proponent',
'chatbot': Chatbot()},
'role2': {'identify': 'Opponent',
'chatbot': Chatbot()}

The self._reset_conversation_history serves to provoke a contemporary dialog historical past and supply the preliminary directions to the chatbots:

def _reset_conversation_history(self):
"""Reset the dialog historical past.
# Placeholder for dialog historical past
self.conversation_history = []

# Inputs for 2 chatbots
self.input1 = "Begin the dialog."
self.input2 = ""

To facilitate interplay between the 2 chatbots, we make use of self.step() methodology. This methodology permits for one spherical of interplay between the 2 bots:

def step(self):
"""Make one change spherical between two chatbots.

# Chatbot1 speaks
output1 = self.chatbots['role1']['chatbot'].dialog.predict(enter=self.input1)
self.conversation_history.append({"bot": self.chatbots['role1']['name'], "textual content": output1})

# Cross output of chatbot1 as enter to chatbot2
self.input2 = output1

# Chatbot2 speaks
output2 = self.chatbots['role2']['chatbot'].dialog.predict(enter=self.input2)
self.conversation_history.append({"bot": self.chatbots['role2']['name'], "textual content": output2})

# Cross output of chatbot2 as enter to chatbot1
self.input1 = output2

# Translate responses
translate1 = self.translate(output1)
translate2 = self.translate(output2)

return output1, output2, translate1, translate2

Discover that now we have embedded a technique known as self.translate(). The aim of this methodology is to translate the script into English. This performance might be helpful for language learners as they will perceive the that means of the dialog generated within the goal language.

To realize the interpretation performance, we are able to make use of the fundamental LLMChain, which requires a backend LLM mannequin and a immediate for instruction:

  def translate(self, message):
"""Translate the generated script into English.

if self.language == 'English':
# No translation carried out
translation = 'Translation: ' + message

# Instantiate translator
if self.engine == 'OpenAI':
# Reminder: have to arrange openAI API key
# (e.g., through atmosphere variable OPENAI_API_KEY)
self.translator = ChatOpenAI(

elevate KeyError("Presently unsupported translation mannequin sort!")

# Specify instruction
instruction = """Translate the next sentence from {src_lang}
(supply language) to {trg_lang} (goal language).
Right here is the sentence in supply language: n

immediate = PromptTemplate(
input_variables=["src_lang", "trg_lang", "src_input"],

# Create a language chain
translator_chain = LLMChain(llm=self.translator, immediate=immediate)
translation = translator_chain.predict(src_lang=self.language,

return translation

Lastly, it might be useful for language learners to have a abstract of the important thing language studying factors of the generated dialog script, be it key vocabulary, grammar factors, or operate phrases. For that, we are able to embrace a self.abstract() methodology:

def abstract(self, script):
"""Distill key language studying factors from the generated scripts.

# Instantiate abstract bot
if self.engine == 'OpenAI':
# Reminder: have to arrange openAI API key
# (e.g., through atmosphere variable OPENAI_API_KEY)
self.summary_bot = ChatOpenAI(

elevate KeyError("Presently unsupported abstract mannequin sort!")

# Specify instruction
instruction = """The next textual content is a simulated dialog in
{src_lang}. The objective of this textual content is to help {src_lang} learners to study
real-life utilization of {src_lang}. Subsequently, your process is to summarize the important thing
studying factors based mostly on the given textual content. Particularly, it is best to summarize
the important thing vocabulary, grammar factors, and performance phrases that might be essential
for college students studying {src_lang}. Your abstract needs to be performed in English, however
use examples from the textual content within the authentic language the place acceptable.
Bear in mind your goal college students have a proficiency degree of
{proficiency} in {src_lang}. You summarization should match with their
proficiency degree.

The dialog is: n

immediate = PromptTemplate(
input_variables=["src_lang", "proficiency", "script"],

# Create a language chain
summary_chain = LLMChain(llm=self.summary_bot, immediate=immediate)
abstract = summary_chain.predict(src_lang=self.language,

return abstract

Just like the self.translate() methodology, we employed a fundamental LLMChain to carry out the specified process. Notice that we explicitly ask the language mannequin to summarize key language studying factors based mostly on the person’s proficiency degree.

With that, now we have accomplished the event of the dual-chatbot class 🥂 As a fast abstract, that is what now we have developed to this point:

Chatbot class based on LangChain
The one chatbot & Twin-chatbot class. (Picture by writer)


Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button