feat: 思考追加机制 — 所有思考类型支持【继续思考】标记循环
- performThink 改为循环执行,最多3轮 - LLM输出含【继续思考】时自动追加一轮 - post_chat/silence/default三种提示词均告知可用此标记 - 存储前剥离控制标记,不污染思考内容 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -942,27 +942,41 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
|
|
||||||
// 5. 构建思考提示词(根据触发原因调整)
|
// 5. 构建思考提示词(根据触发原因调整)
|
||||||
systemPrompt := t.buildThinkingSystemPrompt(personaConfig, triggerReason)
|
systemPrompt := t.buildThinkingSystemPrompt(personaConfig, triggerReason)
|
||||||
userPrompt := t.buildThinkingUserPrompt(memories, convHistory, deviceSummary, triggerReason, platformObservation)
|
|
||||||
|
|
||||||
messages := []model.LLMMessage{
|
|
||||||
{Role: model.RoleSystem, Content: systemPrompt},
|
|
||||||
{Role: model.RoleUser, Content: userPrompt},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. 准备工具定义(通过自主工具策略过滤)
|
// 6. 准备工具定义(通过自主工具策略过滤)
|
||||||
openAITools := t.filterToolsByPolicy(t.buildOpenAITools())
|
openAITools := t.filterToolsByPolicy(t.buildOpenAITools())
|
||||||
|
|
||||||
// 7. 调用 LLM — 优先使用深度思考模型,工具阶段回退到工具模型
|
// 7. 思考循环:支持 [继续思考] 追加,最多 maxThinkRounds 轮
|
||||||
maxToolRounds := t.autoToolPolicy.MaxToolCallsPerRound
|
const maxThinkRounds = 3
|
||||||
var finalContent string
|
var allContents []string
|
||||||
var totalToolCalls int
|
var totalToolCalls int
|
||||||
var toolCallRecords []map[string]interface{}
|
var allToolCallRecords []map[string]interface{}
|
||||||
|
|
||||||
|
currentMessages := []model.LLMMessage{
|
||||||
|
{Role: model.RoleSystem, Content: systemPrompt},
|
||||||
|
}
|
||||||
|
|
||||||
|
for thinkRound := 0; thinkRound < maxThinkRounds; thinkRound++ {
|
||||||
|
// Build user prompt for this round.
|
||||||
|
if thinkRound == 0 {
|
||||||
|
userPrompt := t.buildThinkingUserPrompt(memories, convHistory, deviceSummary, triggerReason, platformObservation)
|
||||||
|
currentMessages = append(currentMessages, model.LLMMessage{Role: model.RoleUser, Content: userPrompt})
|
||||||
|
} else {
|
||||||
|
// Continuation round: simple prompt asking to continue or finish.
|
||||||
|
currentMessages = append(currentMessages, model.LLMMessage{
|
||||||
|
Role: model.RoleUser,
|
||||||
|
Content: "(你可以继续思考。如果已经想完了,不再输出【继续思考】标记。)",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
maxToolRounds := t.autoToolPolicy.MaxToolCallsPerRound
|
||||||
|
var roundContent string
|
||||||
|
|
||||||
// Round 0: 深度思考模型(优先),失败时回退到工具模型
|
// Round 0: 深度思考模型(优先),失败时回退到工具模型
|
||||||
resp, err := t.llmAdapter.ChatWithTools(ctx, messages, openAITools)
|
resp, err := t.llmAdapter.ChatWithTools(ctx, currentMessages, openAITools)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[后台思考] 深度思考模型调用失败,回退到工具模型: %v", err)
|
log.Printf("[后台思考] 深度思考模型调用失败,回退到工具模型: %v", err)
|
||||||
resp, err = t.toolAdapter.ChatWithTools(ctx, messages, openAITools)
|
resp, err = t.toolAdapter.ChatWithTools(ctx, currentMessages, openAITools)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[后台思考] LLM调用失败: %v", err)
|
log.Printf("[后台思考] LLM调用失败: %v", err)
|
||||||
@@ -970,13 +984,21 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(resp.ToolCalls) == 0 {
|
if len(resp.ToolCalls) == 0 {
|
||||||
finalContent = resp.Content
|
roundContent = resp.Content
|
||||||
} else {
|
} else {
|
||||||
// 深度思考模型请求了工具调用,进入工具执行循环
|
// 工具调用循环
|
||||||
|
toolMessages := make([]model.LLMMessage, 0)
|
||||||
|
assistantMsg := model.LLMMessage{
|
||||||
|
Role: model.RoleAssistant,
|
||||||
|
Content: resp.Content,
|
||||||
|
ToolCalls: resp.ToolCalls,
|
||||||
|
ReasoningContent: resp.ReasoningContent,
|
||||||
|
}
|
||||||
|
toolMessages = append(toolMessages, assistantMsg)
|
||||||
|
|
||||||
for round := 0; round <= maxToolRounds; round++ {
|
for round := 0; round <= maxToolRounds; round++ {
|
||||||
if round > 0 {
|
if round > 0 {
|
||||||
// 后续轮次使用工具模型
|
resp, err = t.toolAdapter.ChatWithTools(ctx, append(currentMessages, toolMessages...), openAITools)
|
||||||
resp, err = t.toolAdapter.ChatWithTools(ctx, messages, openAITools)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[后台思考] 工具模型调用失败 (round=%d): %v", round, err)
|
log.Printf("[后台思考] 工具模型调用失败 (round=%d): %v", round, err)
|
||||||
return
|
return
|
||||||
@@ -984,19 +1006,20 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if round > 0 && len(resp.ToolCalls) == 0 {
|
if round > 0 && len(resp.ToolCalls) == 0 {
|
||||||
finalContent = resp.Content
|
roundContent = resp.Content
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[后台思考] LLM 请求 %d 个工具调用 (round=%d)", len(resp.ToolCalls), round)
|
log.Printf("[后台思考] LLM 请求 %d 个工具调用 (thinkRound=%d, toolRound=%d)", len(resp.ToolCalls), thinkRound, round)
|
||||||
|
|
||||||
assistantMsg := model.LLMMessage{
|
if round > 0 {
|
||||||
|
toolMessages = append(toolMessages, model.LLMMessage{
|
||||||
Role: model.RoleAssistant,
|
Role: model.RoleAssistant,
|
||||||
Content: resp.Content,
|
Content: resp.Content,
|
||||||
ToolCalls: resp.ToolCalls,
|
ToolCalls: resp.ToolCalls,
|
||||||
ReasoningContent: resp.ReasoningContent,
|
ReasoningContent: resp.ReasoningContent,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
messages = append(messages, assistantMsg)
|
|
||||||
|
|
||||||
for _, tc := range resp.ToolCalls {
|
for _, tc := range resp.ToolCalls {
|
||||||
var args map[string]interface{}
|
var args map[string]interface{}
|
||||||
@@ -1014,41 +1037,63 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resultJSON, _ := json.Marshal(result)
|
resultJSON, _ := json.Marshal(result)
|
||||||
messages = append(messages, model.LLMMessage{
|
toolMessages = append(toolMessages, model.LLMMessage{
|
||||||
Role: model.RoleTool,
|
Role: model.RoleTool,
|
||||||
Content: string(resultJSON),
|
Content: string(resultJSON),
|
||||||
ToolCallID: tc.ID,
|
ToolCallID: tc.ID,
|
||||||
})
|
})
|
||||||
|
|
||||||
totalToolCalls++
|
totalToolCalls++
|
||||||
toolCallRecords = append(toolCallRecords, map[string]interface{}{
|
allToolCallRecords = append(allToolCallRecords, map[string]interface{}{
|
||||||
"name": tc.Name,
|
"name": tc.Name,
|
||||||
"args": args,
|
"args": args,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if round == maxToolRounds {
|
if round == maxToolRounds {
|
||||||
finalResp, finalErr := t.llmAdapter.Chat(ctx, messages)
|
finalResp, finalErr := t.llmAdapter.Chat(ctx, append(currentMessages, toolMessages...))
|
||||||
if finalErr != nil {
|
if finalErr != nil {
|
||||||
log.Printf("[后台思考] 最终总结调用失败: %v", finalErr)
|
log.Printf("[后台思考] 最终总结调用失败: %v", finalErr)
|
||||||
finalContent = resp.Content
|
roundContent = resp.Content
|
||||||
} else {
|
} else {
|
||||||
finalContent = finalResp.Content
|
roundContent = finalResp.Content
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append tool messages to the ongoing conversation.
|
||||||
|
currentMessages = append(currentMessages, toolMessages...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if finalContent == "" {
|
if roundContent == "" {
|
||||||
|
log.Printf("[后台思考] 第%d轮未获得有效内容,跳过", thinkRound+1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip control marker before storing.
|
||||||
|
cleanContent := strings.TrimSpace(strings.Replace(roundContent, "【继续思考】", "", 1))
|
||||||
|
allContents = append(allContents, cleanContent)
|
||||||
|
|
||||||
|
// Check for continuation marker.
|
||||||
|
if !strings.Contains(roundContent, "【继续思考】") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Printf("[后台思考] 检测到【继续思考】标记,进入第%d轮...", thinkRound+2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allContents) == 0 {
|
||||||
log.Println("[后台思考] 未获得有效思考内容,跳过")
|
log.Println("[后台思考] 未获得有效思考内容,跳过")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Join all thinking rounds into final content.
|
||||||
|
finalContent := strings.Join(allContents, "\n\n---\n\n")
|
||||||
|
|
||||||
// 序列化工具调用记录
|
// 序列化工具调用记录
|
||||||
toolCallsJSON := "[]"
|
toolCallsJSON := "[]"
|
||||||
if len(toolCallRecords) > 0 {
|
if len(allToolCallRecords) > 0 {
|
||||||
if data, err := json.Marshal(toolCallRecords); err == nil {
|
if data, err := json.Marshal(allToolCallRecords); err == nil {
|
||||||
toolCallsJSON = string(data)
|
toolCallsJSON = string(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1071,7 +1116,7 @@ func (t *Thinker) performThink(triggerReason string) {
|
|||||||
log.Printf("[后台思考] 思考链已记录 (序号=%d, 结论数=%d, 后续问题=%d)", t.chain.Size(), len(conclusions), len(followUps))
|
log.Printf("[后台思考] 思考链已记录 (序号=%d, 结论数=%d, 后续问题=%d)", t.chain.Size(), len(conclusions), len(followUps))
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[后台思考] 完成 (触发原因=%s, 内容长度=%d, 工具调用=%d次)", triggerReason, len(finalContent), totalToolCalls)
|
log.Printf("[后台思考] 完成 (触发原因=%s, 轮数=%d, 内容长度=%d, 工具调用=%d次)", triggerReason, len(allContents), len(finalContent), totalToolCalls)
|
||||||
|
|
||||||
// 9. 记忆维护:机械合并(每10次) + LLM整理(每次)
|
// 9. 记忆维护:机械合并(每10次) + LLM整理(每次)
|
||||||
t.maybeMaintainMemories(currentCount)
|
t.maybeMaintainMemories(currentCount)
|
||||||
@@ -1129,7 +1174,8 @@ func (t *Thinker) buildThinkingSystemPrompt(personaConfig *persona.PersonaConfig
|
|||||||
2. 只有开拓者状态正常且真的有必要时,才在独立一行写【主动消息】标记,后面跟你要发给他的话。不要硬找话题。
|
2. 只有开拓者状态正常且真的有必要时,才在独立一行写【主动消息】标记,后面跟你要发给他的话。不要硬找话题。
|
||||||
3. 【主动消息】标记必须独占一行开头,内容直接对开拓者说话(用"你"称呼他),像主动找他聊天一样。
|
3. 【主动消息】标记必须独占一行开头,内容直接对开拓者说话(用"你"称呼他),像主动找他聊天一样。
|
||||||
4. 如果你在反思中提到"主动消息"这个词但不打算发消息,不要使用【主动消息】这个带括号的标记——我会误解析。
|
4. 如果你在反思中提到"主动消息"这个词但不打算发消息,不要使用【主动消息】这个带括号的标记——我会误解析。
|
||||||
5. 2-4句话即可。`
|
5. 2-4句话即可。
|
||||||
|
6. 如果你觉得还有需要继续思考的事情,在反思末尾独占一行写【继续思考】标记,我会让你再想一轮。如果没有了就不要再写。`
|
||||||
|
|
||||||
case "silence":
|
case "silence":
|
||||||
thinkingInstructions = `
|
thinkingInstructions = `
|
||||||
@@ -1152,7 +1198,8 @@ func (t *Thinker) buildThinkingSystemPrompt(personaConfig *persona.PersonaConfig
|
|||||||
|
|
||||||
其他规则:
|
其他规则:
|
||||||
1. 用第三人称/自言自语描述。
|
1. 用第三人称/自言自语描述。
|
||||||
2. 2-3句话即可。`
|
2. 2-3句话即可。
|
||||||
|
3. 如果觉得还需要继续思考,在反思末尾独占一行写【继续思考】标记。`
|
||||||
|
|
||||||
default:
|
default:
|
||||||
thinkingInstructions = `
|
thinkingInstructions = `
|
||||||
@@ -1161,7 +1208,9 @@ func (t *Thinker) buildThinkingSystemPrompt(personaConfig *persona.PersonaConfig
|
|||||||
|
|
||||||
你现在有空,像写日记一样自然地想一想开拓者的事。
|
你现在有空,像写日记一样自然地想一想开拓者的事。
|
||||||
|
|
||||||
请先看对话历史判断开拓者当前状态,再决定是否发送消息。` + noDisturbRules + `
|
请先看对话历史判断开拓者当前状态,再决定是否发送消息。
|
||||||
|
|
||||||
|
如果觉得还需要继续思考,在反思末尾独占一行写【继续思考】标记。` + noDisturbRules + `
|
||||||
|
|
||||||
用第三人称/自言自语的方式,不要直接对开拓者喊话。`
|
用第三人称/自言自语的方式,不要直接对开拓者喊话。`
|
||||||
case "periodic":
|
case "periodic":
|
||||||
|
|||||||
Reference in New Issue
Block a user