Files
ttstd-infinite-canvas/backend/internal/app/cloud_agent_pi_request.go

242 lines
7.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 组装交给 Agent 运行时的请求:工具清单、技能、画像、记忆、功能开关、权限与模型配置。
//
// 权限由服务端按画布归属计算后下发,运行时只能在这个范围内调用工具。
package app
import (
"context"
"encoding/json"
"fmt"
"log"
"log/slog"
"path/filepath"
)
// EnhancedPiRequestParams 增强的 Pi 请求参数
type EnhancedPiRequestParams struct {
UserID string
RunID string
CanvasID string
FocusNodeIDs []string
Prompt string
SystemPrompt string
ModelID string
RuntimeState *cloudAgentRuntime
Canonical *canonicalAgentRequest
SessionJSONL string
}
// buildEnhancedPiRequest 构建增强的 Pi 请求(核心方法)
func (s *Service) buildEnhancedPiRequest(ctx context.Context, params EnhancedPiRequestParams) (cloudAgentPiProcessRequest, error) {
slog.Debug("agent building request", "canvas", params.CanvasID)
// 1. 构建工具列表(包括画布工具)
tools := s.buildCompletePiTools(params.Canonical.Tools)
// 2. 构建 Skills 配置。技能内容只经服务端读取工具提供,不交给运行时按路径加载:
// 技能路径是相对路径,运行时按自己的工作目录解析,会读到无关文件。
enabledSkills := s.buildSkillManifests(params.RuntimeState.Skills)
slog.Debug("agent enabled skills", "count", len(enabledSkills))
// 3. 构建 Profile 配置
profile := s.buildProfileConfig(params.RuntimeState.Profile)
// 4. 构建 Memory 配置
memory := s.buildMemoryConfig(params.UserID, params.CanvasID)
// 5. 构建画布智能上下文(核心创新)
canvasIntelligence := s.NewCanvasIntelligence()
enhancedCanvas, err := canvasIntelligence.BuildEnhancedCanvasContext(ctx, params.UserID, params.CanvasID, params.FocusNodeIDs)
if err != nil {
return cloudAgentPiProcessRequest{}, fmt.Errorf("build canvas intelligence: %w", err)
}
canvasData, err := s.marshalEnhancedCanvas(enhancedCanvas)
if err != nil {
return cloudAgentPiProcessRequest{}, fmt.Errorf("marshal canvas: %w", err)
}
slog.Debug("agent canvas intelligence", "nodes",
enhancedCanvas.Snapshot.TotalNodes,
"focus", len(enhancedCanvas.Snapshot.FocusNodes),
"relationships", len(enhancedCanvas.Snapshot.Relationships),
"clusters", len(enhancedCanvas.Snapshot.Layout.Clusters))
// 6. 构建 Features 配置
features := s.buildFeaturesConfig(params.RuntimeState)
// 7. 构建压缩策略
contextBudget := s.cloudAgentContextBudgetForRequest(params.RuntimeState.Request)
compaction := s.buildCompactionStrategy(params.UserID, params.CanvasID, contextBudget)
// 8. 构建权限配置
permissions := s.buildPermissionsConfig(params.UserID, params.CanvasID)
// 9. 构建模型配置
modelConfig := s.buildModelConfig(params.ModelID, params.RuntimeState)
// 10. 组装完整请求
request := cloudAgentPiProcessRequest{
// 会话信息:只传数据库里的快照。会话文件与工作目录由运行时在自己的
// 临时目录里创建,独立容器里没有服务端的数据目录。
SessionJSONL: params.SessionJSONL,
// 标识信息
SessionId: params.RunID,
UserId: params.UserID,
CanvasId: params.CanvasID,
RunId: params.RunID,
// 对话内容
Prompt: params.Prompt,
SystemPrompt: params.SystemPrompt,
// 核心增强:完整的上下文传递
EnabledSkills: enabledSkills,
Profile: profile,
Memory: memory,
Canvas: canvasData,
Features: features,
// 配置
Tools: tools,
Compaction: compaction,
Permissions: permissions,
Model: modelConfig,
}
slog.Debug("agent request built", "canvas", params.CanvasID)
return request, nil
}
// buildCompletePiTools 返回本轮授权的平台工具(与 Go 业务执行器同一份定义)。
func (s *Service) buildCompletePiTools(canonicalTools []map[string]interface{}) []map[string]any {
return piToolDefinitions(canonicalTools)
}
// buildProfileConfig 构建 Profile 配置
func (s *Service) buildProfileConfig(profile cloudAgentProfileSnapshot) map[string]any {
layers := make([]map[string]any, 0, len(profile.Layers))
for i, layer := range profile.Layers {
layers = append(layers, map[string]any{
"scope": layer.Scope,
"content": layer.Content,
"priority": i,
"hash": hashContentSHA256(layer.Content),
})
}
return map[string]any{
"revision": profile.Revision,
"hash": profile.Hash,
"layers": layers,
"enabled": true,
}
}
// buildMemoryConfig 构建 Memory 配置
func (s *Service) buildMemoryConfig(userID, canvasID string) map[string]any {
memoryDir := filepath.Join(s.dataDir, "memory", userID)
canvasMemoryDir := filepath.Join(memoryDir, canvasID)
return map[string]any{
"enabled": true,
"storePath": memoryDir,
"canvasStorePath": canvasMemoryDir,
"userId": userID,
"canvasId": canvasID,
"maxEntries": 1000,
"indexingEnabled": true,
"searchEnabled": true,
"autoSaveInterval": 60, // seconds
"compressionEnabled": true,
}
}
// buildFeaturesConfig 构建 Features 配置
func (s *Service) buildFeaturesConfig(state *cloudAgentRuntime) map[string]any {
return map[string]any{
"planningEnabled": true,
"formsEnabled": true,
"memoryEnabled": true,
"skillsEnabled": len(state.Skills) > 0,
"canvasIntelligence": true,
"approvalRequired": []string{"canvas_node_delete", "canvas_bulk_operation"},
"autoSave": true,
"collaborationMode": "multi-user",
"versionControl": true,
}
}
// buildPermissionsConfig 根据当前账号的画布归属生成运行时权限。
// userID 是登录账号,canvasID 是画布 ID(不是所属项目 ID);查询失败时拒绝访问。
func (s *Service) buildPermissionsConfig(userID, canvasID string) map[string]any {
// 与画布读写工具共用账号归属查询,不访问旧的 Canvas 扩展模型。
_, err := s.repo.CanvasProjectForUser(userID, canvasID)
if err != nil {
log.Printf("[Agent] failed to get canvas for permissions: %v", err)
// 返回最小权限集
return map[string]any{
"canReadCanvas": false,
"canWriteCanvas": false,
"canDeleteNodes": false,
"canCreateNodes": false,
"canMoveNodes": false,
"canDuplicateNodes": false,
"canManageRelations": false,
"canInviteUsers": false,
"canExportCanvas": false,
"maxTokenBudget": 200000,
"maxSteps": 50,
}
}
return map[string]any{
"canReadCanvas": true,
"canWriteCanvas": true,
"canDeleteNodes": true,
"canCreateNodes": true,
"canMoveNodes": true,
"canDuplicateNodes": true,
"canManageRelations": true,
"canInviteUsers": true,
"canExportCanvas": true,
"maxTokenBudget": 200000,
"maxSteps": 50,
}
}
// buildModelConfig 构建模型配置
func (s *Service) buildModelConfig(modelID string, state *cloudAgentRuntime) map[string]any {
budget := s.cloudAgentContextBudgetForRequest(state.Request)
return map[string]any{
"id": modelID,
"name": modelID,
"reasoning": cloudAgentReasoningEnabled(state.Policy.ReasoningMode),
"input": []string{"text", "image"},
"contextWindow": budget.ContextWindowTokens,
"maxTokens": budget.MaxOutputTokens,
"provider": state.Request.ChannelID,
"switchable": true,
"temperature": 0.7,
"topP": 0.9,
}
}
// marshalEnhancedCanvas 序列化增强画布
func (s *Service) marshalEnhancedCanvas(canvas *EnhancedCanvasContext) (map[string]any, error) {
data, err := json.Marshal(canvas)
if err != nil {
return nil, err
}
var result map[string]any
if err := json.Unmarshal(data, &result); err != nil {
return nil, err
}
return result, nil
}