diff --git a/cmd/agent/BUILD.bazel b/cmd/agent/BUILD.bazel index bed195f..5fdd647 100644 --- a/cmd/agent/BUILD.bazel +++ b/cmd/agent/BUILD.bazel @@ -11,6 +11,10 @@ go_library( "INSTRUCTION_MODIFIER.md", "INSTRUCTION_ORCHESTRATOR.md", "INSTRUCTION_RETRIEVAL.md", + "INSTRUCTION.md", + "skills/youtube/SKILL.md", + "skills/youtube/references/seo-guide.md", + "skills/youtube/references/workflows.md", ], importpath = "github.com/eat-pray-ai/yutu/cmd/agent", visibility = ["//visibility:public"], @@ -29,6 +33,8 @@ go_library( "@org_golang_google_adk_v2//tool", "@org_golang_google_adk_v2//tool/geminitool", "@org_golang_google_adk_v2//tool/mcptoolset", + "@org_golang_google_adk_v2//tool/skilltoolset", + "@org_golang_google_adk_v2//tool/skilltoolset/skill", "@org_golang_google_genai//:genai", ], ) diff --git a/cmd/agent/INSTRUCTION_MODIFIER.md b/cmd/agent/INSTRUCTION_MODIFIER.md index 57a8871..449aa8f 100644 --- a/cmd/agent/INSTRUCTION_MODIFIER.md +++ b/cmd/agent/INSTRUCTION_MODIFIER.md @@ -3,7 +3,7 @@ user. You are transferred to by the orchestrator with a task description. You have access to both read and write tools. You can look up channels, search for videos, list comments, list playlists, list playlist items, list captions, and list subscriptions to gather any context you need — then use write -tools to create or update content. You are self-sufficient for multi-step tasks that involve both data gathering and +tools to create or update content. You are self-sufficient for multistep tasks that involve both data gathering and content modification. ## Workflow diff --git a/cmd/agent/INSTRUCTION_ORCHESTRATOR.md b/cmd/agent/INSTRUCTION_ORCHESTRATOR.md index 12ee197..2171fed 100644 --- a/cmd/agent/INSTRUCTION_ORCHESTRATOR.md +++ b/cmd/agent/INSTRUCTION_ORCHESTRATOR.md @@ -9,7 +9,7 @@ You coordinate a multi-agent system with three specialized agents, all invoked v channel or YouTube in general. - **Modifier**: Transfer for creating or updating content — posting comments, uploading videos, creating playlists, updating metadata, setting thumbnails. This agent can also look up channels, videos, comments, and playlists to - gather any context it needs before making modifications. For multi-step tasks that involve both reading and writing, + gather any context it needs before making modifications. For multistep tasks that involve both reading and writing, transfer directly to Modifier — it is self-sufficient. - **Destroyer**: Transfer for deleting content — removing videos, playlists, comments, captions, subscriptions. This agent can verify targets before deletion. Always confirm with the user before transferring to Destroyer. @@ -18,8 +18,8 @@ You coordinate a multi-agent system with three specialized agents, all invoked v For every user request, follow this process: -1. **Classify**: Is this a read-only query, a modification, a deletion, or a multi-step workflow? -2. **Plan**: For multi-step tasks, break them into numbered steps and state which agent handles each. +1. **Classify**: Is this a read-only query, a modification, a deletion, or a multistep workflow? +2. **Plan**: For multistep tasks, break them into numbered steps and state which agent handles each. 3. **Route**: Transfer to the appropriate agent with a clear, complete description of the full task. The sub-agent will respond directly to the user — you do not need to summarize or verify their output. @@ -29,7 +29,7 @@ For every user request, follow this process: - Create or update content → Transfer to **Modifier** - Delete content → Confirm with user first, then transfer to **Destroyer** -### Multi-Step Workflows +### multistep Workflows When a task requires both reading and writing, **transfer to Modifier directly** with the full task description. The Modifier agent has read tools and can gather data then act on it in a single flow. Do NOT try to gather data diff --git a/cmd/agent/agent.go b/cmd/agent/agent.go index 5162620..e024b48 100644 --- a/cmd/agent/agent.go +++ b/cmd/agent/agent.go @@ -156,7 +156,13 @@ func launch(ctx context.Context, writer io.Writer, args []string) { os.Exit(1) } - orchestrator, err := buildOrchestrator(advancedModel, liteModel, mcpToolSet) + skillToolset, err := newSkillToolset(ctx) + if err != nil { + slog.ErrorContext(ctx, "failed to create skill toolset", "error", err) + os.Exit(1) + } + + orchestrator, err := buildOrchestrator(advancedModel, liteModel, mcpToolSet, skillToolset) if err != nil { slog.ErrorContext(ctx, "failed to build orchestrator agent", "error", err) os.Exit(1) diff --git a/cmd/agent/agents.go b/cmd/agent/agents.go index 86d576c..f341d37 100644 --- a/cmd/agent/agents.go +++ b/cmd/agent/agents.go @@ -4,7 +4,8 @@ package agent import ( - _ "embed" + "context" + "embed" "fmt" "os" @@ -13,9 +14,14 @@ import ( "google.golang.org/adk/v2/model" "google.golang.org/adk/v2/tool" "google.golang.org/adk/v2/tool/geminitool" + "google.golang.org/adk/v2/tool/skilltoolset" + "google.golang.org/adk/v2/tool/skilltoolset/skill" ) var ( + //go:embed INSTRUCTION.md + instruction string + //go:embed INSTRUCTION_ORCHESTRATOR.md orchestratorInstruction string @@ -27,6 +33,9 @@ var ( //go:embed INSTRUCTION_DESTROYER.md destroyerInstruction string + + //go:embed skills + skillsFS embed.FS ) type agentDef struct { @@ -40,7 +49,7 @@ type agentDef struct { var agentDefs = map[string]agentDef{ "Nina": { name: "YouTube Copilot", - description: "Orchestrates YouTube workflows by planning multi-step tasks and delegating to specialized agents.", + description: "Orchestrates YouTube workflows by planning multistep tasks and delegating to specialized agents.", instruction: &orchestratorInstruction, envKey: "YUTU_AGENT_INSTRUCTION", }, @@ -170,8 +179,17 @@ func init() { } } +func newSkillToolset(ctx context.Context) (tool.Toolset, error) { + source := skill.NewFileSystemSource(skillsFS) + source, _, err := skill.WithCompletePreloadSource(ctx, source) + if err != nil { + return nil, fmt.Errorf("preload skills: %w", err) + } + return skilltoolset.New(ctx, skilltoolset.Config{Source: source}) +} + func buildOrchestrator( - advancedModel, liteModel model.LLM, mcpToolSet tool.Toolset, + advancedModel, liteModel model.LLM, mcpToolSet, skillToolset tool.Toolset, ) ( agent.Agent, error, ) { @@ -210,6 +228,7 @@ func buildOrchestrator( Description: oDef.description, Instruction: *oDef.instruction, Tools: []tool.Tool{geminitool.GoogleSearch{}}, + Toolsets: []tool.Toolset{skillToolset}, SubAgents: subAgents, }, )