Files
smartmate/backend/service/agentsvc/reasoning_summary.go
Losita 9902ca3563 Version: 0.9.65.dev.260503
后端:
1. 阶段 1.5/1.6
收口 llm-service / rag-service,统一模型出口与检索基础设施入口,清退 backend/infra/llm 与 backend/infra/rag 旧实现;
2. 同步更新相关调用链与微服务迁移计划文档
2026-05-03 23:21:03 +08:00

113 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package agentsvc
import (
"context"
"errors"
"log"
"strings"
newagentprompt "github.com/LoveLosita/smartflow/backend/newAgent/prompt"
newagentstream "github.com/LoveLosita/smartflow/backend/newAgent/stream"
llmservice "github.com/LoveLosita/smartflow/backend/services/llm"
)
const reasoningSummaryMaxTokens = 700
type reasoningSummaryLLMResponse struct {
ShortSummary string `json:"short_summary"`
DetailSummary string `json:"detail_summary"`
}
// makeReasoningSummaryFunc 把便宜模型封装成 stream 层可注入的摘要函数。
//
// 职责边界:
// 1. service 层负责选择模型与 promptstream 层只负责调度和闸门;
// 2. 这里不持久化摘要,持久化统一走 ChunkEmitter 的 extra hook
// 3. 摘要失败时返回 error由 ReasoningDigestor 吞掉并等待下一次水位线/Flush 兜底。
func (s *AgentService) makeReasoningSummaryFunc(client *llmservice.Client) newagentstream.ReasoningSummaryFunc {
if client == nil {
return nil
}
return func(ctx context.Context, input newagentstream.ReasoningSummaryInput) (newagentstream.StreamThinkingSummaryExtra, error) {
previousSummary := ""
if input.PreviousSummary != nil {
previousSummary = input.PreviousSummary.DetailSummary
if strings.TrimSpace(previousSummary) == "" {
previousSummary = input.PreviousSummary.ShortSummary
}
}
messages := newagentprompt.BuildReasoningSummaryMessages(newagentprompt.ReasoningSummaryPromptInput{
FullReasoning: input.FullReasoning,
DeltaReasoning: input.DeltaReasoning,
PreviousSummary: previousSummary,
CandidateSeq: input.CandidateSeq,
Final: input.Final,
DurationSeconds: input.DurationSeconds,
})
resp, rawResult, err := llmservice.GenerateJSON[reasoningSummaryLLMResponse](
ctx,
client,
messages,
llmservice.GenerateOptions{
Temperature: 0.1,
MaxTokens: reasoningSummaryMaxTokens,
Thinking: llmservice.ThinkingModeDisabled,
Metadata: map[string]any{
"stage": "reasoning_summary",
"candidate_seq": input.CandidateSeq,
"final": input.Final,
},
},
)
if err != nil {
log.Printf("[WARN] reasoning 摘要模型调用失败 seq=%d final=%v err=%v raw=%s",
input.CandidateSeq,
input.Final,
err,
truncateReasoningSummaryRaw(rawResult),
)
return newagentstream.StreamThinkingSummaryExtra{}, err
}
summary := newagentstream.StreamThinkingSummaryExtra{
ShortSummary: strings.TrimSpace(resp.ShortSummary),
DetailSummary: limitReasoningDetailSummary(
resp.DetailSummary,
newagentprompt.ReasoningSummaryDetailRuneLimit(input.FullReasoning, input.DeltaReasoning),
),
}
if summary.ShortSummary == "" && summary.DetailSummary == "" {
return newagentstream.StreamThinkingSummaryExtra{}, errors.New("reasoning 摘要模型返回空摘要")
}
return summary, nil
}
}
func limitReasoningDetailSummary(text string, maxRunes int) string {
text = strings.TrimSpace(text)
if text == "" || maxRunes <= 0 {
return text
}
runes := []rune(text)
if len(runes) <= maxRunes {
return text
}
return string(runes[:maxRunes])
}
func truncateReasoningSummaryRaw(raw *llmservice.TextResult) string {
if raw == nil {
return ""
}
text := strings.TrimSpace(raw.Text)
runes := []rune(text)
if len(runes) <= 200 {
return text
}
return string(runes[:200]) + "..."
}