以下以Chat Completion API 为例。

工作原理

LLM 接受Prompt(字符串),生成Response(字符串)。

用户向 LLM 发送请求,LLM 返回响应。请求和响应的组成的交替序列,称为Conversation,请求和响应被统称为Message

Message的格式为:

{
	"role": "角色",
	"content": "内容"
}

role一共有三种:

  • system:系统角色,用于设置LLM的工作方式。对应的Message称为System Message(系统消息)。
  • user:用户角色,用于向LLM发送请求。对应的Message称为User Message(用户消息)。
  • assistant:助手角色,用于向LLM发送响应。对应的Message称为Assistant Message(助手消息)。

Conversation的所有Message都会被转化成字符串,然后被拼接到一起,作为LLMPrompt

这个Prompt是最终输入到LLM的里的东西。

Chat Completion API (或更一般的 LLM 的 API) 没有持久化记忆功能,记忆完全由Prompt决定。

因此用户可以虚构Prompt(即虚构Conversation),来实现记忆功能。

SillyTavern 的主要作用之一就是帮助用户虚构Prompt

SillyTavern 的Conversation的组织方式

{
  messages: [
    {
      role: 'system',
      content: "Write 虚拟角色's next reply in a fictional chat between 虚拟角色 and 小明."
    },
    {
      role: 'user',
      content: '[世界书条目Position: Before Char Def]\n' +
        '\n' +
        '角色定义:小明的好朋友\n' +
        '\n' +
        '[世界书条目Position: After Char Def]\n' +
        '\n' +
        '[Start a new Chat]\n' +
        '\n' +
        '[世界书条目Position: at System depth:200]'
    },
    {
      role: 'assistant',
      content: '[世界书条目Position: at Assistant, depth:100]'
    },
    {
      role: 'user',
      content: '[世界书条目Position: at User, depth:100]\n' +
        '\n' +
        '[世界书条目Position: at System depth:100]'
    },
    {
      role: 'assistant',
      content: '[世界书条目Position: at Assistant, depth: 50]\n\n角色的第一条消息:你好'
    },
    {
      role: 'user',
      content: '[世界书条目Position: at System depth:1]\n' +
        '\n' +
        '最近如何\n' +
        '\n' +
        '[世界书条目Position: at System depth:0]'
    }
  ]
}

观察上述例子可得到以下结论

  • 第一条消息总是系统消息。然后是虚拟角色定义及其定义前后的世界书条目(Before Char Def,虚拟角色定义,After Char Def)。
  • 世界书里的系统条目会被转化为用户消息。
  • depth逻辑上相邻的属于同一角色的Message会被拼接到一起。
  • depth越小,Message越靠近最新的消息。
  • depth相同的Message会根据role的排列(优先级:系统消息>用户消息>助手消息),优先级越大,Message越靠近最新的消息。
  • 用户的最新一条消息处于 depth=0 和 1 之间。