Files
Agent-System-Backend/internal/runmgr/material_notice.go
T

107 lines
3.0 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 runmgr
import (
"regexp"
"strconv"
"strings"
"unicode/utf8"
core "agent-system-core"
)
type materialSuggestion struct {
Label string `json:"label"`
Basis string `json:"basis"`
}
var (
materialOptionLine = regexp.MustCompile(`^\s*(?:[0-9]+[.、)]|[-*])\s+(.+)$`)
materialBasisLine = regexp.MustCompile(`^\s+[-*]\s*依据[::]\s*(.+)$`)
)
func validMaterialSuggestion(s materialSuggestion) bool {
for _, value := range []string{s.Label, s.Basis} {
if value == "" || strings.ContainsAny(value, "<>\r\n") || strings.Contains(value, "sourceId=") {
return false
}
}
return utf8.RuneCountInString(s.Label) <= 80 && utf8.RuneCountInString(s.Basis) <= 240
}
// parseMaterialSuggestions accepts only bounded, grounded options from the
// completed child answer. Raw child prose never becomes a system reminder.
func parseMaterialSuggestions(text string) []materialSuggestion {
inSection := false
var current *materialSuggestion
var out []materialSuggestion
finish := func() bool {
if current == nil {
return true
}
if !validMaterialSuggestion(*current) || len(out) == 4 {
return false
}
out = append(out, *current)
current = nil
return true
}
for _, raw := range strings.Split(text, "\n") {
line := strings.TrimSpace(raw)
if strings.HasPrefix(line, "#") {
if inSection {
break
}
if strings.Contains(line, "建议后续处理") {
inSection = true
}
continue
}
if !inSection || line == "" {
continue
}
if match := materialBasisLine.FindStringSubmatch(raw); match != nil {
if current == nil || current.Basis != "" {
return nil
}
current.Basis = strings.TrimSpace(match[1])
continue
}
if match := materialOptionLine.FindStringSubmatch(raw); match != nil {
if !finish() {
return nil
}
label := strings.Trim(strings.TrimSpace(match[1]), "* \t")
current = &materialSuggestion{Label: label}
}
}
if !inSection || !finish() || len(out) < 3 || len(out) > 4 {
return nil
}
return out
}
func (m *Manager) materialNoticeUsable(owner string, card core.ActivityCard, payload core.AgentResultPayload) bool {
if payload.Status != core.RunCompleted || m.cards == nil || card.Task == nil {
return false
}
visible, err := m.cards.Get(owner, card.ID)
if err != nil || visible.Task == nil || visible.Task.ExecutionState != "completed" ||
visible.Task.OutcomeCompleteness == "none" || strings.TrimSpace(visible.Task.Conclusion) == "" {
return false
}
return m.taskSourcesAvailable(owner, string(card.ProjectID), card.Task)
}
// materialWorkDecision offers verified suggestions as the work card's optional
// follow-up choice; anything short of a valid set offers no choice.
func materialWorkDecision(items []materialSuggestion) *core.WorkDecision {
if len(items) < 3 || len(items) > 4 {
return nil
}
options := make([]core.WorkOption, 0, len(items))
for i, item := range items {
options = append(options, core.WorkOption{ID: "s" + strconv.Itoa(i+1), Label: item.Label})
}
return &core.WorkDecision{Mode: "single", State: "open", Options: options}
}