Files
Agent-System/internal/squad/evaluator.go
T

153 lines
5.9 KiB
Go

package squad
import (
"fmt"
"strings"
"time"
)
// StepEvaluator 负责单步执行产物评估与自适应反馈调整
type StepEvaluator struct{}
// NewStepEvaluator 创建单步评估器
func NewStepEvaluator() *StepEvaluator {
return &StepEvaluator{}
}
// EvaluateStepOutput 评估单步产物与黑板事实,推导基准假设与动态重排指令
func (e *StepEvaluator) EvaluateStepOutput(
wsDir string,
step StepSpec,
outputContent string,
bb *BlackboardState,
) (*EvaluationResult, error) {
now := time.Now()
res := &EvaluationResult{
StepID: step.ID,
Action: StepActionContinue,
Reason: "步骤按计划正常推进",
NewAssumptions: make([]AssumptionItem, 0),
DynamicStepsToAdd: make([]StepSpec, 0),
DynamicStepsToAlter: make([]StepSpec, 0),
StepsToSkip: make([]string, 0),
}
contentLower := strings.ToLower(outputContent)
// 1. 基于黑板缺口与产物推导“基准假设 (Baseline Assumptions)”
var gapTexts []string
if bb != nil {
for _, g := range bb.Gaps {
gapTexts = append(gapTexts, strings.ToLower(g.Item))
}
}
combinedLower := contentLower + " " + strings.Join(gapTexts, " ")
// 规则 1.1: 灭菌方式缺口推断
if strings.Contains(combinedLower, "未明确灭菌") || strings.Contains(combinedLower, "未提供灭菌") ||
strings.Contains(combinedLower, "未确定灭菌") || strings.Contains(combinedLower, "缺少灭菌方式") ||
strings.Contains(combinedLower, "未提供产品灭菌方式") {
if !hasAssumptionTopic(bb, "sterilization_method") {
res.NewAssumptions = append(res.NewAssumptions, AssumptionItem{
ID: fmt.Sprintf("assump_sterilization_%d", now.UnixNano()),
Topic: "sterilization_method",
Statement: "基准假设灭菌方式采用环氧乙烷(EO)灭菌,解析期设定为14天",
Rationale: "客户未明确灭菌方式,依据二类无源无菌高分子介入器械行业通用工程实践设定",
Source: "inferred",
})
}
}
// 规则 1.2: 货架有效期缺口推断
if strings.Contains(combinedLower, "未明确货架寿命") || strings.Contains(combinedLower, "有效期未定") ||
strings.Contains(combinedLower, "未提供有效期") {
if !hasAssumptionTopic(bb, "shelf_life") {
res.NewAssumptions = append(res.NewAssumptions, AssumptionItem{
ID: fmt.Sprintf("assump_shelflife_%d", now.UnixNano()),
Topic: "shelf_life",
Statement: "基准假设产品货架有效期设定为2年,需执行加速老化试验",
Rationale: "符合常规无菌医疗器械首次注册基准设计周期",
Source: "inferred",
})
}
}
// 规则 1.3: 注册目标市场缺口推断
if strings.Contains(combinedLower, "未明确目标市场") || strings.Contains(combinedLower, "市场未定") {
if !hasAssumptionTopic(bb, "target_market") {
res.NewAssumptions = append(res.NewAssumptions, AssumptionItem{
ID: fmt.Sprintf("assump_market_%d", now.UnixNano()),
Topic: "target_market",
Statement: "基准假设首发上市目标为中国境内NMPA二类医疗器械注册",
Rationale: "以国内优先注册为基准,后续拓展海外认证",
Source: "inferred",
})
}
}
// 2. 检查是否触发动态跳步 (Skip Step)
if strings.Contains(contentLower, "无需开展重复公开资料检索") ||
strings.Contains(contentLower, "无需重复开展市场竞品检索") ||
strings.Contains(contentLower, "竞品数据已全面锁定") ||
strings.Contains(contentLower, "跳过市场调研") {
if res.Action == StepActionContinue {
res.Action = StepActionSkipStep
}
res.StepsToSkip = append(res.StepsToSkip, "step_02_market", "step_02", "market")
res.Reason = "前序分析已充分锁定竞品数据,避免重复冗余检索"
}
// 3. 检查是否触发动态加步 (Add Step)
// 射频/微波 EMC 与热安全风险
if (strings.Contains(contentLower, "射频") || strings.Contains(contentLower, "微波") || strings.Contains(contentLower, "消融")) &&
(strings.Contains(contentLower, "电磁兼容") || strings.Contains(contentLower, "emc") || strings.Contains(contentLower, "热灼伤")) {
res.Action = StepActionAddStep
res.DynamicStepsToAdd = append(res.DynamicStepsToAdd, StepSpec{
ID: "step_rf_emc_safety",
Role: "engineer",
Title: "高频电磁兼容性(EMC)与热安全防护专项工程论证",
SubWorkspace: "tasks/dynamic_rf_emc",
Mode: "execute",
AllowDynamicAdjustment: false,
Accept: []string{
"核算高频消融电磁辐射泄漏安全边际与抗干扰设计",
"设计温度反馈闭环与防止意外热灼伤切断机制",
},
})
res.Reason = "识别到大功率射频高频风险,动态注入EMC与热防护专项工程论证工序"
}
// 动物源病毒灭活专题
if (strings.Contains(contentLower, "牛心包") || strings.Contains(contentLower, "动物源")) &&
(strings.Contains(contentLower, "病毒灭活") || strings.Contains(contentLower, "免疫原性")) {
res.Action = StepActionAddStep
res.DynamicStepsToAdd = append(res.DynamicStepsToAdd, StepSpec{
ID: "step_animal_virus_inactivation",
Role: "regulatory",
Title: "动物源性材料病毒灭活与免疫原性合规专题论证",
SubWorkspace: "tasks/dynamic_virus_inactivation",
Mode: "execute",
AllowDynamicAdjustment: false,
Accept: []string{
"核查动物源溯源合规证明与供体检疫要求",
"拟定基于ISO 22442的病毒灭活有效性验证方案",
},
})
res.Reason = "识别到动物源性材料生物风险,动态追加病毒灭活合规论证工序"
}
return res, nil
}
func hasAssumptionTopic(bb *BlackboardState, topic string) bool {
if bb == nil {
return false
}
for _, a := range bb.Assumptions {
if a.Topic == topic {
return true
}
}
return false
}