Files
ttstd-infinite-canvas/backend/internal/app/cloud_agent_pi_canvas_intelligence.go
T

522 lines
16 KiB
Go

package app
import (
"context"
"fmt"
"strings"
"time"
"yingce/backend/internal/model"
)
// Canvas Intelligence Layer - 让 Agent 深度理解画布
// 这是重构的核心:从"工具调用"升级到"画布认知"
// CanvasIntelligence 画布智能层
type CanvasIntelligence struct {
service *Service
}
// NewCanvasIntelligence 创建画布智能层
func (s *Service) NewCanvasIntelligence() *CanvasIntelligence {
return &CanvasIntelligence{service: s}
}
// EnhancedCanvasContext 增强的画布上下文(传递给Pi)
type EnhancedCanvasContext struct {
// 基础信息
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
// 结构化快照
Snapshot CanvasSnapshot `json:"snapshot"`
// 智能洞察
Intelligence CanvasIntelligenceInsights `json:"intelligence"`
// 能力矩阵
Capabilities CanvasCapabilityMatrix `json:"capabilities"`
// 实时状态
RealtimeState CanvasRealtimeState `json:"realtimeState"`
}
// CanvasSnapshot 画布快照(结构化)
type CanvasSnapshot struct {
Timestamp int64 `json:"timestamp"`
TotalNodes int `json:"totalNodes"`
FocusNodes []EnhancedNodeView `json:"focusNodes"`
ContextNodes []EnhancedNodeView `json:"contextNodes"` // 焦点节点周边相关节点
Relationships []RelationshipView `json:"relationships"`
Layout LayoutAnalysis `json:"layout"`
Hierarchy HierarchyView `json:"hierarchy"`
}
// EnhancedNodeView 增强的节点视图(更智能的表示)
type EnhancedNodeView struct {
// 标识
ID string `json:"id"`
Type string `json:"type"`
// 内容摘要(智能压缩)
ContentPreview string `json:"contentPreview"` // 前200字符
ContentHash string `json:"contentHash"`
ContentLength int `json:"contentLength"`
HasFullContent bool `json:"hasFullContent"` // 是否在焦点中(有完整内容)
// 完整内容(仅焦点节点)
FullContent string `json:"fullContent,omitempty"`
// 空间信息
Position *Position `json:"position,omitempty"`
Size *Size `json:"size,omitempty"`
ZIndex int `json:"zIndex"`
// 关系
ParentID string `json:"parentId,omitempty"`
Children []string `json:"children,omitempty"`
Siblings []string `json:"siblings,omitempty"` // 同级节点
// 语义标签
Tags []string `json:"tags,omitempty"`
Category string `json:"category,omitempty"` // 自动分类
Sentiment string `json:"sentiment,omitempty"` // 情感:positive/neutral/negative/todo
Keywords []string `json:"keywords,omitempty"` // 关键词提取
// 状态
IsFocus bool `json:"isFocus"`
IsModified bool `json:"isModified"` // 最近是否被修改
Status string `json:"status"` // draft/review/approved/archived
// 元数据
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
UpdatedBy string `json:"updatedBy,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// Position 位置
type Position struct {
X float64 `json:"x"`
Y float64 `json:"y"`
}
// Size 尺寸
type Size struct {
Width float64 `json:"width"`
Height float64 `json:"height"`
}
// RelationshipView 关系视图
type RelationshipView struct {
From string `json:"from"`
To string `json:"to"`
Type string `json:"type"` // parent/reference/dependency/flow/semantic
Strength float64 `json:"strength"` // 0-1,关系强度
Bidirection bool `json:"bidirection"` // 是否双向
Label string `json:"label,omitempty"`
}
// LayoutAnalysis 布局分析
type LayoutAnalysis struct {
Algorithm string `json:"algorithm"` // detected: tree/grid/force/freeform
Direction string `json:"direction"` // horizontal/vertical/radial
Density float64 `json:"density"` // 0-1,节点密度
Clusters []ClusterInfo `json:"clusters"` // 聚类分析
Bounds Bounds `json:"bounds"` // 画布边界
ViewportFit string `json:"viewportFit"` // 视口适配建议
}
// ClusterInfo 聚类信息
type ClusterInfo struct {
ID string `json:"id"`
NodeIDs []string `json:"nodeIds"`
Center Position `json:"center"`
Label string `json:"label"` // 聚类主题
Cohesion float64 `json:"cohesion"` // 0-1,内聚度
}
// Bounds 边界
type Bounds struct {
MinX float64 `json:"minX"`
MinY float64 `json:"minY"`
MaxX float64 `json:"maxX"`
MaxY float64 `json:"maxY"`
}
// HierarchyView 层级视图
type HierarchyView struct {
MaxDepth int `json:"maxDepth"`
RootNodes []string `json:"rootNodes"`
Tree map[string]TreeNode `json:"tree"` // nodeID -> TreeNode
}
// TreeNode 树节点
type TreeNode struct {
ID string `json:"id"`
Depth int `json:"depth"`
Children []string `json:"children"`
Path []string `json:"path"` // 从根到当前节点的路径
}
// CanvasIntelligenceInsights 画布智能洞察
type CanvasIntelligenceInsights struct {
// 自动总结
Summary CanvasSummary `json:"summary"`
// 建议
Suggestions []ActionSuggestion `json:"suggestions"`
// 模式识别
Patterns []PatternRecognition `json:"patterns"`
// 问题诊断
Issues []CanvasIssue `json:"issues"`
// 趋势分析
Trends TrendAnalysis `json:"trends"`
}
// CanvasSummary 画布总结
type CanvasSummary struct {
OneSentence string `json:"oneSentence"` // 一句话总结
MainTopics []string `json:"mainTopics"` // 主要主题
Progress string `json:"progress"` // 进度评估:early/mid/mature
Completeness float64 `json:"completeness"` // 0-1,完整性评分
}
// ActionSuggestion 行动建议
type ActionSuggestion struct {
Priority int `json:"priority"` // 1-5
Action string `json:"action"` // 建议的操作
Reason string `json:"reason"` // 原因
Impact string `json:"impact"` // 预期影响
}
// PatternRecognition 模式识别
type PatternRecognition struct {
Pattern string `json:"pattern"` // 识别的模式名称
Confidence float64 `json:"confidence"` // 0-1,置信度
Evidence []string `json:"evidence"` // 证据节点
Description string `json:"description"` // 描述
}
// CanvasIssue 画布问题
type CanvasIssue struct {
Severity string `json:"severity"` // low/medium/high/critical
Type string `json:"type"` // orphan/duplicate/inconsistent/incomplete
Description string `json:"description"` // 问题描述
AffectedNodes []string `json:"affectedNodes"` // 受影响的节点
FixSuggestion string `json:"fixSuggestion"` // 修复建议
}
// TrendAnalysis 趋势分析
type TrendAnalysis struct {
RecentActivity ActivityLevel `json:"recentActivity"`
GrowthRate float64 `json:"growthRate"` // 节点增长率
HotAreas []string `json:"hotAreas"` // 热门区域
CollaborationMap map[string]int `json:"collaborationMap"` // userID -> 贡献数
}
// ActivityLevel 活动水平
type ActivityLevel struct {
Level string `json:"level"` // low/medium/high
LastUpdated int64 `json:"lastUpdated"` // 最后更新时间
UpdateCount int `json:"updateCount"` // 更新次数(最近1小时)
}
// CanvasCapabilityMatrix 画布能力矩阵
type CanvasCapabilityMatrix struct {
// 节点类型支持
SupportedNodeTypes []NodeTypeCapability `json:"supportedNodeTypes"`
// 操作权限
Permissions CanvasPermissions `json:"permissions"`
// 限制
Limits CanvasLimits `json:"limits"`
// 特性开关
Features map[string]bool `json:"features"`
// 扩展点
Extensions []ExtensionPoint `json:"extensions"`
}
// NodeTypeCapability 节点类型能力
type NodeTypeCapability struct {
Type string `json:"type"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Icon string `json:"icon,omitempty"`
Creatable bool `json:"creatable"`
Operations []string `json:"operations"` // read/update/delete/move/duplicate
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// CanvasPermissions 画布权限
type CanvasPermissions struct {
CanRead bool `json:"canRead"`
CanCreate bool `json:"canCreate"`
CanUpdate bool `json:"canUpdate"`
CanDelete bool `json:"canDelete"`
CanMove bool `json:"canMove"`
CanShare bool `json:"canShare"`
RequireApproval []string `json:"requireApproval"` // 需要审批的操作
}
// CanvasLimits 画布限制
type CanvasLimits struct {
MaxNodes int `json:"maxNodes"`
MaxDepth int `json:"maxDepth"`
MaxContentSize int `json:"maxContentSize"` // bytes
MaxBatchSize int `json:"maxBatchSize"` // 批量操作限制
}
// ExtensionPoint 扩展点
type ExtensionPoint struct {
Name string `json:"name"`
Type string `json:"type"` // hook/plugin/custom
Enabled bool `json:"enabled"`
Description string `json:"description"`
}
// CanvasRealtimeState 画布实时状态
type CanvasRealtimeState struct {
OnlineUsers []string `json:"onlineUsers"`
ActiveCursors map[string]Position `json:"activeCursors"` // userID -> Position
LockedNodes []string `json:"lockedNodes"`
PendingOperations int `json:"pendingOperations"`
SyncStatus string `json:"syncStatus"` // synced/syncing/conflict
}
// BuildEnhancedCanvasContext constructs the Agent context from CanvasProject,
// the application's persisted canvas document. The extension Canvas/CanvasNode
// models are not part of the production schema.
func (ci *CanvasIntelligence) BuildEnhancedCanvasContext(ctx context.Context, userID, canvasID string, focusNodeIDs []string) (*EnhancedCanvasContext, error) {
canvas, err := ci.service.repo.CanvasProjectForUser(userID, canvasID)
if err != nil {
return nil, fmt.Errorf("get canvas: %w", err)
}
document, err := creationDocument(canvas.PayloadJSON)
if err != nil {
return nil, fmt.Errorf("parse canvas document: %w", err)
}
nodes := canvasIntelligenceNodes(canvas, document)
// 2. 构建结构化快照
snapshot := ci.buildSnapshot(nodes, focusNodeIDs, canvasIntelligenceRelationships(document, nodes))
// 3. 生成智能洞察
intelligence := ci.generateIntelligence(nodes, snapshot)
// 4. 构建能力矩阵
capabilities := ci.buildCapabilityMatrix(userID, canvasID)
// 5. 获取实时状态
realtimeState := ci.getRealtimeState(ctx, document)
return &EnhancedCanvasContext{
ID: canvasID,
Name: canvas.Title,
Description: stringValue(document["description"]),
Snapshot: snapshot,
Intelligence: intelligence,
Capabilities: capabilities,
RealtimeState: realtimeState,
}, nil
}
// buildSnapshot 构建画布快照
func (ci *CanvasIntelligence) buildSnapshot(nodes []model.CanvasNode, focusNodeIDs []string, explicitRelationships ...[]RelationshipView) CanvasSnapshot {
focusMap := make(map[string]bool)
for _, id := range focusNodeIDs {
focusMap[id] = true
}
// 构建节点视图
focusNodes := make([]EnhancedNodeView, 0)
contextNodes := make([]EnhancedNodeView, 0)
for _, node := range nodes {
view := ci.buildEnhancedNodeView(node, focusMap[node.ID])
if focusMap[node.ID] {
focusNodes = append(focusNodes, view)
} else {
// 判断是否是上下文节点(焦点节点的相邻节点)
if ci.isContextNode(node, focusNodeIDs, nodes) {
contextNodes = append(contextNodes, view)
}
}
}
// 构建关系
relationships := ci.buildRelationships(nodes)
if len(explicitRelationships) > 0 {
relationships = append(explicitRelationships[0], relationships...)
}
// 分析布局
layout := ci.analyzeLayout(nodes)
// 构建层级
hierarchy := ci.buildHierarchy(nodes)
return CanvasSnapshot{
Timestamp: time.Now().Unix(),
TotalNodes: len(nodes),
FocusNodes: focusNodes,
ContextNodes: contextNodes,
Relationships: relationships,
Layout: layout,
Hierarchy: hierarchy,
}
}
func canvasIntelligenceNodes(canvas *model.CanvasProject, document map[string]any) []model.CanvasNode {
rawNodes := creationMaps(document["nodes"])
nodes := make([]model.CanvasNode, 0, len(rawNodes))
children := make(map[string][]string)
for _, raw := range rawNodes {
id := stringValue(raw["id"])
if id == "" {
continue
}
metadata := canvasIntelligenceMetadata(raw)
content := firstNonEmpty(
stringValue(raw["content"]),
stringValue(metadata["content"]),
stringValue(raw["title"]),
)
var parentID *string
if value := stringValue(raw["parentId"]); value != "" {
parentID = &value
children[value] = append(children[value], id)
}
nodes = append(nodes, model.CanvasNode{
ID: id, CanvasID: canvas.ID, Type: stringValue(raw["type"]), Content: content,
Position: canvasIntelligencePosition(raw), Size: canvasIntelligenceSize(raw), ParentID: parentID, Metadata: metadata,
CreatedAt: canvas.CreatedAt, UpdatedAt: canvas.UpdatedAt,
})
}
for index := range nodes {
nodes[index].Children = children[nodes[index].ID]
}
return nodes
}
func canvasIntelligenceMetadata(raw map[string]any) map[string]any {
metadata := map[string]any{}
if value, ok := raw["metadata"].(map[string]any); ok {
for key, item := range value {
metadata[key] = item
}
}
if value, ok := raw["data"].(map[string]any); ok {
for key, item := range value {
if _, exists := metadata[key]; !exists {
metadata[key] = item
}
}
}
return metadata
}
func canvasIntelligencePosition(raw map[string]any) *model.CanvasPosition {
position, _ := raw["position"].(map[string]any)
x, y := numberValue(raw["x"], 0), numberValue(raw["y"], 0)
if position != nil {
x, y = numberValue(position["x"], x), numberValue(position["y"], y)
}
if position == nil && raw["x"] == nil && raw["y"] == nil {
return nil
}
return &model.CanvasPosition{X: x, Y: y}
}
func canvasIntelligenceSize(raw map[string]any) *model.CanvasSize {
size, _ := raw["size"].(map[string]any)
width, height := numberValue(raw["width"], 0), numberValue(raw["height"], 0)
if size != nil {
width, height = numberValue(size["width"], width), numberValue(size["height"], height)
}
if size == nil && raw["width"] == nil && raw["height"] == nil {
return nil
}
return &model.CanvasSize{Width: width, Height: height}
}
func canvasIntelligenceRelationships(document map[string]any, nodes []model.CanvasNode) []RelationshipView {
known := make(map[string]bool, len(nodes))
for _, node := range nodes {
known[node.ID] = true
}
relationships := make([]RelationshipView, 0)
for _, edge := range creationMaps(document["connections"]) {
from, to := stringValue(edge["fromNodeId"]), stringValue(edge["toNodeId"])
if from == "" || to == "" || !known[from] || !known[to] {
continue
}
typeName := strings.TrimSpace(stringValue(edge["type"]))
if typeName == "" {
typeName = "flow"
}
relationships = append(relationships, RelationshipView{
From: from, To: to, Type: typeName, Strength: 1, Label: stringValue(edge["label"]),
})
}
return relationships
}
// buildEnhancedNodeView 构建增强节点视图
func (ci *CanvasIntelligence) buildEnhancedNodeView(node model.CanvasNode, isFocus bool) EnhancedNodeView {
// 智能内容摘要
preview := node.Content
if len(preview) > 200 {
preview = preview[:200] + "..."
}
// 提取关键词(简化实现)
keywords := ci.extractKeywords(node.Content)
// 自动分类
category := ci.categorizeNode(node)
// 情感分析(简化)
sentiment := ci.analyzeSentiment(node.Content)
view := EnhancedNodeView{
ID: node.ID,
Type: node.Type,
ContentPreview: preview,
ContentHash: hashContentSHA256(node.Content),
ContentLength: len(node.Content),
HasFullContent: isFocus,
Position: ci.convertPosition(node.Position),
Size: ci.convertSize(node.Size),
ZIndex: ci.getZIndex(node),
ParentID: ci.getParentID(node.ParentID),
Children: node.Children,
Tags: ci.extractTags(node),
Category: category,
Sentiment: sentiment,
Keywords: keywords,
IsFocus: isFocus,
IsModified: ci.isRecentlyModified(node),
Status: ci.getNodeStatus(node),
CreatedAt: node.CreatedAt.Unix(),
UpdatedAt: node.UpdatedAt.Unix(),
Metadata: node.Metadata,
}
if isFocus {
view.FullContent = node.Content
}
return view
}
// Continue in next chunk...