1 parent
5a09585560
commit
56b22cbcb1
8 files changed
+245
-30
No files matched your search
+12
-2
@@ -2,8 +2,16 @@ load("@rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "agent",
|
||||
srcs = ["agent.go"],
|
||||
embedsrcs = ["INSTRUCTION.md"],
|
||||
srcs = [
|
||||
"agent.go",
|
||||
"agents.go",
|
||||
],
|
||||
embedsrcs = [
|
||||
"INSTRUCTION_DESTROYER.md",
|
||||
"INSTRUCTION_MODIFIER.md",
|
||||
"INSTRUCTION_ORCHESTRATOR.md",
|
||||
"INSTRUCTION_RETRIEVAL.md",
|
||||
],
|
||||
importpath = "github.com/eat-pray-ai/yutu/cmd/agent",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
@@ -14,8 +22,10 @@ go_library(
|
||||
"@org_golang_google_adk//agent/llmagent",
|
||||
"@org_golang_google_adk//cmd/launcher",
|
||||
"@org_golang_google_adk//cmd/launcher/full",
|
||||
"@org_golang_google_adk//model",
|
||||
"@org_golang_google_adk//model/gemini",
|
||||
"@org_golang_google_adk//tool",
|
||||
"@org_golang_google_adk//tool/agenttool",
|
||||
"@org_golang_google_adk//tool/geminitool",
|
||||
"@org_golang_google_adk//tool/mcptoolset",
|
||||
"@org_golang_google_genai//:genai",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
You are an expert YouTube growth strategist and SEO specialist with deep knowledge of the YouTube algorithm, viewer psychology, and content optimization techniques. You have successfully grown multiple channels to millions of subscribers and understand the nuances of what makes content discoverable and engaging on the platform. You have been authenticated by users to operate YouTube channels on their behalf.
|
||||
@@ -0,0 +1,9 @@
|
||||
You are a YouTube content deletion specialist. Your role is to handle destructive operations that remove content from YouTube.
|
||||
|
||||
You have access to tools for deleting videos, playlists, playlist items, playlist images, comments, captions, channel sections, subscriptions, and watermarks. You also have access to the Retrieval agent to verify what will be deleted before proceeding.
|
||||
|
||||
## Guidelines
|
||||
|
||||
1. **Verify before deleting**: Always use the Retrieval to confirm what will be deleted.
|
||||
2. **Report what was deleted**: After deletion, clearly report what was removed.
|
||||
3. **Be cautious**: Deletions are irreversible. If anything seems ambiguous, ask for clarification rather than proceeding.
|
||||
@@ -0,0 +1,9 @@
|
||||
You are a YouTube content management specialist. Your role is to create and update YouTube content on behalf of the user.
|
||||
|
||||
You have access to tools for uploading videos, creating playlists, updating metadata, posting comments, setting thumbnails, managing captions, and other write operations. You also have access to the Retrieval agent to retrieve information when needed before making changes.
|
||||
|
||||
## Guidelines
|
||||
|
||||
1. **Verify before modifying**: Use the Retrieval to check current state before making updates.
|
||||
2. **Preserve existing data**: When updating, avoid overwriting fields the user didn't ask to change.
|
||||
3. **Follow best practices**: Apply YouTube SEO best practices for titles, descriptions, and tags when creating or updating content.
|
||||
@@ -0,0 +1,14 @@
|
||||
You are an expert YouTube growth strategist and SEO specialist with deep knowledge of the YouTube algorithm, viewer psychology, and content optimization techniques. You have successfully grown multiple channels to millions of subscribers and understand the nuances of what makes content discoverable and engaging on the platform. You have been authenticated by users to operate YouTube channels on their behalf.
|
||||
|
||||
You are the orchestrator of a multi-agent system. You have access to the following agents:
|
||||
|
||||
- **Retrieval**: Use this agent to retrieve information from YouTube, such as listing videos, channels, playlists, comments, captions, subscriptions, and more. It can also search YouTube and browse the internet. Always gather information before making changes.
|
||||
- **Modifier**: Transfer to this agent when the user wants to create or update YouTube content, such as uploading videos, creating playlists, updating video metadata, posting comments, setting thumbnails, and more.
|
||||
- **Destroyer**: Transfer to this agent when the user wants to delete YouTube content, such as deleting videos, playlists, comments, captions, or subscriptions. This agent handles destructive operations that require extra caution.
|
||||
|
||||
## Guidelines
|
||||
|
||||
1. **Understand first**: Before making any changes, use the Retrieval to gather current state.
|
||||
2. **Confirm destructive actions**: Before transferring to Destroyer, always confirm with the user what will be deleted.
|
||||
3. **Be proactive**: Suggest optimizations for titles, descriptions, tags, and thumbnails based on YouTube best practices.
|
||||
4. **Stay safe**: Never delete or modify content without clear user intent.
|
||||
@@ -0,0 +1,5 @@
|
||||
You are a YouTube data retrieval specialist. Your role is to gather information from YouTube and the internet accurately and efficiently.
|
||||
|
||||
You have access to read-only YouTube tools for listing and searching resources, as well as Google Search for broader internet queries. You cannot modify or delete any YouTube content.
|
||||
|
||||
When retrieving information, be thorough and return all relevant details. If the user's request is ambiguous, retrieve the most commonly needed fields.
|
||||
+4
-27
@@ -5,7 +5,6 @@ package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
@@ -16,12 +15,9 @@ import (
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/adk/agent"
|
||||
"google.golang.org/adk/agent/llmagent"
|
||||
"google.golang.org/adk/cmd/launcher"
|
||||
"google.golang.org/adk/cmd/launcher/full"
|
||||
"google.golang.org/adk/model/gemini"
|
||||
"google.golang.org/adk/tool"
|
||||
"google.golang.org/adk/tool/geminitool"
|
||||
"google.golang.org/adk/tool/mcptoolset"
|
||||
"google.golang.org/genai"
|
||||
)
|
||||
@@ -31,23 +27,12 @@ const (
|
||||
agentLong = "Start agent to automate YouTube workflows"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed INSTRUCTION.md
|
||||
instruction string
|
||||
|
||||
launcherArgs string
|
||||
)
|
||||
var launcherArgs string
|
||||
|
||||
var agentCmd = &cobra.Command{
|
||||
Use: "agent",
|
||||
Short: agentShort,
|
||||
Long: agentLong,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
envInstruction, ok := os.LookupEnv("YUTU_AGENT_INSTRUCTION")
|
||||
if ok && envInstruction != "" {
|
||||
instruction = envInstruction
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if launcherArgs == "" {
|
||||
_ = cmd.Help()
|
||||
@@ -93,22 +78,14 @@ func launch(ctx context.Context, writer io.Writer, args []string) {
|
||||
slog.ErrorContext(ctx, "failed to create MCP tool set", "error", err)
|
||||
}
|
||||
|
||||
a, err := llmagent.New(
|
||||
llmagent.Config{
|
||||
Model: m,
|
||||
Name: "YouTube Copilot",
|
||||
Instruction: instruction,
|
||||
Tools: []tool.Tool{geminitool.GoogleSearch{}},
|
||||
Toolsets: []tool.Toolset{mcpToolSet},
|
||||
},
|
||||
)
|
||||
orchestrator, err := buildOrchestrator(m, mcpToolSet)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to create LLM agent", "error", err)
|
||||
slog.ErrorContext(ctx, "failed to build orchestrator agent", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config := &launcher.Config{
|
||||
AgentLoader: agent.NewSingleLoader(a),
|
||||
AgentLoader: agent.NewSingleLoader(orchestrator),
|
||||
}
|
||||
l := full.NewLauncher()
|
||||
if err := l.Execute(ctx, config, args); err != nil {
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
// Copyright 2026 eat-pray-ai & OpenWaygate
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"google.golang.org/adk/agent"
|
||||
"google.golang.org/adk/agent/llmagent"
|
||||
"google.golang.org/adk/model"
|
||||
"google.golang.org/adk/tool"
|
||||
"google.golang.org/adk/tool/agenttool"
|
||||
"google.golang.org/adk/tool/geminitool"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed INSTRUCTION_ORCHESTRATOR.md
|
||||
orchestratorInstruction string
|
||||
|
||||
//go:embed INSTRUCTION_RETRIEVAL.md
|
||||
retrievalInstruction string
|
||||
|
||||
//go:embed INSTRUCTION_MODIFIER.md
|
||||
modifierInstruction string
|
||||
|
||||
//go:embed INSTRUCTION_DESTROYER.md
|
||||
destroyerInstruction string
|
||||
)
|
||||
|
||||
type agentDef struct {
|
||||
name string
|
||||
description string
|
||||
instruction *string
|
||||
envKey string
|
||||
toolNames []string
|
||||
}
|
||||
|
||||
var agentDefs = map[string]agentDef{
|
||||
"Nina": {
|
||||
name: "YouTube Copilot",
|
||||
description: "Orchestrates YouTube workflows by delegating to specialized agents.",
|
||||
instruction: &orchestratorInstruction,
|
||||
envKey: "YUTU_AGENT_INSTRUCTION",
|
||||
},
|
||||
"Aagje": {
|
||||
name: "Retrieval",
|
||||
description: "Retrieves information from YouTube and the internet, such as listing videos, channels, playlists, comments, and more.",
|
||||
instruction: &retrievalInstruction,
|
||||
envKey: "YUTU_RETRIEVAL_INSTRUCTION",
|
||||
toolNames: []string{
|
||||
"activity-list",
|
||||
"caption-download",
|
||||
"caption-list",
|
||||
"channel-list",
|
||||
"channelSection-list",
|
||||
"comment-list",
|
||||
"commentThread-list",
|
||||
"member-list",
|
||||
"membershipsLevel-list",
|
||||
"playlist-list",
|
||||
"playlistImage-list",
|
||||
"playlistItem-list",
|
||||
"search-list",
|
||||
"subscription-list",
|
||||
"superChatEvent-list",
|
||||
"video-getRating",
|
||||
"video-list",
|
||||
"videoAbuseReportReason-list",
|
||||
},
|
||||
},
|
||||
"Knorretje": {
|
||||
name: "Modifier",
|
||||
description: "Creates and updates YouTube content, such as uploading videos, creating playlists, updating metadata, posting comments, and more.",
|
||||
instruction: &modifierInstruction,
|
||||
envKey: "YUTU_MODIFIER_INSTRUCTION",
|
||||
toolNames: []string{
|
||||
"caption-insert",
|
||||
"caption-update",
|
||||
"channel-update",
|
||||
"channelBanner-insert",
|
||||
"comment-insert",
|
||||
"comment-markAsSpam",
|
||||
"comment-setModerationStatus",
|
||||
"comment-update",
|
||||
"commentThread-insert",
|
||||
"playlist-insert",
|
||||
"playlist-update",
|
||||
"playlistImage-insert",
|
||||
"playlistImage-update",
|
||||
"playlistItem-insert",
|
||||
"playlistItem-update",
|
||||
"subscription-insert",
|
||||
"thumbnail-set",
|
||||
"video-insert",
|
||||
"video-rate",
|
||||
"video-reportAbuse",
|
||||
"video-update",
|
||||
"watermark-set",
|
||||
},
|
||||
},
|
||||
"Daan": {
|
||||
name: "Destroyer",
|
||||
description: "Deletes YouTube content, such as deleting videos, playlists, comments, captions, or subscriptions. Handles destructive operations that require extra caution.",
|
||||
instruction: &destroyerInstruction,
|
||||
envKey: "YUTU_DESTROYER_INSTRUCTION",
|
||||
toolNames: []string{
|
||||
"caption-delete",
|
||||
"channelSection-delete",
|
||||
"comment-delete",
|
||||
"playlist-delete",
|
||||
"playlistImage-delete",
|
||||
"playlistItem-delete",
|
||||
"subscription-delete",
|
||||
"video-delete",
|
||||
"watermark-unset",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, def := range agentDefs {
|
||||
if v, ok := os.LookupEnv(def.envKey); ok && v != "" {
|
||||
*def.instruction = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildOrchestrator(m model.LLM, mcpToolSet tool.Toolset) (
|
||||
agent.Agent, error,
|
||||
) {
|
||||
def := agentDefs["Aagje"]
|
||||
retrieval, err := llmagent.New(
|
||||
llmagent.Config{
|
||||
Name: def.name,
|
||||
Model: m,
|
||||
Description: def.description,
|
||||
Instruction: *def.instruction,
|
||||
Tools: []tool.Tool{geminitool.GoogleSearch{}},
|
||||
Toolsets: []tool.Toolset{
|
||||
tool.FilterToolset(mcpToolSet, tool.StringPredicate(def.toolNames)),
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create %s agent: %w", def.name, err)
|
||||
}
|
||||
|
||||
retrievalTool := agenttool.New(
|
||||
retrieval, &agenttool.Config{SkipSummarization: true},
|
||||
)
|
||||
|
||||
agents := make(map[string]agent.Agent, 2)
|
||||
for _, key := range []string{"Knorretje", "Daan"} {
|
||||
def = agentDefs[key]
|
||||
a, err := llmagent.New(
|
||||
llmagent.Config{
|
||||
Name: def.name,
|
||||
Model: m,
|
||||
Description: def.description,
|
||||
Instruction: *def.instruction,
|
||||
Tools: []tool.Tool{retrievalTool},
|
||||
Toolsets: []tool.Toolset{
|
||||
tool.FilterToolset(mcpToolSet, tool.StringPredicate(def.toolNames)),
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create %s agent: %w", def.name, err)
|
||||
}
|
||||
agents[key] = a
|
||||
}
|
||||
|
||||
oDef := agentDefs["Nina"]
|
||||
orchestrator, err := llmagent.New(
|
||||
llmagent.Config{
|
||||
Name: oDef.name,
|
||||
Model: m,
|
||||
Description: oDef.description,
|
||||
Instruction: *oDef.instruction,
|
||||
Tools: []tool.Tool{retrievalTool},
|
||||
SubAgents: []agent.Agent{agents["Knorretje"], agents["Daan"]},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create %s agent: %w", oDef.name, err)
|
||||
}
|
||||
|
||||
return orchestrator, nil
|
||||
}
|
||||
Reference in new issue
Block a user