100 lines
2.9 KiB
Go
100 lines
2.9 KiB
Go
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
|
||
}
|