This commit is contained in:
Eric 1549169735@qq.com
2026-09-13 20:08:13 +08:00
parent 7cc06976ab
commit da3f16e6db
65 changed files with 3855 additions and 2153 deletions

View File

@@ -8,13 +8,12 @@ import (
"git.iwork-ai.com/xdc/iqudo-top1/internal/model"
)
var allowedNodeTypes = map[string]bool{"start": true, "message": true, "question": true, "form": true, "choice": true, "condition": true, "knowledge": true, "escalate": true, "finish": true}
var allowedNodeTypes = map[string]bool{"start": true, "message": true, "question": true, "form": true, "choice": true, "condition": true, "stage": true, "escalate": true, "finish": true}
var allowedConditionOperators = map[string]bool{"equals": true, "not_equals": true, "contains": true, "greater_than": true, "less_than": true, "exists": true, "not_exists": true, "in": true}
type ValidationContext struct {
Fields []model.ScenarioField
KnowledgeItems []model.KnowledgeItem
KnowledgeRelations []model.KnowledgeRelation
Fields []model.ScenarioField
StageKeys []string
}
func ValidateGraph(startNodeKey string, nodes []model.SOPNode, edges []model.SOPEdge) []string {
@@ -135,22 +134,9 @@ func ValidateForPublish(startNodeKey string, nodes []model.SOPNode, edges []mode
for _, field := range context.Fields {
fieldMap[field.FieldKey] = field
}
knowledgeByKey := make(map[string]model.KnowledgeItem, len(context.KnowledgeItems))
knowledgeTypes := make(map[string]bool)
knowledgeIDs := make(map[uint64]bool, len(context.KnowledgeItems))
for _, item := range context.KnowledgeItems {
if item.Status != "active" {
continue
}
knowledgeByKey[item.ItemKey] = item
knowledgeTypes[item.Type] = true
knowledgeIDs[item.ID] = true
}
relationTypes := make(map[string]bool)
for _, relation := range context.KnowledgeRelations {
if knowledgeIDs[relation.FromKnowledgeID] && knowledgeIDs[relation.ToKnowledgeID] {
relationTypes[relation.RelationType] = true
}
stageKeys := make(map[string]bool, len(context.StageKeys))
for _, key := range context.StageKeys {
stageKeys[key] = true
}
collected := map[string]bool{}
nodeMap := make(map[string]model.SOPNode, len(nodes))
@@ -191,33 +177,25 @@ func ValidateForPublish(startNodeKey string, nodes []model.SOPNode, edges []mode
}
collected[fieldKey] = true
}
case "knowledge":
selector, ok := config["knowledge_selector"].(map[string]interface{})
if !ok {
problems = append(problems, fmt.Sprintf("节点“%s”没有配置知识选择器", node.Title))
break
case "stage":
stageKey, _ := config["stage_key"].(string)
if stageKey == "" {
problems = append(problems, fmt.Sprintf("节点“%s”没有绑定话术包阶段", node.Title))
} else if len(stageKeys) > 0 && !stageKeys[stageKey] {
problems = append(problems, fmt.Sprintf("节点“%s”绑定的话术包阶段 %s 不存在", node.Title, stageKey))
}
validateKnowledgeSelector(node, selector, knowledgeByKey, knowledgeTypes, relationTypes, &problems)
if collection, ok := config["knowledge_collection"].(map[string]interface{}); ok {
if keys, ok := collection["context_field_keys"].([]interface{}); ok {
for _, value := range keys {
if key, ok := value.(string); ok {
if _, exists := fieldMap[key]; !exists {
problems = append(problems, fmt.Sprintf("节点“%s”引用的采集字段 %s 不存在", node.Title, key))
} else {
collected[key] = true
}
}
if keys, ok := config["field_keys"].([]interface{}); ok {
for _, value := range keys {
fieldKey, keyOK := value.(string)
if !keyOK || fieldKey == "" {
problems = append(problems, fmt.Sprintf("节点“%s”包含无效的表单字段", node.Title))
continue
}
}
if steps, ok := collection["steps"].([]interface{}); ok {
for _, raw := range steps {
if step, ok := raw.(map[string]interface{}); ok {
if key, ok := step["field_key"].(string); ok && key != "" {
collected[key] = true
}
}
if _, exists := fieldMap[fieldKey]; !exists {
problems = append(problems, fmt.Sprintf("节点“%s”引用的字段 %s 不存在", node.Title, fieldKey))
continue
}
collected[fieldKey] = true
}
}
}
@@ -354,63 +332,6 @@ func isDefaultCondition(value []byte) bool {
return ok && len(object) == 0
}
func validateKnowledgeSelector(node model.SOPNode, selector map[string]interface{}, items map[string]model.KnowledgeItem, availableTypes, availableRelations map[string]bool, problems *[]string) {
derivedField, _ := selector["derived_field"].(string)
answerField, _ := selector["answer_field"].(string)
keys, keysValid := selectorStrings(selector, "knowledge_keys")
types, typesValid := selectorStrings(selector, "knowledge_types")
relations, relationsValid := selectorStrings(selector, "relation_types")
if !keysValid || !typesValid || !relationsValid {
*problems = append(*problems, fmt.Sprintf("节点“%s”的知识选择器必须使用字符串数组", node.Title))
return
}
if derivedField == "" && answerField == "" && len(keys) == 0 {
*problems = append(*problems, fmt.Sprintf("节点“%s”没有配置派生知识字段或固定知识 key", node.Title))
}
allowedTypes := make(map[string]bool, len(types))
for _, itemType := range types {
allowedTypes[itemType] = true
if !availableTypes[itemType] {
*problems = append(*problems, fmt.Sprintf("节点“%s”引用的知识类型 %s 不存在", node.Title, itemType))
}
}
for _, key := range keys {
item, exists := items[key]
if !exists {
*problems = append(*problems, fmt.Sprintf("节点“%s”引用的知识 key %s 不存在或未启用", node.Title, key))
continue
}
if len(allowedTypes) > 0 && !allowedTypes[item.Type] {
*problems = append(*problems, fmt.Sprintf("节点“%s”的知识 key %s 不属于允许的根类型", node.Title, key))
}
}
for _, relationType := range relations {
if !availableRelations[relationType] {
*problems = append(*problems, fmt.Sprintf("节点“%s”引用的知识关系类型 %s 不存在", node.Title, relationType))
}
}
}
func selectorStrings(selector map[string]interface{}, key string) ([]string, bool) {
raw, exists := selector[key]
if !exists || raw == nil {
return nil, true
}
values, ok := raw.([]interface{})
if !ok {
return nil, false
}
result := make([]string, 0, len(values))
for _, value := range values {
text, ok := value.(string)
if !ok || text == "" {
return nil, false
}
result = append(result, text)
}
return result, true
}
func canReachType(start, nodeType string, nodes map[string]model.SOPNode, adjacency map[string][]string) bool {
visited := map[string]bool{}
var walk func(string) bool