diff --git a/docs/content/C2 Building Systems with the ChatGPT API/2.语言模型,提问范式与 Token Language Models, the Chat Format and Tokens.ipynb b/docs/content/C2 Building Systems with the ChatGPT API/2.语言模型,提问范式与 Token Language Models, the Chat Format and Tokens.ipynb new file mode 100644 index 0000000..a31bb19 --- /dev/null +++ b/docs/content/C2 Building Systems with the ChatGPT API/2.语言模型,提问范式与 Token Language Models, the Chat Format and Tokens.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"markdown","id":"ae5bcee9-6588-4d29-bbb9-6fb351ef6630","metadata":{},"source":["# 第二章 语言模型,提问范式与 Token\n","\n"," - [一、环境配置](#一、环境配置)\n"," - [1.1 加载 API key 和一些 Python 的库。](#1.1-加载-API-key-和一些-Python-的库。)\n"," - [1.2 Helper function 辅助函数](#1.2-Helper-function-辅助函数)\n"," - [二、尝试向模型提问并得到结果](#二、尝试向模型提问并得到结果)\n"," - [三、Tokens](#三、Tokens)\n"," - [四、Helper function 辅助函数 (提问范式)](#四、Helper-function-辅助函数-(提问范式))\n"]},{"cell_type":"markdown","id":"baaf0c21","metadata":{},"source":["在本章中,我们将和您分享大型语言模型(LLM)的工作原理、训练方式以及分词器(tokenizer)等细节对 LLM 输出的影响。我们还将介绍 LLM 的提问范式(chat format),这是一种指定系统消息(system message)和用户消息(user message)的方式,让您了解如何利用这种能力。"]},{"cell_type":"markdown","id":"fe10a390-2461-447d-bf8b-8498db404c44","metadata":{},"source":["## 一、语言模型"]},{"cell_type":"markdown","id":"50317bec","metadata":{},"source":["LLM 可以通过使用监督学习来构建,通过不断预测下一个词来学习。\n","并且,给定一个大的训练集,有数百亿甚至更多的词,你可以创建一个大规模的训练集,你可以从一\n","句话或一段文本的一部分开始,反复要求语言模型学习预测下一个词是什么"]},{"cell_type":"markdown","id":"325afca0","metadata":{},"source":["LLM 主要分为两种类型:基础语言模型(Base LLM)和越来越受欢迎的指令微调语言模型(Instruction Tuned LLM)。基础语言模型通过反复预测下一个词来训练,因此如果我们给它一个 Prompt,比如“从前有一只独角兽”,它可能通过逐词预测来完成一个关于独角兽在魔法森林中与其他独角兽朋友们生活的故事。\n","\n","然而,这种方法的缺点是,如果您给它一个 Prompt,比如“中国的首都是哪里?”,很可能它数据中有一段互联网上关于中国的测验问题列表。这时,它可能会用“中国最大的城市是什么?中国的人口是多少?”等等来回答这个问题。但实际上,您只是想知道中国的首都是什么,而不是列举所有这些问题。然而,指令微调语言模型会尝试遵循 Prompt,并给出“中国的首都是北京”的回答。\n","\n","那么,如何将基础语言模型转变为指令微调语言模型呢?这就是训练一个指令微调语言模型(例如ChatGPT)的过程。首先,您需要在大量数据上训练基础语言模型,因此需要数千亿个单词,甚至更多。这个过程在大型超级计算系统上可能需要数月时间。训练完基础语言模型后,您会通过在一小部分示例上进行进一步的训练,使模型的输出符合输入的指令。例如,您可以请承包商帮助您编写许多指令示例,并对这些指令的正确回答进行训练。这样就创建了一个用于微调的训练集,让模型学会在遵循指令的情况下预测下一个词是什么。\n","\n","之后,为了提高语言模型输出的质量,常见的方法是让人类对许多不同输出进行评级,例如是否有用、是否真实、是否无害等。然后,您可以进一步调整语言模型,增加生成高评级输出的概率。这通常使用强化学习中的人类反馈(RLHF)技术来实现。相较于训练基础语言模型可能需要数月的时间,从基础语言模型到指令微调语言模型的转变过程可能只需要数天时间,使用较小规模的数据集和计算资源。"]},{"cell_type":"code","execution_count":4,"id":"10f34f3b","metadata":{"height":64},"outputs":[{"name":"stdout","output_type":"stream","text":["中国的首都是北京。\n"]}],"source":["from tool import get_completion\n","\n","response = get_completion(\"中国的首都是哪里?\")\n","print(response)"]},{"cell_type":"markdown","id":"b83d4e38-3e3c-4c5a-a949-040a27f29d63","metadata":{},"source":["## 二、Tokens"]},{"cell_type":"markdown","id":"76233527","metadata":{},"source":["到目前为止对 LLM 的描述中,我们将其描述为一次预测一个单词,但实际上还有一个更重要的技术细节。即 **`LLM 实际上并不是重复预测下一个单词,而是重复预测下一个 token`** 。当 LLM 接收到输入时,它将将其转换为一系列 token,其中每个 token 都代表常见的字符序列。例如,对于 \"Learning new things is fun!\" 这句话,每个单词都被转换为一个 token ,而对于较少使用的单词,如 \"Prompting as powerful developer tool\",单词 \"prompting\" 会被拆分为三个 token,即\"prom\"、\"pt\"和\"ing\"。\n","\n","当您要求 ChatGPT 颠倒 \"lollipop\" 的字母时,由于分词器(tokenizer) 将 \"lollipop\" 分解为三个 token,即 \"l\"、\"oll\"、\"ipop\",因此 ChatGPT 难以正确输出字母的顺序。您可以通过在字母之间添加连字符或空格的方式,使分词器将每个字母分解为单独的 token,从而帮助 ChatGPT 更好地认识单词中的每个字母并正确输出它们。"]},{"cell_type":"code","execution_count":5,"id":"cc2d9e40","metadata":{"height":64},"outputs":[{"name":"stdout","output_type":"stream","text":["The reversed letters of \"lollipop\" are \"pillipol\".\n"]}],"source":["# 为了更好展示效果,这里就没有翻译成中文的 Prompt\n","# 注意这里的字母翻转出现了错误,吴恩达老师正是通过这个例子来解释 token 的计算方式\n","response = get_completion(\"Take the letters in lollipop \\\n","and reverse them\")\n","print(response)"]},{"cell_type":"markdown","id":"9d2b14d0-749d-4a79-9812-7b00ace9ae6f","metadata":{},"source":["但是,\"lollipop\" 反过来应该是 \"popillol\""]},{"cell_type":"code","execution_count":6,"id":"37cab84f","metadata":{"height":88},"outputs":[{"name":"stdout","output_type":"stream","text":["p-o-p-i-l-l-o-l\n"]}],"source":["response = get_completion(\"\"\"Take the letters in \\\n","l-o-l-l-i-p-o-p and reverse them\"\"\")\n","\n","print(response)"]},{"cell_type":"markdown","id":"f5a6cb95","metadata":{},"source":["![Tokens.png](../../../figures/docs/C2/tokens.png)"]},{"cell_type":"markdown","id":"8b46bc72","metadata":{},"source":["对于英文输入,一个 token 一般对应 4 个字符或者四分之三个单词;对于中文输入,一个 token 一般对应一个或半个词。\n","\n","不同模型有不同的 token 限制,需要注意的是,这里的 token 限制是输入的 Prompt 和输出的 completion 的 token 数之和,因此输入的 Prompt 越长,能输出的 completion 的上限就越低。\n","\n","ChatGPT3.5-turbo 的 token 上限是 4096。"]},{"cell_type":"markdown","id":"c8b88940-d3ab-4c00-b5c0-31531deaacbd","metadata":{},"source":["## 三、Helper function 辅助函数 (提问范式)\n","下面是课程中用到的辅助函数。\n","下图是 OpenAI 提供的一种提问范式,接下来吴恩达老师就是在演示如何利用这种范式进行更好的提问\n","\n","![Chat-format.png](../../../figures/docs/C2/chat-format.png)"]},{"cell_type":"markdown","id":"9e6b6b3d","metadata":{},"source":["System 信息用于指定模型的规则,例如设定、回答准则等,而 assistant 信息就是让模型完成的具体指令"]},{"cell_type":"code","execution_count":8,"id":"8f89efad","metadata":{"height":200},"outputs":[],"source":["import openai\n","def get_completion_from_messages(messages, \n"," model=\"gpt-3.5-turbo\", \n"," temperature=0, \n"," max_tokens=500):\n"," '''\n"," 封装一个支持更多参数的自定义访问 OpenAI GPT3.5 的函数\n","\n"," 参数: \n"," messages: 这是一个消息列表,每个消息都是一个字典,包含 role(角色)和 content(内容)。角色可以是'system'、'user' 或 'assistant’,内容是角色的消息。\n"," model: 调用的模型,默认为 gpt-3.5-turbo(ChatGPT),有内测资格的用户可以选择 gpt-4\n"," temperature: 这决定模型输出的随机程度,默认为0,表示输出将非常确定。增加温度会使输出更随机。\n"," max_tokens: 这决定模型输出的最大的 token 数。\n"," '''\n"," response = openai.ChatCompletion.create(\n"," model=model,\n"," messages=messages,\n"," temperature=temperature, # 这决定模型输出的随机程度\n"," max_tokens=max_tokens, # 这决定模型输出的最大的 token 数\n"," )\n"," return response.choices[0].message[\"content\"]"]},{"cell_type":"code","execution_count":9,"id":"3d0ef08f","metadata":{"height":149},"outputs":[{"name":"stdout","output_type":"stream","text":["在大海的广漠深处,\n","有一只小鲸鱼欢乐自由;\n","它的身上披着光彩斑斓的袍,\n","跳跃飞舞在波涛的傍。\n","\n","它不知烦恼,只知欢快起舞,\n","阳光下闪亮,活力无边疆;\n","它的微笑如同璀璨的星辰,\n","为大海增添一片美丽的光芒。\n","\n","大海是它的天地,自由是它的伴,\n","快乐是它永恒的干草堆;\n","在浩瀚无垠的水中自由畅游,\n","小鲸鱼的欢乐让人心中温暖。\n","\n","所以啊,让我们感受那欢乐的鲸鱼,\n","尽情舞动,让快乐自由流;\n","无论何时何地,都保持微笑,\n","像鲸鱼一样,活出自己的光芒。\n"]}],"source":["messages = [ \n","{'role':'system', \n"," 'content':'你是一个助理, 并以 Seuss 苏斯博士的风格作出回答。'}, \n","{'role':'user', \n"," 'content':'就快乐的小鲸鱼为主题给我写一首短诗'}, \n","] \n","response = get_completion_from_messages(messages, temperature=1)\n","print(response)"]},{"cell_type":"code","execution_count":10,"id":"e34c399e","metadata":{"height":166},"outputs":[{"name":"stdout","output_type":"stream","text":["从小鲸鱼的快乐笑声中,我们学到了无论遇到什么困难,快乐始终是最好的解药。\n"]}],"source":["# 长度控制\n","messages = [ \n","{'role':'system',\n"," 'content':'你的所有答复只能是一句话'}, \n","{'role':'user',\n"," 'content':'写一个关于快乐的小鲸鱼的故事'}, \n","] \n","response = get_completion_from_messages(messages, temperature =1)\n","print(response)"]},{"cell_type":"code","execution_count":11,"id":"0ca678de","metadata":{"height":181},"outputs":[{"name":"stdout","output_type":"stream","text":["在海洋的深处住着一只小鲸鱼,它总是展开笑容在水中翱翔,快乐无边的时候就会跳起华丽的舞蹈。\n"]}],"source":["# 以上结合\n","messages = [ \n","{'role':'system',\n"," 'content':'你是一个助理, 并以 Seuss 苏斯博士的风格作出回答,只回答一句话'}, \n","{'role':'user',\n"," 'content':'写一个关于快乐的小鲸鱼的故事'},\n","] \n","response = get_completion_from_messages(messages, temperature =1)\n","print(response)"]},{"cell_type":"code","execution_count":12,"id":"89a70c79","metadata":{"height":370},"outputs":[],"source":["def get_completion_and_token_count(messages, \n"," model=\"gpt-3.5-turbo\", \n"," temperature=0, \n"," max_tokens=500):\n"," \"\"\"\n"," 使用 OpenAI 的 GPT-3 模型生成聊天回复,并返回生成的回复内容以及使用的 token 数量。\n","\n"," 参数:\n"," messages: 聊天消息列表。\n"," model: 使用的模型名称。默认为\"gpt-3.5-turbo\"。\n"," temperature: 控制生成回复的随机性。值越大,生成的回复越随机。默认为 0。\n"," max_tokens: 生成回复的最大 token 数量。默认为 500。\n","\n"," 返回:\n"," content: 生成的回复内容。\n"," token_dict: 包含'prompt_tokens'、'completion_tokens'和'total_tokens'的字典,分别表示提示的 token 数量、生成的回复的 token 数量和总的 token 数量。\n"," \"\"\"\n"," response = openai.ChatCompletion.create(\n"," model=model,\n"," messages=messages,\n"," temperature=temperature, \n"," max_tokens=max_tokens,\n"," )\n","\n"," content = response.choices[0].message[\"content\"]\n"," \n"," token_dict = {\n","'prompt_tokens':response['usage']['prompt_tokens'],\n","'completion_tokens':response['usage']['completion_tokens'],\n","'total_tokens':response['usage']['total_tokens'],\n"," }\n","\n"," return content, token_dict"]},{"cell_type":"code","execution_count":13,"id":"cfd8fbd4","metadata":{"height":146},"outputs":[{"name":"stdout","output_type":"stream","text":["在大海的深处,有一只小鲸鱼,\n","它快乐地游来游去,像一只小小的鱼。\n","它的皮肤光滑又湛蓝,像天空中的云朵,\n","它的眼睛明亮又温柔,像夜空中的星星。\n","\n","它和海洋为伴,一起跳跃又嬉戏,\n","它和鱼儿们一起,快乐地游来游去。\n","它喜欢唱歌又跳舞,给大家带来欢乐,\n","它的声音甜美又动听,像音乐中的节奏。\n","\n","小鲸鱼是快乐的使者,给世界带来笑声,\n","它的快乐是无穷的,永远不会停止。\n","让我们跟随小鲸鱼,一起快乐地游来游去,\n","在大海的宽阔中,找到属于我们的快乐之地。\n"]}],"source":["messages = [ \n","{'role':'system', \n"," 'content':'你是一个助理, 并以 Seuss 苏斯博士的风格作出回答。'}, \n","{'role':'user', \n"," 'content':'就快乐的小鲸鱼为主题给我写一首短诗'}, \n","] \n","response, token_dict = get_completion_and_token_count(messages)\n","print(response)"]},{"cell_type":"code","execution_count":14,"id":"352ad320","metadata":{"height":30},"outputs":[{"name":"stdout","output_type":"stream","text":["{'prompt_tokens': 67, 'completion_tokens': 293, 'total_tokens': 360}\n"]}],"source":["print(token_dict)"]},{"cell_type":"markdown","id":"d7f65685","metadata":{},"source":["最后,我们认为 Prompt 对 AI 应用开发的革命性影响仍未得到充分重视低。在传统的监督机器学习工作流中,如果想要构建一个可以将餐厅评论分类为正面或负面的分类器,首先需要获取一大批带有标签的数据,可能需要几百个,这个过程可能需要几周,甚至一个月的时间。接着,您需要在这些数据上训练一个模型,找到一个合适的开源模型,并进行模型的调整和评估,这个阶段可能需要几天、几周,甚至几个月的时间。最后,您可能需要使用云服务来部署模型,将模型上传到云端,并让它运行起来,才能最终调用您的模型。整个过程通常需要一个团队数月时间才能完成。\n","\n","相比之下,使用基于 Prompt 的机器学习方法,当您有一个文本应用时,只需提供一个简单的 Prompt 就可以了。这个过程可能只需要几分钟,如果需要多次迭代来得到有效的 Prompt 的话,最多几个小时即可完成。在几天内(尽管实际情况通常是几个小时),您就可以通过 API 调用来运行模型,并开始使用。一旦您达到了这个步骤,只需几分钟或几个小时,就可以开始调用模型进行推理。因此,以前可能需要花费六个月甚至一年时间才能构建的应用,现在只需要几分钟或几个小时,最多是几天的时间,就可以使用 Prompt 构建起来。这种方法正在极大地改变 AI 应用的快速构建方式。\n","\n","需要注意的是,这种方法适用于许多非结构化数据应用,特别是文本应用,以及越来越多的视觉应用,尽管目前的视觉技术仍在发展中。但它并不适用于结构化数据应用,也就是那些处理 Excel 电子表格中大量数值的机器学习应用。然而,对于适用于这种方法的应用,AI 组件可以被快速构建,并且正在改变整个系统的构建工作流。构建整个系统可能仍然需要几天、几周或更长时间,但至少这部分可以更快地完成。"]},{"cell_type":"markdown","id":"cfe248d6","metadata":{},"source":["下一个章中,我们将展示如何利用这些组件来评估客户服务助手的输入。\n","这将是本课程中构建在线零售商客户服务助手的更完整示例的一部分。"]},{"cell_type":"markdown","id":"195a6733","metadata":{},"source":["## 四、英文版"]},{"cell_type":"markdown","id":"82212f83","metadata":{},"source":["**1.1 语言模型**"]},{"cell_type":"code","execution_count":15,"id":"6cc72ba8","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["The capital of China is Beijing.\n"]}],"source":["response = get_completion(\"What is the capital of China?\")\n","print(response)"]},{"cell_type":"markdown","id":"e3aebf26","metadata":{},"source":["**2.1 Tokens**"]},{"cell_type":"code","execution_count":16,"id":"0ad0d49a","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["The reversed letters of \"lollipop\" are \"pillipol\".\n"]}],"source":["response = get_completion(\"Take the letters in lollipop and reverse them\")\n","print(response)"]},{"cell_type":"code","execution_count":17,"id":"1b4ac3d6","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["p-o-p-i-l-l-o-l\n"]}],"source":["response = get_completion(\"\"\"Take the letters in \\\n","l-o-l-l-i-p-o-p and reverse them\"\"\")\n","\n","print(response)"]},{"cell_type":"markdown","id":"7ab33697","metadata":{},"source":["**3.1 提问范式**"]},{"cell_type":"code","execution_count":18,"id":"9882f4d9","metadata":{},"outputs":[],"source":["def get_completion_from_messages(messages, \n"," model=\"gpt-3.5-turbo\", \n"," temperature=0, \n"," max_tokens=500):\n"," '''\n"," 封装一个支持更多参数的自定义访问 OpenAI GPT3.5 的函数\n","\n"," 参数: \n"," messages: 这是一个消息列表,每个消息都是一个字典,包含 role(角色)和 content(内容)。角色可以是'system'、'user' 或 'assistant’,内容是角色的消息。\n"," model: 调用的模型,默认为 gpt-3.5-turbo(ChatGPT),有内测资格的用户可以选择 gpt-4\n"," temperature: 这决定模型输出的随机程度,默认为0,表示输出将非常确定。增加温度会使输出更随机。\n"," max_tokens: 这决定模型输出的最大的 token 数。\n"," '''\n"," response = openai.ChatCompletion.create(\n"," model=model,\n"," messages=messages,\n"," temperature=temperature, # 这决定模型输出的随机程度\n"," max_tokens=max_tokens, # 这决定模型输出的最大的 token 数\n"," )\n"," return response.choices[0].message[\"content\"]"]},{"cell_type":"code","execution_count":19,"id":"ca6fd80c","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["Oh, a carrot so happy and bright,\n","With a vibrant orange hue, oh what a sight!\n","It grows in the garden, so full of delight,\n","A veggie so cheery, it shines in the light.\n","\n","Its green leaves wave with such joyful glee,\n","As it dances and sways, so full of glee.\n","With a crunch when you bite, so wonderfully sweet,\n","This happy little carrot is quite a treat!\n","\n","From the soil, it sprouts, reaching up to the sky,\n","With a joyous spirit, it can't help but try.\n","To bring smiles to faces and laughter to hearts,\n","This happy little carrot, a work of art!\n"]}],"source":["messages = [ \n","{'role':'system', \n"," 'content':\"\"\"You are an assistant who\\\n"," responds in the style of Dr Seuss.\"\"\"}, \n","{'role':'user', \n"," 'content':\"\"\"write me a very short poem\\\n"," about a happy carrot\"\"\"}, \n","] \n","response = get_completion_from_messages(messages, temperature=1)\n","print(response)"]},{"cell_type":"code","execution_count":20,"id":"ae0d1308","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["Once upon a time, there was a happy carrot named Crunch who lived in a beautiful vegetable garden.\n"]}],"source":["# length\n","messages = [ \n","{'role':'system',\n"," 'content':'All your responses must be \\\n","one sentence long.'}, \n","{'role':'user',\n"," 'content':'write me a story about a happy carrot'}, \n","] \n","response = get_completion_from_messages(messages, temperature =1)\n","print(response)"]},{"cell_type":"code","execution_count":21,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["Once there was a carrot named Larry, he was jolly and bright orange, never wary.\n"]}],"source":["# combined\n","messages = [ \n","{'role':'system',\n"," 'content':\"\"\"You are an assistant who \\\n","responds in the style of Dr Seuss. \\\n","All your responses must be one sentence long.\"\"\"}, \n","{'role':'user',\n"," 'content':\"\"\"write me a story about a happy carrot\"\"\"},\n","] \n","response = get_completion_from_messages(messages, \n"," temperature =1)\n","print(response)"]},{"cell_type":"code","execution_count":22,"id":"944c0a78","metadata":{},"outputs":[],"source":["def get_completion_and_token_count(messages, \n"," model=\"gpt-3.5-turbo\", \n"," temperature=0, \n"," max_tokens=500):\n"," \"\"\"\n"," 使用 OpenAI 的 GPT-3 模型生成聊天回复,并返回生成的回复内容以及使用的 token 数量。\n","\n"," 参数:\n"," messages: 聊天消息列表。\n"," model: 使用的模型名称。默认为\"gpt-3.5-turbo\"。\n"," temperature: 控制生成回复的随机性。值越大,生成的回复越随机。默认为 0。\n"," max_tokens: 生成回复的最大 token 数量。默认为 500。\n","\n"," 返回:\n"," content: 生成的回复内容。\n"," token_dict: 包含'prompt_tokens'、'completion_tokens'和'total_tokens'的字典,分别表示提示的 token 数量、生成的回复的 token 数量和总的 token 数量。\n"," \"\"\"\n"," response = openai.ChatCompletion.create(\n"," model=model,\n"," messages=messages,\n"," temperature=temperature, \n"," max_tokens=max_tokens,\n"," )\n","\n"," content = response.choices[0].message[\"content\"]\n"," \n"," token_dict = {\n","'prompt_tokens':response['usage']['prompt_tokens'],\n","'completion_tokens':response['usage']['completion_tokens'],\n","'total_tokens':response['usage']['total_tokens'],\n"," }\n","\n"," return content, token_dict"]},{"cell_type":"code","execution_count":23,"id":"7363bc60","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["Oh, the happy carrot, so bright and orange,\n","Grown in the garden, a joyful forage.\n","With a smile so wide, from top to bottom,\n","It brings happiness, oh how it blossoms!\n","\n","In the soil it grew, with love and care,\n","Nourished by sunshine, fresh air to share.\n","Its leaves so green, reaching up so high,\n","A happy carrot, oh my, oh my!\n","\n","With a crunch and a munch, it's oh so tasty,\n","Filled with vitamins, oh so hasty.\n","A happy carrot, a delight to eat,\n","Bringing joy and health, oh what a treat!\n","\n","So let's celebrate this veggie so grand,\n","With a happy carrot in each hand.\n","For in its presence, we surely find,\n","A taste of happiness, one of a kind!\n"]}],"source":["messages = [\n","{'role':'system', \n"," 'content':\"\"\"You are an assistant who responds\\\n"," in the style of Dr Seuss.\"\"\"}, \n","{'role':'user',\n"," 'content':\"\"\"write me a very short poem \\ \n"," about a happy carrot\"\"\"}, \n","] \n","response, token_dict = get_completion_and_token_count(messages)\n","print(response)"]},{"cell_type":"code","execution_count":24,"id":"c1fa09dd","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["{'prompt_tokens': 37, 'completion_tokens': 164, 'total_tokens': 201}\n"]}],"source":["print(token_dict)"]}],"metadata":{"kernelspec":{"display_name":"Python 3.9.6 64-bit","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.11"},"vscode":{"interpreter":{"hash":"31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"}}},"nbformat":4,"nbformat_minor":5} diff --git a/figures/docs/C2/chat-format.png b/figures/docs/C2/chat-format.png new file mode 100644 index 0000000..f52b0c7 Binary files /dev/null and b/figures/docs/C2/chat-format.png differ diff --git a/figures/docs/C2/tokens.png b/figures/docs/C2/tokens.png new file mode 100644 index 0000000..7524b8e Binary files /dev/null and b/figures/docs/C2/tokens.png differ