package knowledge import ( "fmt" "strings" ) // Document metadata keys for Eino schema.Document flowing through the RAG pipeline. const ( metaKBCategory = "kb_category" metaKBTitle = "kb_title" metaKBItemID = "kb_item_id" metaKBChunkIndex = "kb_chunk_index" metaSimilarity = "similarity" ) // DSL keys for [VectorEinoRetriever.Retrieve] via [retriever.WithDSLInfo]. const ( DSLRiskType = "risk_type" DSLSimilarityThreshold = "similarity_threshold" DSLSubIndexFilter = "sub_index_filter" ) // FormatEmbeddingInput matches the historical indexing format so existing embeddings // stay comparable if users skip reindex; new indexes use the same string shape. func FormatEmbeddingInput(category, title, chunkText string) string { return fmt.Sprintf("[风险类型:%s] [标题:%s]\n%s", category, title, chunkText) } // FormatQueryEmbeddingText builds the string embedded at query time so it matches // [FormatEmbeddingInput] for the same risk category (title left empty for queries). func FormatQueryEmbeddingText(riskType, query string) string { q := strings.TrimSpace(query) rt := strings.TrimSpace(riskType) if rt != "" { return FormatEmbeddingInput(rt, "", q) } return q } // MetaLookupString returns metadata string value or "" if absent. func MetaLookupString(md map[string]any, key string) string { if md == nil { return "" } v, ok := md[key] if !ok || v == nil { return "" } switch t := v.(type) { case string: return t default: return strings.TrimSpace(fmt.Sprint(t)) } } // MetaStringOK returns trimmed non-empty string and true if present and non-empty. func MetaStringOK(md map[string]any, key string) (string, bool) { s := strings.TrimSpace(MetaLookupString(md, key)) if s == "" { return "", false } return s, true } // RequireMetaString requires a non-empty string metadata field. func RequireMetaString(md map[string]any, key string) (string, error) { s, ok := MetaStringOK(md, key) if !ok { return "", fmt.Errorf("missing or empty metadata %q", key) } return s, nil } // RequireMetaInt requires an integer metadata field. func RequireMetaInt(md map[string]any, key string) (int, error) { if md == nil { return 0, fmt.Errorf("missing metadata key %q", key) } v, ok := md[key] if !ok { return 0, fmt.Errorf("missing metadata key %q", key) } switch t := v.(type) { case int: return t, nil case int32: return int(t), nil case int64: return int(t), nil case float64: return int(t), nil default: return 0, fmt.Errorf("metadata %q: unsupported type %T", key, v) } } // DSLNumeric coerces DSL map values (e.g. from JSON) to float64. func DSLNumeric(v any) (float64, bool) { switch t := v.(type) { case float64: return t, true case float32: return float64(t), true case int: return float64(t), true case int64: return float64(t), true case uint32: return float64(t), true case uint64: return float64(t), true default: return 0, false } } // MetaFloat64OK reads a float metadata value. func MetaFloat64OK(md map[string]any, key string) (float64, bool) { if md == nil { return 0, false } v, ok := md[key] if !ok { return 0, false } return DSLNumeric(v) }