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

100 lines
2.9 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 (
core "agent-system-core"
"fmt"
"strings"
"unicode/utf8"
)
// Approval is a high-consequence status. A source-backed material_record is
// rendered from the exact quote by the host; free-form model analysis cannot
// turn an ordinary requirement or suggestion into an approved decision.
// This narrow language guard is not a general semantic verifier.
var affirmativeApprovalTerms = []string{
"已批准", "已经批准", "已获批", "审批通过", "批复同意", "已通过审批",
}
var approvalImmediateQualifiers = []string{
"是否", "不等于", "不代表", "并非", "不是", "尚待确认是否", "需要确认是否",
"尚未", "未获", "未被", "未曾", "并未", "没有", "未",
}
func approvalIsQuestionOrNegation(prefix, suffix string) bool {
for _, qualifier := range approvalImmediateQualifiers {
if strings.HasSuffix(prefix, qualifier) {
return true
}
}
suffix = strings.TrimSpace(suffix)
for _, question := range []string{"了吗", "了么", "了没有", "吗", "么", "没有", "?", "?"} {
if strings.HasPrefix(suffix, question) {
return true
}
}
for _, framing := range []string{"写成", "说成", "当作", "视为", "认定为"} {
if !strings.HasSuffix(prefix, framing) {
continue
}
for _, prevention := range []string{"防止", "避免", "不要", "不能", "不得"} {
if strings.Contains(prefix, prevention) {
return true
}
}
}
return false
}
func hasAffirmativeApprovalClaim(text string) bool {
claimInClause := func(clause string) bool {
for _, term := range affirmativeApprovalTerms {
for start := 0; start < len(clause); {
at := strings.Index(clause[start:], term)
if at < 0 {
break
}
at += start
prefix := []rune(clause[:at])
if len(prefix) > 16 {
prefix = prefix[len(prefix)-16:]
}
if !approvalIsQuestionOrNegation(string(prefix), clause[at+len(term):]) {
return true
}
start = at + len(term)
}
}
return false
}
start := 0
for i, r := range text {
if strings.ContainsRune("。;;,,!??!\n::", r) {
end := i + utf8.RuneLen(r)
if claimInClause(text[start:end]) {
return true
}
start = end
}
}
return claimInClause(text[start:])
}
func validateMaterialApprovalClaims(candidate core.WorkCheckpointCandidate) error {
for _, text := range []string{candidate.Focus, candidate.Remaining, candidate.NextStep} {
if hasAffirmativeApprovalClaim(text) {
return fmt.Errorf("checkpoint.claim: approval needs explicit source record")
}
}
for _, finding := range candidate.Findings {
if finding.Kind == "material_record" {
continue // The host replaces all model-authored display text with exact quotes.
}
for _, text := range []string{finding.Text, finding.Impact, finding.Assumptions} {
if hasAffirmativeApprovalClaim(text) {
return fmt.Errorf("checkpoint.claim: approval needs explicit source record")
}
}
}
return nil
}