### NativeTavern 功能设计文档(供 Go + React 集成参考) --- ## 总览 本项目中,这几个模块的作用和依赖关系如下: - **聊天功能(Chat)**:统一负责从「用户消息」到「LLM 调用」再到「写回消息」的完整流程。 - **提示词管理(Prompt Management)**:把系统提示、人设、世界信息、作者注释、历史等拆成可配置的「段」,决定顺序与注入深度。 - **宏系统(Macros)**:在构建 prompt 时,根据上下文展开 `{{user}}`、`{{char}}`、`{{time}}`、`{{random}}` 等占位符。 - **变量系统(Variables)**:提供全局 / 每会话变量 + `{{setvar}}` / `{{getvar}}` 等变量宏,支持状态机、计数等逻辑。 - **思维链支持(Chain-of-Thought / Reasoning)**:对 o1/o3、Claude、Gemini 等的推理内容单独解析、存储,并在前端单独渲染。 Go + React 中建议同样分层:**数据模型(DB) + 领域服务(Go) + 状态/UI(React)**。 --- ## 一、变量系统(Variables System) ### 1.1 目标 - 支持两类变量: - **全局变量**:跨所有聊天共用,用户级。 - **本地变量**:每个 chat 独立,存到 chat metadata。 - 通过宏语法在文本中读写变量,如: - `{{setvar::score::10}}` - `{{incvar::turn}}` - `{{getvar::user_name}}` ### 1.2 数据模型(示例) 后端可参考: ```go // 全局变量:按 userId 存 type GlobalVariables map[string]any // name -> value // 本地变量:按 chatId 存 type LocalVariables map[string]map[string]any // chatId -> (name -> value) type ChatMetadata struct { Variables map[string]any `json:"variables,omitempty"` // ... 其他 metadata 字段 ... } ``` - 全局变量:存 Redis / Postgres JSONB / KV,按 userId 分片。 - 本地变量:挂在 `chats.metadata.variables` 字段。 ### 1.3 变量服务接口(Go) ```go type VariablesService interface { // 全局变量 GetGlobal(userId, name string) any SetGlobal(userId, name string, v any) error AddGlobal(userId, name string, delta any) (any, error) IncGlobal(userId, name string) (any, error) DecGlobal(userId, name string) (any, error) // 本地变量(带 chatId) GetLocal(userId, chatId, name string) any SetLocal(userId, chatId, name string, v any) error AddLocal(userId, chatId, name string, delta any) (any, error) IncLocal(userId, chatId, name string) (any, error) DecLocal(userId, chatId, name string) (any, error) LoadLocalFromMetadata(chatId string, metaVars map[string]any) ExportLocalToMetadata(chatId string) map[string]any } ``` ### 1.4 变量宏语法 支持以下语法(与项目一致): - 本地变量: - `{{setvar::name::value}}` - `{{addvar::name::value}}` - `{{incvar::name}}` - `{{decvar::name}}` - `{{getvar::name}}` - 全局变量: - `{{setglobalvar::name::value}}` - `{{addglobalvar::name::value}}` - `{{incglobalvar::name}}` - `{{decglobalvar::name}}` - `{{getglobalvar::name}}` 解析函数示例: ```go func ProcessVariableMacros(input, userId string, chatId *string, vars VariablesService) (string, error) ``` 实现建议: 1. 为每种宏写一个正则,比如: - `\{\{setvar::([^:]+)::([^}]*)\}\}` - `\{\{getvar::([^}]+)\}\}` 等。 2. 按「写变量 → 读变量」顺序依次 `ReplaceAllStringFunc`: - set / add / inc / dec:调用 `VariablesService`,返回空串或新值字符串。 - get:用变量值替换整段 `{{...}}`。 **集成位置**:在构建 prompt、处理用户输入时,先跑一遍 `ProcessVariableMacros`。 --- ## 二、宏系统(Macro System) ### 2.1 目标 - 使用 `{{...}}` 宏在 prompt 中引入上下文信息,包括: - 用户/人设:`{{user}}`、`{{persona}}`、`{{user_description}}` - 角色:`{{char}}`、`{{char_description}}`、`{{system_prompt}}`、`{{jailbreak}}` - 时间:`{{time}}`、`{{date}}`、`{{weekday}}` - 聊天上下文:`{{lastMessage}}`、`{{lastUserMessage}}`、`{{messageCount}}` 等 - 随机:`{{random}}`、`{{random::min::max}}`、`{{roll::2d6}}`、`{{pick::a::b}}` - 元信息:`{{model}}`、`{{provider}}`、`{{idle_duration}}` 等。 ### 2.2 宏上下文(Go) ```go type MacroContext struct { UserName string UserDescription string CharacterName string CharacterDescription string CharacterSystemPrompt string PostHistoryInstructions string LastMessage string LastUserMessage string LastCharacterMessage string MessageCount int ChatID string OriginalPrompt string CurrentInput string ModelName string ProviderName string IdleDurationMinutes int Now time.Time } ``` ### 2.3 宏解析函数 ```go func ProcessMacros(text string, ctx MacroContext) string ``` 推荐拆为多个子步骤(顺序类似原项目): 1. `_processRandomMacros`: - `{{random}}` - `{{random::min::max}}` - `{{roll::NdM}}` - `{{pick::opt1::opt2}}` - `{{uuid}}` 2. `_processConditionalMacros`: - `{{if::cond::then}}` - `{{if::cond::then::else}}`(可以做简单布尔表达式解析) 3. `_processTimeDateMacros`: - `{{time}}` / `{{date}}` / `{{weekday}}` / 自定义时间格式。 4. `_processCharacterMacros`: - `{{char}}`、`{{char_description}}`、`{{system_prompt}}`、`{{post_history_instructions}}` 等。 5. `_processUserMacros`: - `{{user}}` / `{{persona}}` / `{{user_description}}`。 6. `_processChatMacros`: - `{{lastMessage}}` / `{{lastUserMessage}}` / `{{lastCharMessage}}` / `{{messageCount}}` / `{{chatId}}`。 7. `_processSpecialMacros`: - `{{newline}}` / `{{nl}}`、`{{trim}}`、`{{noop}}` - `{{original}}`(原始 prompt,用于局部覆盖) - `{{input}}`(当前用户输入) - `{{model}}` / `{{provider}}` - `{{idle_duration}}`。 **集成顺序**(在构建 prompt 时): 1. 先跑变量宏 `ProcessVariableMacros`(会改变变量状态)。 2. 再跑文本宏 `ProcessMacros`。 --- ## 三、思维链支持(Chain-of-Thought / Reasoning) ### 3.1 目标 - 对支持推理的模型(o1/o3、Claude Thinking、Gemini 思考流): - 把推理内容单独抓出来(从 `reasoning_content` / `thinking` / `thought` 字段等)。 - 和正常回复一起存在消息上。 - 前端给一个可折叠的「思维链/推理」区域。 ### 3.2 数据模型 ```go type ChatMessage struct { ID string ChatID string Role string // "user" / "assistant" / "system" Content string Timestamp time.Time Swipes []string CurrentSwipeIdx int Reasoning *string // 默认推理文本 ReasoningSwipes []string // 每个 swipe 的推理 // ... 其他字段(attachments、characterId 等) } ``` 前端便于使用的派生属性(在 React 里实现): - `currentReasoning`: - 如果 `ReasoningSwipes` 长度 > `CurrentSwipeIdx` → 用对应项; - 否则用 `Reasoning`。 - `hasReasoning`: - `Reasoning` 非空;或 `ReasoningSwipes` 中有任意非空字符串。 ### 3.3 LLM 接口设计(Go) **非流式:** ```go type LLMReasoningResponse struct { Content string Reasoning *string } func GenerateWithReasoning(promptCtx PromptContext, cfg LLMConfig) (LLMReasoningResponse, error) ``` **流式:** ```go type ReasoningChunk struct { Content *string Reasoning *string IsReasoningChunk bool } func GenerateStreamWithReasoning(promptCtx PromptContext, cfg LLMConfig) (<-chan ReasoningChunk, error) ``` 解析逻辑示例(伪代码): ```go for chunk := range ch { if chunk.IsReasoningChunk && chunk.Reasoning != nil { reasoningBuffer.WriteString(*chunk.Reasoning) } if chunk.Content != nil { contentBuffer.WriteString(*chunk.Content) } // 把当前 buffer 写回当前 assistant 消息,前端即可实时显示 } ``` - OpenAI o1/o3:从 `choice.message.reasoning_content` 或自定义扩展字段中解析。 - Claude:从 `thinking` 字段解析。 - Gemini:从 `thought` 或增强字段解析。 ### 3.4 前端渲染(React) 示例: ```tsx function MessageView({ message }: { message: ChatMessage }) { return (