Files
smartmate/backend/services/agent/prompt/reasoning_summary.go
Losita d7184b776b Version: 0.9.75.dev.260505
后端:
1.收口阶段 6 agent 结构迁移,将 newAgent 内核与 agentsvc 编排层迁入 services/agent
- 切换 Agent 启动装配与 HTTP handler 直连 agent sv,移除旧 service agent bridge
- 补齐 Agent 对 memory、task、task-class、schedule 的 RPC 适配与契约字段
- 扩展 schedule、task、task-class RPC/contract 支撑 Agent 查询、写入与 provider 切流
- 更新迁移文档、README 与相关注释,明确 agent 当前切流点和剩余 memory 迁移面
2026-05-05 16:00:57 +08:00

137 lines
6.4 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 agentprompt
import (
"encoding/json"
"fmt"
"strings"
"unicode/utf8"
"github.com/cloudwego/eino/schema"
)
const (
reasoningSummaryMaxFullRunes = 6000
reasoningSummaryMaxDeltaRunes = 1800
)
// ReasoningSummaryPromptInput 描述一次“思考摘要”模型调用所需的最小输入。
//
// 职责边界:
// 1. 只承载摘要模型需要看的文本与运行态,不绑定 stream 包的 DTO避免 prompt 层反向依赖输出协议;
// 2. FullReasoning 会在构造 prompt 时只保留尾部,避免长时间思考把便宜模型上下文撑爆;
// 3. PreviousSummary 只作为连续摘要的参考,不要求模型逐字继承。
type ReasoningSummaryPromptInput struct {
FullReasoning string
DeltaReasoning string
PreviousSummary string
CandidateSeq int
Final bool
DurationSeconds float64
}
type reasoningSummaryPromptPayload struct {
CandidateSeq int `json:"candidate_seq"`
Final bool `json:"final"`
DurationSeconds float64 `json:"duration_seconds"`
PreviousSummary string `json:"previous_summary,omitempty"`
RecentReasoning string `json:"recent_reasoning,omitempty"`
DeltaReasoning string `json:"delta_reasoning,omitempty"`
SourceTextRunes int `json:"source_text_runes,omitempty"`
MaxDetailSummaryRunes int `json:"max_detail_summary_runes,omitempty"`
}
// BuildReasoningSummaryMessages 构造思考摘要模型调用的 messages。
//
// 步骤说明:
// 1. system prompt 明确“只做用户可见摘要”,禁止复述原始思考链和内部推理细节;
// 2. user prompt 使用 JSON 承载输入,便于后续扩展字段且减少模型误读;
// 3. 长文本只保留尾部窗口,保证异步摘要请求稳定、便宜、可控。
func BuildReasoningSummaryMessages(input ReasoningSummaryPromptInput) []*schema.Message {
recentReasoning := trimRunesFromEnd(input.FullReasoning, reasoningSummaryMaxFullRunes)
deltaReasoning := trimRunesFromEnd(input.DeltaReasoning, reasoningSummaryMaxDeltaRunes)
payload := reasoningSummaryPromptPayload{
CandidateSeq: input.CandidateSeq,
Final: input.Final,
DurationSeconds: input.DurationSeconds,
PreviousSummary: strings.TrimSpace(input.PreviousSummary),
RecentReasoning: recentReasoning,
DeltaReasoning: deltaReasoning,
SourceTextRunes: reasoningSummarySourceRunes(recentReasoning, deltaReasoning),
MaxDetailSummaryRunes: ReasoningSummaryDetailRuneLimit(input.FullReasoning, input.DeltaReasoning),
}
raw, err := json.MarshalIndent(payload, "", " ")
if err != nil {
raw = []byte(fmt.Sprintf(`{"recent_reasoning":%q}`, trimRunesFromEnd(input.FullReasoning, reasoningSummaryMaxFullRunes)))
}
return []*schema.Message{
schema.SystemMessage(buildReasoningSummarySystemPrompt()),
schema.UserMessage("请基于 delta_reasoning 生成本轮新增的用户可见阶段摘要recent_reasoning 仅作上下文previous_summary 仅作去重参考。\n输入\n" + string(raw)),
}
}
func buildReasoningSummarySystemPrompt() string {
return strings.TrimSpace(`你是 SmartMate 的“思考摘要器”。你的任务是把模型内部 reasoning 整理成用户可见的进度摘要。
输出必须是严格 JSON 对象:
{
"short_summary": "8到18个汉字的短摘要",
"detail_summary": "不超过 max_detail_summary_runes 个字的展开摘要"
}
字段语义:
- previous_summary上一条已经展示给用户的摘要只用于判断哪些内容已经说过。
- delta_reasoning本轮新增 reasoning是生成 detail_summary 的主要依据。
- recent_reasoning全量尾部上下文只用于补齐题目、变量名、阶段背景不要按它重写一遍完整摘要。
规则:
1. 不输出 markdown不输出代码块不解释 JSON 以外的内容。
2. 摘要要像“阶段更新”,不是流水账;优先写新增结论、阶段变化、卡点、修正、下一步动作。
3. detail_summary 以 delta_reasoning 为主previous_summary 已覆盖的信息不要大段重复,除非本轮对它有修正或推进。
4. short_summary 用 8 到 18 个汉字,偏结果或动作短语,例如“补齐边界条件”“转入代码实现”“优化滚动数组”。
5. detail_summary 用自然的一到两句话表达,优先以具体对象、动作或结果开头,不要把“正在”“当前”“已确定”“已完成”作为默认句首模板;若 previous_summary 已使用类似开头,本轮必须换一种表达。
6. final=false 时不要用“已完成”概括整体任务;只有 delta_reasoning 明确完成某个局部步骤时,才可描述该局部已经完成。
7. detail_summary 字数必须小于等于 max_detail_summary_runes不需要凑满上限信息密度优先。
8. 不暴露原始思考链、隐含假设链、逐步演算,只保留用户可见的进展。
9. 若本轮没有实质新增信息,输出保守但不重复的摘要,例如“沿用上一轮判断,暂无新的可展示进展。”
10. final=true 时,用完成态语气,说明思考已经收拢到下一步答复或动作。`)
}
// ReasoningSummaryDetailRuneLimit 返回 detail_summary 的最大字数。
//
// 职责边界:
// 1. 与 BuildReasoningSummaryMessages 使用同一套输入窗口,避免 prompt 提示和服务端兜底口径不一致;
// 2. 上限取“提供给摘要模型的主要文本段”的一半,并向上取整,适配极短文本;
// 3. 返回 0 表示没有有效输入文本,调用方不应做硬裁剪。
func ReasoningSummaryDetailRuneLimit(fullReasoning, deltaReasoning string) int {
recentReasoning := trimRunesFromEnd(fullReasoning, reasoningSummaryMaxFullRunes)
delta := trimRunesFromEnd(deltaReasoning, reasoningSummaryMaxDeltaRunes)
sourceRunes := reasoningSummarySourceRunes(recentReasoning, delta)
if sourceRunes <= 0 {
return 0
}
return (sourceRunes + 1) / 2
}
func reasoningSummarySourceRunes(recentReasoning, deltaReasoning string) int {
recentReasoning = strings.TrimSpace(recentReasoning)
if recentReasoning != "" {
return utf8.RuneCountInString(recentReasoning)
}
return utf8.RuneCountInString(strings.TrimSpace(deltaReasoning))
}
func trimRunesFromEnd(text string, maxRunes int) string {
text = strings.TrimSpace(text)
if text == "" || maxRunes <= 0 {
return ""
}
runes := []rune(text)
if len(runes) <= maxRunes {
return text
}
return string(runes[len(runes)-maxRunes:])
}