package replyexport import ( "strings" ) // PolicyOptions 传递判定所需上下文 type PolicyOptions struct { Role string Status string // pending, streaming_status, auto_retrying, completed, failed, cancelled Content string HasInteractiveCard bool HasError bool IsCancelled bool IsRestricted bool // 内容合规策略是否禁止导出 Revision int } // EvaluatePolicy 权威判定一条消息的内容类型与导出能力 func EvaluatePolicy(opts PolicyOptions) (ContentKind, MessageExportCapability) { revision := opts.Revision if revision <= 0 { revision = 1 } normContent := strings.TrimSpace(opts.Content) hash := ComputeSha256(normContent) cap := MessageExportCapability{ Policy: PolicyNone, Formats: []string{}, BodySelector: "content_only", ContentRevision: revision, ContentSha256: hash, } // 1. 非助手回复不可导出 if opts.Role != "assistant" { cap.ReasonCode = ReasonNotAssistant return KindConversationalText, cap } // 2. 异常或已取消状态 if opts.HasError || opts.IsCancelled || opts.Status == "failed" || opts.Status == "cancelled" { cap.ReasonCode = ReasonErrorOrCancelled return KindErrorNotice, cap } // 3. 流式中或未完成 if opts.Status == "pending" || opts.Status == "streaming_status" || opts.Status == "auto_retrying" { cap.ReasonCode = ReasonNotCompleted return KindStatusUpdate, cap } // 4. 空正文 if normContent == "" { if opts.HasInteractiveCard { cap.ReasonCode = ReasonCardOnly return KindActionCard, cap } cap.ReasonCode = ReasonEmptyBody return KindStatusUpdate, cap } // 5. 内容合规策略拦截 if opts.IsRestricted { cap.ReasonCode = ReasonContentRestricted return KindDocumentText, cap } // 6. 纯交互卡片与短依赖引导语 if opts.HasInteractiveCard { // 检查正文是否具备独立阅读价值 isDependentPrompt := isDependentCardPrompt(normContent) if isDependentPrompt { cap.ReasonCode = ReasonCardOnly return KindActionCard, cap } // 正文具备独立性,允许导出,且明确仅导出 content cap.Policy = PolicyFull cap.Formats = FullFormats() return KindDocumentText, cap } // 7. 极短寒暄或瞬时确认答复:开放 copy_only,不保存为系统文档 if isCasualOrVeryShort(normContent) { cap.Policy = PolicyCopyOnly cap.Formats = CopyOnlyFormats() return KindConversationalText, cap } // 8. 常规具备独立结构的助手正文 cap.Policy = PolicyFull cap.Formats = FullFormats() return KindDocumentText, cap } // isDependentCardPrompt 判断正文是否仅仅是依赖卡片的简短引导语 func isDependentCardPrompt(content string) bool { runes := []rune(content) // 超过 80 字符且包含结构化 Markdown 标记的,通常为完整方案说明 if len(runes) > 80 && (strings.Contains(content, "#") || strings.Contains(content, "- ") || strings.Contains(content, "|")) { return false } // 短于 50 字符且匹配经典卡片引导语模式 if len(runes) <= 50 { lower := strings.ToLower(content) patterns := []string{ "请确认", "请选择", "请核对", "点击下方", "操作如下", "是否继续", "请决策", "请审批", "已为您生成以下操作卡", } for _, p := range patterns { if strings.Contains(lower, p) { return true } } } return false } // isCasualOrVeryShort 判断是否属于无长久沉淀价值的极短寒暄 func isCasualOrVeryShort(content string) bool { runes := []rune(content) if len(runes) <= 12 { shortGreetings := []string{ "好的", "没问题", "收到", "您好", "在的", "请稍候", "不用谢", "再见", "明白", } for _, g := range shortGreetings { if strings.Contains(content, g) { return true } } } return false }