Chatterstack 是一种非常简单且直观的方法来处理 ChatGPT API 使用的“对话”变量,同时还为您提供高级功能。
从 pip 安装包:
pip install chatterstack
Chatterstack 有两个选项。基础库和扩展基础库的高级库。
import chatterstack
# The base library if you are only really concerned about mananging
# the conversation variable, it's order, and maybe tracking tokens.
convo = chatterstack . Chatterstack ()
# The advanced library allows you issuing commands from the chat input,
# gives the bot the ability to reach out to you with reminders, etc
convo = chatterstack . ChatterstackAdvanced ()
ChatGPT API 使用“对话”变量来跟踪您与机器人的交互。这是一个字典列表,每个字典代表对话中的一个回合。
这会让你的程序中充斥着这样的东西:
# append the bot's response to your conversation
conversation . append ({ 'role' : response . choices [ 0 ]. message . role , 'content' : response . choices [ 0 ]. message . content })
# to print the last message
print ( conversation [ - 1 ][ "content" ])
荒谬的。
如果您想做任何更高级的事情,例如“将对话变量保持为最多 8 条消息”,或“使系统消息始终成为对话中的最新消息”......好吧,我将省去您的代码片段那些。
由于这些词典的许多方面都是高度可预测的,并且我们通常只用它们执行少数基本任务......
...这意味着我们实际上可以抽象出很多混乱的东西,从而使其使用起来更加直观 - 同时还基本上保留所有固有的灵活性!这是一件很难得的事情。
这是一个使用chatterstack的功能齐全的聊天机器人程序:
import chatterstack
import os
os . environ [ 'OPENAI_API_KEY' ] = 'your_api_key_here'
convo = chatterstack . Chatterstack ()
while True :
convo . user_input ()
convo . send_to_bot ()
convo . print_last_message ()
这就是全部事情!
这就是终端中的对话的样子:
USER: hey, can you help me with something?
ASSISTANT: Sure! What would you like help with?
USER: I need to know if France has a President or a Prime Minister
ASSISTANT: France has both a President and a Prime Minister, which [... bot goes on]
该库的构建直观且灵活,因此您可以通过多种方式轻松更改行为或外观,以满足您的需求。以下是一些基本示例,暂时停留在终端中:
while True :
# Change the user's display name
convo . user_input ( prefix = "ME: " )
# change any of the API arguments
convo . send_to_bot ( model = "gpt-4" , temperature = 1 , max_tokens = 40 )
# change the line spacing of the convo
convo . print_last_message ( prefix = "GPT: " , lines_before = 0 , lines_after = 2 )
# and let's have it print the total tokens after each turn
print ( convo . tokens_total_all )
现在您的对话是与 GPT-4 进行的,对话如下所示,其中包含令牌计数:
ME: hey, can you help me with something?
GPT: Of course! I'm here to help. Please let me know what you need assistance with, and I'll do my best to help you.
28
ME: I need to know if France has a President or a Prime Minister
GPT: France has both a President and a Prime Minister. The President of France is [...bot goes on]
87
下面有关向 API 发送消息的部分提供了有关当前默认值以及更改它们的各种方法的更多信息
user_input()
方法与 python input()
方法相同,不同之处在于它还会自动将用户的输入作为格式正确的字典附加到对话变量中。
convo . user_input ()
如上所示,此方法默认提示用户使用“USER:”,但您可以将其更改为您想要的任何内容
convo . user_input ( "Ask the bot: " )
Ask the bot:
也许您没有使用终端。
或者您可能想在附加输入之前以某种方式更改输入。
有多种方法可以获取任何字符串变量并将它们作为格式正确的字典添加到对话中:
# Use the .add() method. Pass it the role, then the content
convo . add ( "user" , message_string )
# or, use the role-specific methods & just pass the content
convo . add_user ( message_string )
convo . add_assistant ( "I'm a manually added assistant response" )
convo . add_system ( "SYSTEM INSTRUCTIONS - you are a helpful assistant who responds only in JSON" )
如果您想在特定索引处添加消息,而不是将其附加到对话末尾,还可以使用 .insert() :
# Here's the format
convo . insert ( index , role , content )
# example
convo . insert ( 4 , "system" , "IMPORTANT: Remember to not apologize to the user so much" )
chatterstack“send_to_bot”方法是一个标准的 OpenAI API 调用,但它使用起来更简单,并且在后台为您做一些方便的事情。像这样称呼它:
convo . send_to_bot ()
就是这样!
它将负责为您传递所有默认值,并将响应附加到您的对话中。它还为您保留令牌计数(在高级课程中,更多)。
默认情况下,chatterstack 使用这些值:
model = "gpt-3.5-turbo" ,
temperature = 0.8 ,
top_p = 1 ,
frequency_penalty = 0 ,
presence_penalty = 0 ,
max_tokens = 200
有几种方法可以改变这些。选择对您来说最方便的。
最明显的方法是在调用时将它们作为参数传递。例如,如果您想要 GPT-4 和最大 800 个令牌:
convo . send_to_bot ( model = "gpt-4" , max_tokens = 800 )
当您只想使用一些不同的值进行一次调用时,这种方法非常有用。
但是,如果您知道整个对话需要不同的值,您可以在文件顶部的大写字母中定义它们,并使用globals()
字典初始化chatterstack,如下所示:
MODEL = "gpt-4"
TEMPERATURE = 0.6
FREQUENCY_PENALTY = 1.25
MAX_TOKENS = 500
# initialize with 'globals()'
convo = chatterstack . Chatterstack ( user_defaults = globals ())
# and now you can just call it like this again
convo . send_to_bot ()
最后,如果您只想使用样板 OpenAI 调用,您仍然可以这样做!只需将 Chatterstack 的 .list 属性传递给它即可,这是字典的原始列表:
response = openai . ChatCompletion . create (
model = "gpt-3.5-turbo" ,
messages = convo . list , # <--- right here
temperature = 0.9 ,
top_p = 1 ,
frequency_penalty = 0 ,
presence_penalty = 0 ,
max_tokens = 200 ,
)
超级简单:
# Print the "content" of the last message
convo . print_last_message ()
或者,如果您想先对字符串进行格式化......
# This represents/is the content string of the last message
convo . last_message
# So you can do stuff like this:
message_in_caps = convo . last_message . upper ()
# print the message in all upper case
print ( message_in_caps )
哦,是的。我们正在跟踪代币。
# See the tokens used on the last API call
self . last_call_prompt_tokens
self . last_call_full_context_prompt_tokens
self . last_call_completion_tokens
self . last_call_tokens_all
# At any time, check the totals for the whole conversation so far
self . prompt_tokens_total
self . assistant_tokens_total
self . tokens_total_all
有多种方法可以控制对话的顺序,以下是一些:
# Insert message at a specified index
convo . insert ( index , role , content )
# Remove N messages from the end of the list
convo . remove_from_end ( count )
# Remove N messages from the start of the list
convo . remove_from_start ( count )
但更重要的是——对系统消息进行细粒度控制的方法。
系统消息通常用于指示,并且让指示在对话中“最近”出现通常会很有帮助。这意味着在不干扰其他消息的情况下跟踪和移动该消息。
# move your system message to be the most recent message in convo
convo . move_system_to_end ()
# or second to last, etc
convo . move_system_to_end ( minus = 1 )
我个人最喜欢的 - 基本上是我编写整个库的全部原因 -
convo . set_system_lock_index ( - 1 )
向此函数传递正值会将系统消息锁定到索引。每当添加、删除或重新排序消息时,它都会确保系统消息保留在该位置(或尽可能接近该位置)。
向其传递负值会将您的系统消息锁定到从对话结束开始计数的索引(上面的示例将使其始终成为对话中倒数第二条消息)。
注意:目前,这些方法假设您只有一条系统消息
打印对话的格式化版本(非常适合调试)
convo . print_formatted_conversation
默认情况下,打印如下:
System: You are a helpful assistant.
User: hi!
Assistant: Hi! How can I help you?
查看对话的总体统计数据。
convo . summary ()
SUMMARY:
{'total_messages': 2, 'prompt_tokens': 200, 'assistant_tokens': 78, 'total_tokens': 278}
Chatterstack Advanced 类扩展了基类,并且内置了更多功能。它也很容易扩展。
警告:最近对模型的更改似乎破坏了此功能。当你尝试时,它可能有效,也可能无效。它可能需要重新编写才能使用函数调用来再次一致地工作
你现在可以告诉机器人“提醒我晚上 8 点把垃圾拿出去”,或者“提醒我在一小时内把垃圾拿出去”
USER: hey, can you remind me to take the garbage out in an hour
ASSISTANT: Sure! I'll send you a reminder in an hour.
USER:
{...time passes...}
ASSISTANT: Hey! Just a quick reminder to take the garbage out!
该机器人可以跟踪您想要发出的任意数量的提醒。
根据用户输入发出命令:
# saves the conversation to a txt file
USER: [save]
# quit the program
USER: [quit]
默认情况下,您使用[
和]
作为分隔符来发出命令。但您可以将它们更改为您想要的任何内容:
convo.open_command = "{{"
convo.close_command = "}}"
您还可以直接从聊天界面自调用 chatterstack 类本身的任何方法(或设置任何属性):
# if you want to see what is currently in the conversation history
USER: [print_formatted_conversation]
# or how many tokens you have used so far
USER: [print_total_tokens]
# you can even pass arguments to commands
USER: [set_system_lock_index(-2)]
因此,虽然本存储库开头的初始聊天程序示例一开始可能看起来很简单,但您可以看到它确实是您所需要的,因为几乎您想要的任何功能实际上都可以从聊天本身内部调用。
如果您想编写自己的命令,chatterstack 提供了一个简单的接口类来执行此操作,称为ICommand
。
class ICommand :
def execute ( self ):
pass
基本上,您将命令编写为一个类,该类继承自ICommand
类,并具有“执行”方法(这是您希望在调用命令时实际发生的情况。)
这是一个例子:
class ExampleCommand ( ICommand ):
def execute ( self ):
print ( "An example command that prints this statement right here." )
如果您的命令需要参数,您还可以添加一个__init__
方法,并像这样传递它*args
:
class ExampleCommand ( ICommand ):
def __init__ ( self , * args ):
self . args = args
def execute ( self ):
print ( "Example command that print this statement with this extra stuff:" , self . args )
您需要做的最后一件事是通过将触发词或短语添加到 ChatterstackAdvanced 类的init方法中来为您的命令分配触发词或短语。
class ChatterstackAdvanced ( Chatterstack ):
def __init__ ( self , ...)
# ...Lots of other code here...
# This command is already in the class:
self . command_handler . register_command ( 'save' , SaveConversationCommand ( self ))
# Add your new command
self . command_handler . register_command ( "example" , ExampleCommand )
目前有一个 Javascript 版本的 Chatterstack,但我还没有提供它,因为我也不了解 Javascript,并且对其可靠性不太有信心。如果您了解 Javascript,并且愿意提供帮助,请告诉我!