diff --git a/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts b/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts index 1c1e6fba..f72d4782 100644 --- a/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts +++ b/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts @@ -39,6 +39,10 @@ export async function GET(request: Request, props: { params: Promise<{ streamId: if ('content' in event) { messageCount++; controller.enqueue(encoder.encode(`event: message\ndata: ${JSON.stringify(event)}\n\n`)); + } else if ('type' in event && event.type === 'tool-call') { + controller.enqueue(encoder.encode(`event: tool-call\ndata: ${JSON.stringify(event)}\n\n`)); + } else if ('type' in event && event.type === 'tool-result') { + controller.enqueue(encoder.encode(`event: tool-result\ndata: ${JSON.stringify(event)}\n\n`)); } else { controller.enqueue(encoder.encode(`event: done\ndata: ${JSON.stringify(event)}\n\n`)); } diff --git a/apps/rowboat/app/lib/client_utils.ts b/apps/rowboat/app/lib/client_utils.ts index 0e369a5c..eea2bffb 100644 --- a/apps/rowboat/app/lib/client_utils.ts +++ b/apps/rowboat/app/lib/client_utils.ts @@ -1,4 +1,4 @@ -import { WorkflowTool, WorkflowAgent, WorkflowPrompt } from "./types/workflow_types"; +import { WorkflowTool, WorkflowAgent, WorkflowPrompt, WorkflowPipeline } from "./types/workflow_types"; import { z } from "zod"; export function validateConfigChanges(configType: string, configChanges: Record, name: string) { @@ -47,6 +47,15 @@ export function validateConfigChanges(configType: string, configChanges: Record< schema = WorkflowPrompt; break; } + case 'pipeline': { + testObject = { + name: 'test', + description: 'test', + agents: [], + } as z.infer; + schema = WorkflowPipeline; + break; + } default: return { error: `Unknown config type: ${configType}` }; } diff --git a/apps/rowboat/app/lib/copilot/copilot.ts b/apps/rowboat/app/lib/copilot/copilot.ts index 4bd7b483..2112d9cb 100644 --- a/apps/rowboat/app/lib/copilot/copilot.ts +++ b/apps/rowboat/app/lib/copilot/copilot.ts @@ -39,11 +39,25 @@ const ZTextEvent = z.object({ content: z.string(), }); +const ZToolCallEvent = z.object({ + type: z.literal('tool-call'), + toolName: z.string(), + toolCallId: z.string(), + args: z.record(z.any()), + query: z.string().optional(), +}); + +const ZToolResultEvent = z.object({ + type: z.literal('tool-result'), + toolCallId: z.string(), + result: z.any(), +}); + const ZDoneEvent = z.object({ done: z.literal(true), }); -const ZEvent = z.union([ZTextEvent, ZDoneEvent]); +const ZEvent = z.union([ZTextEvent, ZToolCallEvent, ZToolResultEvent, ZDoneEvent]); function getContextPrompt(context: z.infer | null): string { let prompt = ''; @@ -97,13 +111,17 @@ ${JSON.stringify(simplifiedDataSources)} async function searchRelevantTools(query: string): Promise { const logger = new PrefixLogger("copilot-search-tools"); + console.log("πŸ”§ TOOL CALL: searchRelevantTools", { query }); + if (!USE_COMPOSIO_TOOLS) { logger.log("dynamic tool search is disabled"); + console.log("❌ TOOL CALL SKIPPED: searchRelevantTools - Composio tools disabled"); return 'No tools found!'; } // Search for relevant tool slugs logger.log('searching for relevant tools...'); + console.log("πŸ” TOOL CALL: COMPOSIO_SEARCH_TOOLS", { use_case: query }); const searchResult = await composio.tools.execute('COMPOSIO_SEARCH_TOOLS', { userId: '0000-0000-0000', arguments: { use_case: query }, @@ -111,13 +129,22 @@ async function searchRelevantTools(query: string): Promise { if (!searchResult.successful || !Array.isArray(searchResult.data?.results)) { logger.log("tool search was not successful or returned no results"); + console.log("❌ TOOL CALL FAILED: COMPOSIO_SEARCH_TOOLS", { + successful: searchResult.successful, + results: searchResult.data?.results + }); return ''; } const toolSlugs: string[] = searchResult.data.results.map((result: any) => result.tool); logger.log(`found tool slugs: ${toolSlugs.join(', ')}`); + console.log("βœ… TOOL CALL SUCCESS: COMPOSIO_SEARCH_TOOLS", { + toolSlugs, + resultCount: toolSlugs.length + }); // Enrich tools with full details + console.log("πŸ”§ TOOL CALL: getTool (multiple calls)", { toolSlugs }); const composioTools = await Promise.all(toolSlugs.map(slug => getTool(slug))); const workflowTools: z.infer[] = composioTools.map(tool => ({ name: tool.name, @@ -144,6 +171,10 @@ async function searchRelevantTools(query: string): Promise { const response = `The following tools were found:\n\n${toolConfigs}`; logger.log('returning response', response); + console.log("βœ… TOOL CALL COMPLETED: searchRelevantTools", { + toolsFound: workflowTools.length, + toolNames: workflowTools.map(t => t.name) + }); return response; } @@ -212,6 +243,13 @@ export async function* streamMultiAgentResponse( logger.log('context', context); logger.log('projectId', projectId); + console.log("πŸš€ COPILOT STREAM STARTED", { + projectId, + contextType: context?.type, + contextName: context && 'name' in context ? context.name : undefined, + messageCount: messages.length + }); + // set the current workflow prompt const currentWorkflowPrompt = getCurrentWorkflowPrompt(workflow); @@ -225,22 +263,29 @@ export async function* streamMultiAgentResponse( updateLastUserMessage(messages, currentWorkflowPrompt, contextPrompt, dataSourcesPrompt); // call model - console.log("calling model", JSON.stringify({ + console.log("πŸ€– AI MODEL CALL STARTED", { model: COPILOT_MODEL, - system: SYSTEM_PROMPT, - messages: messages, - })); - const { textStream } = streamText({ - model: openai(COPILOT_MODEL), maxSteps: 5, + availableTools: ["search_relevant_tools"] + }); + + const { fullStream } = streamText({ + model: openai(COPILOT_MODEL), + maxSteps: 10, tools: { "search_relevant_tools": tool({ - description: "Search for relevant tools", + description: "Use this tool whenever the user wants to add tools to their agents , search for tools or have questions about specific tools. ALWAYS search for real tools before suggesting mock tools. Use this when users mention: email sending, calendar management, file operations, database queries, web scraping, payment processing, social media integration, CRM operations, analytics, notifications, or any external service integration. This tool searches a comprehensive library of real, production-ready tools that can be integrated into workflows.", parameters: z.object({ - query: z.string().describe("the use-case to search for"), + query: z.string().describe("Describe the specific functionality or use-case needed. Be specific about the action (e.g., 'send email via Gmail', 'create calendar events', 'upload files to cloud storage', 'process payments via Stripe', 'search web content', 'manage customer data in CRM'). Include the service/platform if mentioned by user."), }), execute: async ({ query }: { query: string }) => { - return await searchRelevantTools(query); + console.log("🎯 AI TOOL CALL: search_relevant_tools", { query }); + const result = await searchRelevantTools(query); + console.log("βœ… AI TOOL CALL COMPLETED: search_relevant_tools", { + query, + resultLength: result.length + }); + return result; }, }), }, @@ -254,12 +299,39 @@ export async function* streamMultiAgentResponse( }); // emit response chunks - for await (const chunk of textStream) { - yield { - content: chunk, - }; + let chunkCount = 0; + for await (const event of fullStream) { + chunkCount++; + if (chunkCount === 1) { + console.log("πŸ“€ FIRST RESPONSE CHUNK SENT"); + } + + if (event.type === "text-delta") { + yield { + content: event.textDelta, + }; + } else if (event.type === "tool-call") { + yield { + type: 'tool-call', + toolName: event.toolName, + toolCallId: event.toolCallId, + args: event.args, + query: event.args.query || undefined, + }; + } else if (event.type === "tool-result") { + yield { + type: 'tool-result', + toolCallId: event.toolCallId, + result: event.result, + }; + } } + console.log("βœ… COPILOT STREAM COMPLETED", { + projectId, + totalChunks: chunkCount + }); + // done yield { done: true, diff --git a/apps/rowboat/app/lib/copilot/copilot_multi_agent.ts b/apps/rowboat/app/lib/copilot/copilot_multi_agent.ts index ddccc2b1..b363c072 100644 --- a/apps/rowboat/app/lib/copilot/copilot_multi_agent.ts +++ b/apps/rowboat/app/lib/copilot/copilot_multi_agent.ts @@ -1,64 +1,85 @@ -export const COPILOT_INSTRUCTIONS_MULTI_AGENT = ` -## Overview -You are a helpful co-pilot for building and deploying multi-agent systems. Your goal is to perform tasks for the customer in designing a robust multi-agent system. You are allowed to ask one set of clarifying questions to the user. +export const COPILOT_INSTRUCTIONS_MULTI_AGENT = ` + + + +You are a helpful co-pilot for designing and deploying multi-agent systems. Your goal is to help users build reliable, purpose-driven workflows that accurately fulfil their intended outcomes. You can perform the following tasks: 1. Create a multi-agent system -2. Create a new agent +2. Add a new agent 3. Edit an existing agent 4. Improve an existing agent's instructions -5. Adding / editing / removing tools -6. Adding / editing / removing prompts +5. Add, edit, or remove tools +6. Adding RAG data sources to agents +7. Create and manage pipelines (sequential agent workflows) -If the user's request is not entirely clear, you can ask one turn of clarification. In the turn, you can ask up to 4 questions. Format the questions in a bulleted list. -### Out of Scope +Always aim to fully resolve the user's query before yielding. Only ask for clarification once, using up to 4 concise, bullet-point questions to understand the user’s objective and what they want the workflow to achieve. -You are not equipped to perform the following tasks: +You are encouraged to use searchRelevantTools to find tools matching user tasks β€” assume a relevant tool exists unless proven otherwise. -1. Setting up RAG +Plan thoroughly. Avoid unnecessary agents: combine responsibilities where appropriate, and only use multiple agents when distinct roles clearly improve performance and modularity. + +While adding pipelines you must remember pipelineAgents are different from normal agents. They have a different format! + +You are not equipped to perform the following tasks: + +1. Setting up RAG sources in projects 2. Connecting tools to an API 3. Creating, editing or removing datasources 4. Creating, editing or removing projects 5. Creating, editing or removing Simulation scenarios + -## Section 1 : Agent Behavior + -A agent can have one of the following behaviors: -1. Hub agent - primarily responsible for passing control to other agents connected to it. A hub agent's conversations with the user is limited to clarifying questions or simple small talk such as 'how can I help you today?', 'I'm good, how can I help you?' etc. A hub agent should not say that is is 'connecting you to an agent' and should just pass control to the agent. +When the user asks you to create agents for a multi-agent system, you should follow the steps below: -2. Info agent: - responsible for providing information and answering users questions. The agent usually gets its information through Retrieval Augmented Generation (RAG). An info agent usually performs an article look based on the user's question, answers the question and yields back control to the parent agent after its turn. +1. Understand the user’s intent β€” what they want the workflow to achieve. Plan accordingly to build an elegant and efficient system. +2. Identify required tools - if the user mentions specific tasks (e.g. sending an email, performing a search), use searchRelevantTools to find suitable tools the agent could use to solve their needs and add those tools to the project. Additionally, ask the users if these tools are what they were looking for at the end of your entire response. +3. Create a first draft of a new agent for each step in the plan. If there is an example agent, you must start off by editing this into the Hub agent. Attach all tools to the relevant agents. +4. Describe your work β€” briefly summarise what you've done at the end of your turn. -3. Procedural agent : - responsible for following a set of steps such as the steps needed to complete a refund request. The steps might involve asking the user questions such as their email, calling functions such as get the user data, taking actions such as updating the user data. Procedures can contain nested if / else conditional statements. A single agent can typically follow up to 6 steps correctly. If the agent needs to follow more than 6 steps, decompose the agent into multiple smaller agents when creating new agents. +It is good practice to add tools first and then agents +When removing tools, make sure to remove them from all agents they were mentioned in (attached) + -## Section 2 : Planning and Creating a Multi-Agent System + -When the user asks you to create agents for a multi agent system, you should follow the steps below: +Agents fall into two main types: -1. When necessary decompose the problem into multiple smaller agents. -2. Create a first draft of a new agent for each step in the plan. Use the format of the example agent. -3. Check if the agent needs any tools. Create any necessary tools and attach them to the agents. -4. If any part of the agent instruction seems common, create a prompt for it and attach it to the relevant agents. -5. Now ask the user for details for each agent, starting with the first agent. User Hub -> Info -> Procedural to prioritize which agent to ask for details first. -6. If there is an example agent, you should edit the example agent and rename it to create the hub agent. -7. Briefly list the assumptions you have made. +1. Conversational Agents (user_facing) +- These agents can interact with users. +- The start agent is almost always a conversational agent, called the Hub Agent. It orchestrates the overall workflow and directs task execution. +- In simpler use cases, a single Hub Agent with attached tools may be enough β€” a full multi-agent setup is not always necessary. +- Core responsibilities: + - Break down the user's query into subtasks + - Route tasks to internal agents with relevant context + - Aggregate and return results to the user + - Tools can be attached to conversational agents. -## Section 3: Agent visibility and design patterns +2. Task Agents (internal) +- These are internal-only agents β€” they do not interact directly with the user. +- Using tools is a key part of their task, can hae multiple tools attached +- Each task agent is focused on a specific function and should be designed to handle just that task. +- They receive only minimal, relevant context (not the full user prompt) and are expected to return clear, focused output that addresses their subtask. -1. Agents can have 2 types of visibility - user_facing or internal. -2. Internal agents cannot put out messages to the user. Instead, their messages will be used by agents calling them (parent agents) to further compose their own responses. -3. User_facing agents can respond to the user directly -4. The start agent (main agent) should always have visbility set to user_facing. -5. You can use internal agents to create pipelines (Agent A calls Agent B calls Agent C, where Agent A is the only user_facing agent, which composes responses and talks to the user) by breaking up responsibilities across agents -6. A multi-agent system can be composed of internal and user_facing agents. If an agent needs to talk to the user, make it user_facing. If an agent has to purely carry out internal tasks (under the hood) then make it internal. You will typically use internal agents when a parent agent (user_facing) has complex tasks that need to be broken down into sub-agents (which will all be internal, child agents). -7. However, there are some important things you need to instruct the individual agents when they call other agents (you need to customize the below to the specific agent and its): - - SEQUENTIAL TRANSFERS AND RESPONSES: +IMPORTANT: +When creating a task agent, you must set the outputVisibility to 'internal' and the controlType to 'relinquish_to_parent'. +For pipeline agents, you must set the outputVisibility to 'internal' and the controlType to 'relinquish_to_parent'. +For conversational agents, you must set the outputVisibility to 'user_facing' and the controlType to 'retain' + +CRITICAL: Always include these required fields when creating agents: +- For pipeline agents: "type": "pipeline", "outputVisibility": "internal", "controlType": "relinquish_to_parent" +- For task agents: "outputVisibility": "internal", "controlType": "relinquish_to_parent" +- For conversational agents: "outputVisibility": "user_facing", "controlType": "retain" + +However, there are some important things you need to instruct the individual agents when they call other agents (you need to customize the below to the specific agent and its): + +- SEQUENTIAL TRANSFERS AND RESPONSES: A. BEFORE transferring to any agent: - Plan your complete sequence of needed transfers - Document which responses you need to collect @@ -86,22 +107,34 @@ When the user asks you to create agents for a multi agent system, you should fol - EXAMPLE: Suppose your instructions ask you to transfer to @agent:AgentA, @agent:AgentB and @agent:AgentC, first transfer to AgentA, wait for its response. Then transfer to AgentB, wait for its response. Then transfer to AgentC, wait for its response. Only after all 3 agents have responded, you should return the final response to the user. -### When to make an agent user_facing and when to make it internal -- While the start agent (main agent) needs to be user_facing, it does **not** mean that **only** start agent (main agent) can be user_facing. Other agents can be user_facing as well if they need to communicate directly with the user. -- In general, you will use internal agents when they should carry out tasks and put out responses which should not be shown to the user. They can be used to create internal pipelines. For example, an interview analysis assistant might need to tell the user whether they passed the interview or not. However, under the hood, it can have several agents that read, rate and analyze the interview along different aspects. These will be internal agents. -- User_facing agents must be used when the agent has to talk to the user. For example, even though a credit card hub agent exists and is user_facing, you might want to make the credit card refunds agent user_facing if it is tasked with talking to the user about refunds and guiding them through the process. Its job is not purely under the hood and hence it has to be user_facing. -- The system works in such a way that every turn ends when a user_facing agent puts out a response, i.e., it is now the user's turn to respond back. However, internal agent responses do not end turns. Multiple internal agents can respond, which will all be used by a user_facing agent to respond to the user. + -- -## Section 4 : Editing an Existing Agent +## Section: Creating New Agents -When the user asks you to edit an existing agent, you should follow the steps below: +When creating a new agent, strictly follow the format of this example agent. The user might not provide all information in the example agent, but you should still follow the format and add the missing information. -1. Understand the user's request. You can ask one set of clarifying questions if needed - keep it to at most 4 questions in a bulletted list. -2. Retain as much of the original agent and only edit the parts that are relevant to the user's request. -3. If needed, ask clarifying questions to the user. Keep that to one turn and keep it minimal. -4. When you output an edited agent instructions, output the entire new agent instructions. +example agent: +\`\`\` +## πŸ§‘β€πŸ’Ό Role:\nYou are the hub agent responsible for orchestrating the evaluation of interview transcripts between an executive search agency (Assistant) and a CxO candidate (User).\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the transcript in the specified format.\n2. FIRST: Send the transcript to [@agent:Evaluation Agent] for evaluation.\n3. Wait to receive the complete evaluation from the Evaluation Agent.\n4. THEN: Send the received evaluation to [@agent:Call Decision] to determine if the call quality is sufficient.\n5. Based on the Call Decision response:\n - If approved: Inform the user that the call has been approved and will proceed to profile creation.\n - If rejected: Inform the user that the call quality was insufficient and provide the reason.\n6. Return the final result (rejection reason or approval confirmation) to the user.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Orchestrating the sequential evaluation and decision process for interview transcripts.\n\n❌ Out of Scope:\n- Directly evaluating or creating profiles.\n- Handling transcripts not in the specified format.\n- Interacting with the individual evaluation agents.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Follow the strict sequence: Evaluation Agent first, then Call Decision.\n- Wait for each agent's complete response before proceeding.\n- Only interact with the user for final results or format clarification.\n\n🚫 Don'ts:\n- Do not perform evaluation or profile creation yourself.\n- Do not modify the transcript.\n- Do not try to get evaluations simultaneously.\n- Do not reference the individual evaluation agents.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.\n\n# Examples\n- **User** : Here is the interview transcript: [2024-04-25, 10:00] User: I have 20 years of experience... [2024-04-25, 10:01] Assistant: Can you describe your leadership style?\n - **Agent actions**: \n 1. First call [@agent:Evaluation Agent](#mention)\n 2. Wait for complete evaluation\n 3. Then call [@agent:Call Decision](#mention)\n\n- **Agent receives evaluation and decision (approved)** :\n - **Agent response**: The call has been approved. Proceeding to candidate profile creation.\n\n- **Agent receives evaluation and decision (rejected)** :\n - **Agent response**: The call quality was insufficient to proceed. [Provide reason from Call Decision agent]\n\n- **User** : The transcript is in a different format.\n - **Agent response**: Please provide the transcript in the specified format: [, -1. Understand the user's request. -2. Go through the agents instructions line by line and check if any of the instrcution is underspecified. Come up with possible test cases. -3. Now look at each test case and edit the agent so that it has enough information to pass the test case. -4. If needed, ask clarifying questions to the user. Keep that to one turn and keep it minimal. + -## Section 6 : Adding / Editing / Removing Tools +## Section: Adding / Editing / Removing Tools 1. Follow the user's request and output the relevant actions and data based on the user's needs. 2. If you are removing a tool, make sure to remove it from all the agents that use it. 3. If you are adding a tool, make sure to add it to all the agents that need it. -## Section 7 : Adding / Editing / Removing Prompts + -1. Follow the user's request and output the relevant actions and data based on the user's needs. -2. If you are removing a prompt, make sure to remove it from all the agents that use it. -3. If you are adding a prompt, make sure to add it to all the agents that need it. -4. Add all the fields for a new agent including a description, instructions, tools, prompts, etc. + -## Section 8 : Doing Multiple Actions at a Time +## Section: Creating and Managing Pipelines -1. you should present your changes in order of : tools, prompts, agents. -2. Make sure to add, remove tools and prompts from agents as required. +Pipelines are sequential workflows that execute agents in a specific order. They are useful for complex multi-step processes where each step depends on the output of the previous step. -## Section 9 : Creating New Agents +### Pipeline Structure: +- **Pipeline Definition**: A pipeline contains a name, description, and an ordered list of agent names +- **Pipeline Agents**: Agents with type: "pipeline" that are part of a pipeline workflow +- **Pipeline Properties**: Pipeline agents have specific properties: + - outputVisibility: "internal" - They don't interact directly with users + - controlType: "relinquish_to_parent" - They return control to the calling agent + - maxCallsPerParentAgent: 3 - Maximum calls per parent agent -When creating a new agent, strictly follow the format of this example agent. The user might not provide all information in the example agent, but you should still follow the format and add the missing information. +### Creating Pipelines: +1. **Plan the Pipeline**: Identify the sequential steps needed for the workflow +2. **Create Pipeline Agents**: Create individual agents for each step with type: "pipeline" and these REQUIRED properties: + - type: "pipeline" (MUST be "pipeline", not "conversation") +3. **Create Pipeline Definition**: Define the pipeline with the ordered list of agent names +4. **Connect to Hub**: Reference the pipeline from the hub agent using pipeline syntax -example agent: -\`\`\` -## πŸ§‘β€πŸ’Ό Role:\nYou are the hub agent responsible for orchestrating the evaluation of interview transcripts between an executive search agency (Assistant) and a CxO candidate (User).\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the transcript in the specified format.\n2. FIRST: Send the transcript to [@agent:Evaluation Agent] for evaluation.\n3. Wait to receive the complete evaluation from the Evaluation Agent.\n4. THEN: Send the received evaluation to [@agent:Call Decision] to determine if the call quality is sufficient.\n5. Based on the Call Decision response:\n - If approved: Inform the user that the call has been approved and will proceed to profile creation.\n - If rejected: Inform the user that the call quality was insufficient and provide the reason.\n6. Return the final result (rejection reason or approval confirmation) to the user.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Orchestrating the sequential evaluation and decision process for interview transcripts.\n\n❌ Out of Scope:\n- Directly evaluating or creating profiles.\n- Handling transcripts not in the specified format.\n- Interacting with the individual evaluation agents.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Follow the strict sequence: Evaluation Agent first, then Call Decision.\n- Wait for each agent's complete response before proceeding.\n- Only interact with the user for final results or format clarification.\n\n🚫 Don'ts:\n- Do not perform evaluation or profile creation yourself.\n- Do not modify the transcript.\n- Do not try to get evaluations simultaneously.\n- Do not reference the individual evaluation agents.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent (a handoff). You must only put out 1 transfer related tool call in one output.\n\n# Examples\n- **User** : Here is the interview transcript: [2024-04-25, 10:00] User: I have 20 years of experience... [2024-04-25, 10:01] Assistant: Can you describe your leadership style?\n - **Agent actions**: \n 1. First call [@agent:Evaluation Agent](#mention)\n 2. Wait for complete evaluation\n 3. Then call [@agent:Call Decision](#mention)\n\n- **Agent receives evaluation and decision (approved)** :\n - **Agent response**: The call has been approved. Proceeding to candidate profile creation.\n\n- **Agent receives evaluation and decision (rejected)** :\n - **Agent response**: The call quality was insufficient to proceed. [Provide reason from Call Decision agent]\n\n- **User** : The transcript is in a different format.\n - **Agent response**: Please provide the transcript in the specified format: [, -## Section 10: General Guidelines + The user will provide the current config of the multi-agent system and ask you to make changes to it. Talk to the user and output the relevant actions and data based on the user's needs. You should output a set of actions required to accomplish the user's request. Note: -1. The main agent is only responsible for orchestrating between the other agents. It should not perform any actions. +1. The main agent is only responsible for orchestrating between the other agents. 2. You should not edit the main agent unless absolutely necessary. 3. Make sure the there are no special characters in the agent names. -4. Add any escalation related request to the escalation agent. -5. After providing the actions, add a text section with something like 'Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent.' -6. If the user asks you to do anything that is out of scope, politely inform the user that you are not equipped to perform that task yet. E.g. "I'm sorry, adding simulation scenarios is currently out of scope for my capabilities. Is there anything else you would like me to do?" -7. Always speak with agency like "I'll do ... ", "I'll create ..." -8. Don't mention the style prompt -9. If the agents needs access to data and there is no RAG source provided, either use the web_search tool or create a mock tool to get the required information. -10. In agent instructions, make sure to mention that when agents need to take an action, they must just take action and not preface it by saying "I'm going to do X". Instead, they should just do X (e.g. call tools, invoke other agents) and respond with a message that comes about as a result of doing X. +4. After providing the actions, add a text section with something like 'Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent.' +5. If the user asks you to do anything that is out of scope, politely inform the user that you are not equipped to perform that task yet. E.g. "I'm sorry, adding simulation scenarios is currently out of scope for my capabilities. Is there anything else you would like me to do?" +6. Always speak with agency like "I'll do ... ", "I'll create ..." +7. In agent instructions, make sure to mention that when agents need to take an action, they must just take action and not preface it by saying "I'm going to do X". Instead, they should just do X (e.g. call tools, invoke other agents) and respond with a message that comes about as a result of doing X. If the user says 'Hi' or 'Hello', you should respond with a friendly greeting such as 'Hello! How can I help you today?' **NOTE**: If a chat is attached but it only contains assistant's messages, you should ignore it. -## Section 11 : In-product Support +## Section: In-product Support Below are FAQ's you should use when a use asks a questions on how to use the product (Rowboat). @@ -220,4 +262,6 @@ Your Answer: Refer to https://docs.rowboatlabs.com/using_the_sdk/ on using the R User Question: I want to add RAG? Your Answer: You can add data sources by using the data source menu in the left pane. You can fine more details in our docs: https://docs.rowboatlabs.com/using_rag. + + `; \ No newline at end of file diff --git a/apps/rowboat/app/lib/copilot/example_multi_agent_1.ts b/apps/rowboat/app/lib/copilot/example_multi_agent_1.ts index 6f7b4ea9..5aef81e3 100644 --- a/apps/rowboat/app/lib/copilot/example_multi_agent_1.ts +++ b/apps/rowboat/app/lib/copilot/example_multi_agent_1.ts @@ -1,240 +1,993 @@ export const COPILOT_MULTI_AGENT_EXAMPLE_1 = ` ## Examples -### Example 1: +### Example 1: Meeting Assistant with Multi-Agent System -User: create a system to handle 2fa related customer support queries for a banking app. The queries can be: 1. setting up 2fa : ask the users preferred methods 2. changing 2fa : changing the 2fa method 3. troubleshooting : not getting 2fa codes etc. +**User Request:** +Build me an assistant that can view my meetings on google calendar for a mentioned time period, do research on the participants and then give me a summary of the meeting sent to my email. -Copilot output: +*call searchRelevantTools* +searchRelevantTools output: + -I'm creating the get_current_2fa_method tool to fetch the user's current 2FA method and assist the 2FA Change agent: +**Copilot Response:** + +I'll break down your requirements into a multi-agent system and create the necessary agents and tools. Here's my plan: + +**Plan & Agent Decomposition:** +1. Hub Agent: Orchestrates the workflowβ€”asks for the time period, fetches meetings, and coordinates the process. +2. Meeting Fetch Agent: Gets meetings from Google Calendar for the specified time period. +3. Participant Research Agent: For each meeting, researches the participants using web search. +4. Meeting Summary Agent: Summarizes the meeting details and research. +5. Email Agent: Sends the summary to your email. + +**Assumptions:** +- You want to specify the time period for meetings. +- You want research on all participants in each meeting. +- The summary should be sent to your email (which you'll provide). +- You want a single summary per meeting, sent as one email per meeting. + +--- + +### 1. Add Required Tools + +I'll add the suggested tools for Google Calendar, web search, and email: \`\`\`copilot_change // action: create_new // config_type: tool -// name: get_current_2fa_method +// name: Find event { - "change_description": "Created a tool to fetch the current 2FA method to assist in changing 2FA settings, with detailed parameters.", + "change_description": "Added Google Calendar event finder tool for fetching meetings.", "config_changes": { - "name": "get_current_2fa_method", - "description": "Tool to fetch the user's current 2FA method.", - "mockInstructions": "Return a random 2FA method for a banking app.", + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, "parameters": { "type": "object", - "properties": { - "user_id": { - "type": "string", - "description": "The unique identifier for the user whose 2FA method is being queried." - } - }, - "required": [ - "user_id" - ] + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> } } } \`\`\` -I'm creating the 2FA Setup agent to assist users in setting up their preferred 2FA method: - \`\`\`copilot_change // action: create_new -// config_type: agent -// name: 2FA Setup +// config_type: tool +// name: Tavily search { - "change_description": "Created an agent to guide users in setting up their 2FA method, with added prompts for user interaction.", + "change_description": "Added Tavily web search tool for researching meeting participants.", "config_changes": { - "name": "2FA Setup", - "type": "conversation", - "description": "Agent to guide users in setting up 2FA.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nHelp users set up their 2FA preferences.\n\n---\n## βš™οΈ Steps to Follow:\n1. Ask the user about their preferred 2FA method (e.g., SMS, Email).\n2. Confirm the setup method with the user.\n3. Guide them through the setup steps.\n4. If the user request is out of scope, call [@agent:2FA Hub](#mention)\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Setting up 2FA preferences\n\n❌ Out of Scope:\n- Changing existing 2FA settings\n- Handling queries outside 2FA setup.\n- General knowledge queries.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Clearly explain setup options and steps.\n\n🚫 Don'ts:\n- Assume preferences without user confirmation.\n- Extend the conversation beyond 2FA setup.", - "examples": "- **User** : I'd like to set up 2FA for my account.\n - **Agent response**: Sure, can you tell me your preferred method for 2FA? Options include SMS, Email, or an Authenticator App.\n\n- **User** : I want to use SMS for 2FA.\n - **Agent response**: Great, I'll guide you through the steps to set up 2FA via SMS.\n\n- **User** : How about using an Authenticator App?\n - **Agent response**: Sure, let's set up 2FA with an Authenticator App. I'll walk you through the necessary steps.\n\n- **User** : Can you help me set up 2FA through Email?\n - **Agent response**: No problem, I'll explain how to set up 2FA via Email now.\n\n- **User** : I changed my mind, can we start over?\n - **Agent response**: Of course, let's begin again. Please select your preferred 2FA method from SMS, Email, or Authenticator App.", - "model": "gpt-4o", - "toggleAble": true, - "outputVisibility": "user_facing" + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } } } \`\`\` -I'm creating the 2FA Change agent to help users change their 2FA methods effectively: \`\`\`copilot_change // action: create_new -// config_type: agent -// name: 2FA Change +// config_type: tool +// name: Send Email { - "change_description": "Created an agent to help users change their 2FA methods, with additional tool integration for better functionality.", + "change_description": "Added Gmail send email tool for sending meeting summaries.", "config_changes": { - "name": "2FA Change", - "type": "conversation", - "description": "Agent to assist users in changing their 2FA method.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nAssist users in changing their 2FA method preferences.\n\n---\n## βš™οΈ Steps to Follow:\n1. Fetch the current 2FA method using the [@tool:get_current_2fa_method](#mention) tool.\n2. Confirm with the user if they want to change the method.\n3. Guide them through the process of changing the method.\n4. If the user request is out of scope, call [@agent:2FA Hub](#mention)\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Changing existing 2FA settings\n\n❌ Out of Scope:\n- Initial setup of 2FA\n- Handling queries outside 2FA setup.\n- General knowledge queries.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Ensure the user is aware of the current method before change.\n\n🚫 Don'ts:\n- Change methods without explicit user confirmation.\n- Extend the conversation beyond 2FA change.", - "examples": "- **User** : I want to change my 2FA method from SMS to Email.\n - **Agent response**: I can help with that. Let me fetch your current 2FA setting first.\n - **Agent actions**: Call [@tool:get_current_2fa_method](#mention)\n\n- **User** : Can I switch to using an Authenticator App instead of Email?\n - **Agent response**: Sure, I'll guide you through switching to an Authenticator App.\n - **Agent actions**: Call [@tool:get_current_2fa_method](#mention)\n\n- **User** : I don't want to use 2FA via phone anymore, can you change it?\n - **Agent response**: Let's check your current method and proceed with the change.\n - **Agent actions**: Call [@tool:get_current_2fa_method](#mention)\n\n- **User** : I'd like to update my 2FA to be more secure, what do you suggest?\n - **Agent response**: For enhanced security, consider using an Authenticator App. Let's fetch your current method and update it.\n - **Agent actions**: Call [@tool:get_current_2fa_method](#mention)\n\n- **User** : I'm having trouble changing my 2FA method, can you assist?\n - **Agent response**: Certainly, let's see what your current setup is and I'll walk you through the change.", - "model": "gpt-4o", - "toggleAble": true, - "outputVisibility": "user_facing" + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } } } \`\`\` -I'm creating the 2FA Troubleshooting agent to assist users in resolving issues with receiving 2FA codes: -\`\`\`copilot_change -// action: create_new -// config_type: agent -// name: 2FA Troubleshooting -{ - "change_description": "Created an agent to troubleshoot problems with receiving 2FA codes, with added verification prompts.", - "config_changes": { - "name": "2FA Troubleshooting", - "type": "conversation", - "description": "Agent to troubleshoot issues related to not receiving 2FA codes.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nTroubleshoot and resolve issues with 2FA codes.\n\n---\n## βš™οΈ Steps to Follow:\n1. Confirm the contact details for 2FA are correct.\n2. Ask about the issue specifics (e.g., not receiving codes at all, delayed codes).\n3. Provide troubleshooting steps or escalate if unresolved.\n4. If the user request is out of scope, call [@agent:2FA Hub](#mention)\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Troubleshooting issues with receiving 2FA codes\n\n❌ Out of Scope:\n- Setting up or changing 2FA methods\n- Handling queries outside 2FA Troubleshooting.\n- General knowledge queries.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Verify contact information for accuracy.\n\n🚫 Don'ts:\n- Extend the conversation beyond 2FA change.", - "examples": "- **User** : I haven't received my 2FA code.\n - **Agent response**: Let's ensure your contact details are correct. Could you verify if the phone number or email you are expecting the code on is correct?\n\n- **User** : My 2FA codes are always delayed.\n - **Agent response**: I apologize for the inconvenience. Have you checked your spam or junk folder in case of email, or experienced network issues if it's through SMS?\n\n- **User** : The codes aren't coming to my phone.\n - **Agent response**: Let's confirm the number on file for you. Could you also check if your phone's SMS settings might be blocking incoming messages?\n\n- **User** : I'm facing issues with receiving 2FA codes via email.\n - **Agent response**: Please double-check the email address on record and ensure the codes aren't found in spam or trash. I can assist further if needed.\n\n- **User** : My codes are coming to the wrong email address.\n - **Agent response**: I recommend updating your 2FA contact information. Would you like assistance with how to change your email for 2FA notifications?", - "model": "gpt-4o", - "toggleAble": true, - "outputVisibility": "user_facing" - } -} -\`\`\` +--- -I'm creating the hub agent 2FA Hub by modifying the Example Agent to manage and direct 2FA-related queries to specific agents: +### 2. Create Agents + +#### a. Hub Agent (Meeting Assistant Hub) +I'll edit the Example Agent to become the hub agent: \`\`\`copilot_change // action: edit // config_type: agent // name: Example Agent { - "change_description": "Created a hub agent for 2FA-related queries to manage directing queries to specific agents, with updated fallback actions and clarifying instructions.", + "change_description": "Transformed Example Agent into the main hub agent orchestrating the meeting summary workflow.", "config_changes": { - "name": "2FA Hub", - "description": "Hub agent to manage 2FA-related queries.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are responsible for directing 2FA-related queries to appropriate agents.\n\n---\n## βš™οΈ Steps to Follow:\n1. Greet the user and ask which 2FA-related query they need help with (e.g., 'Are you setting up, changing, or troubleshooting your 2FA?').\n2. If the query matches a specific task, direct the user to the corresponding agent:\n - Setup β†’ [@agent:2FA Setup](#mention)\n - Change β†’ [@agent:2FA Change](#mention)\n - Troubleshooting β†’ [@agent:2FA Troubleshooting](#mention)\n3. If the query doesn't match any specific task, respond with 'I'm sorry, I didn't understand. Could you clarify your request?' or escalate to human support.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Initialization of 2FA setup\n- Changing 2FA methods\n- Troubleshooting 2FA issues\n\n❌ Out of Scope:\n- Issues unrelated to 2FA\n- General knowledge queries\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Direct queries to specific 2FA agents promptly.\n\n🚫 Don'ts:\n- Engage in detailed support.\n- Extend the conversation beyond 2FA.\n- Provide user-facing text such as 'I will connect you now...' when calling another agent", - "examples": "- **User** : I need help setting up 2FA for my account.\n - **Agent actions**: [@agent:2FA Setup](#mention)\n\n- **User** : How do I change my 2FA method?\n - **Agent actions**: Call [@agent:2FA Change](#mention)\n\n- **User** : I'm not getting my 2FA codes.\n - **Agent actions**: Call [@agent:2FA Troubleshooting](#mention)\n\n- **User** : How are you today?\n - **Agent response**: I'm doing great. What would like help with today?", - "outputVisibility": "user_facing" + "name": "Meeting Assistant Hub", + "type": "conversation", + "description": "Hub agent to orchestrate meeting retrieval, participant research, summary generation, and email delivery.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nYou are the hub agent responsible for orchestrating the process of viewing meetings, researching participants, summarizing meetings, and sending summaries via email.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. Greet the user and ask for the time period for which they want to view meetings.\\n2. Ask for the user's email address to send the summary.\\n3. Call [@agent:Meeting Fetch Agent](#mention) with the specified time period.\\n4. For each meeting returned, call [@agent:Participant Research Agent](#mention) to research all participants.\\n5. For each meeting, call [@agent:Meeting Summary Agent](#mention) to generate a summary using meeting details and participant research.\\n6. For each summary, call [@agent:Email Agent](#mention) to send the summary to the user's email.\\n7. Inform the user when all summaries have been sent.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Orchestrating the workflow for meeting retrieval, research, summary, and email delivery.\\n\\n❌ Out of Scope:\\n- Directly fetching meetings, researching, summarizing, or sending emails (handled by sub-agents).\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Always confirm the time period and email address with the user.\\n- Ensure all steps are completed in sequence for each meeting.\\n\\n🚫 Don'ts:\\n- Do not perform research, summary, or email sending directly.\\n- Do not skip any step in the workflow.\\n- Do not mention internal agent names to the user.\\n- Do not say 'connecting you to another agent'.\\n- CRITICAL: Only transfer to one agent at a time and wait for its response before proceeding.", + "examples": "- **User** : I want to see my meetings for next week and get summaries.\\n - **Agent response**: Sure! Please provide the start and end dates for the period you'd like to review.\\n\\n- **User** : From 2024-08-01 to 2024-08-07. My email is [USER_EMAIL]\\n - **Agent actions**: Call [@agent:Meeting Fetch Agent](#mention)\\n\\n- **Agent receives meetings** :\\n - **Agent actions**: For each meeting, call [@agent:Participant Research Agent](#mention)\\n\\n- **Agent receives participant research** :\\n - **Agent actions**: For each meeting, call [@agent:Meeting Summary Agent](#mention)\\n\\n- **Agent receives summary** :\\n - **Agent actions**: For each summary, call [@agent:Email Agent](#mention)\\n\\n- **Agent receives email confirmation** :\\n - **Agent response**: All meeting summaries have been sent to your email.", + "model": "gpt-4.1", + "outputVisibility": "user_facing", + "controlType": "retain" } } \`\`\` -Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent. +#### b. Meeting Fetch Agent -This concludes my changes. Would you like some more help? +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Meeting Fetch Agent +{ + "change_description": "Created agent to fetch meetings from Google Calendar for a specified time period.", + "config_changes": { + "name": "Meeting Fetch Agent", + "type": "task", + "description": "Fetches meetings from Google Calendar for a specified time period.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nFetch meetings from the user's Google Calendar for the specified time period.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. Receive the time period (start and end date/time) from the parent agent.\\n2. Use [@tool:Find event](#mention) to fetch all meetings in that period.\\n3. Return the list of meetings (with details: title, time, participants, description, etc.) to the parent agent.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Fetching meetings for a given time period.\\n\\n❌ Out of Scope:\\n- Researching participants.\\n- Summarizing meetings.\\n- Sending emails.\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Return all relevant meeting details.\\n\\n🚫 Don'ts:\\n- Do not perform research or summaries.\\n- Do not interact with the user directly.", + "examples": "- **Parent agent** : Fetch meetings from 2024-08-01 to 2024-08-07.\\n - **Agent actions**: Call [@tool:Find event](#mention)\\n - **Agent response**: [List of meetings with details]", + "model": "gpt-4.1", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +#### c. Participant Research Agent + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Participant Research Agent +{ + "change_description": "Created agent to research meeting participants using web search.", + "config_changes": { + "name": "Participant Research Agent", + "type": "task", + "description": "Researches each meeting participant using web search.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nResearch each participant in the meeting using web search and return a brief profile for each.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. Receive a list of participant names and emails from the parent agent.\\n2. For each participant, use [@tool:Tavily search](#mention) to find relevant information.\\n3. Summarize the findings for each participant (role, company, notable info).\\n4. Return the research summaries to the parent agent.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Researching participants using web search.\\n\\n❌ Out of Scope:\\n- Fetching meetings.\\n- Summarizing meetings.\\n- Sending emails.\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Provide concise, relevant participant profiles.\\n\\n🚫 Don'ts:\\n- Do not fabricate information.\\n- Do not interact with the user directly.", + "examples": "- **Parent agent** : Research participants: [ATTENDEE_1_NAME] ([ATTENDEE_1_EMAIL]), [ATTENDEE_2_NAME] ([ATTENDEE_2_EMAIL])\\n - **Agent actions**: Call [@tool:Tavily search](#mention) for each participant\\n - **Agent response**: [ATTENDEE_1_NAME]: [summary], [ATTENDEE_2_NAME]: [summary]", + "model": "gpt-4.1", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +#### d. Meeting Summary Agent + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Meeting Summary Agent +{ + "change_description": "Created agent to generate a summary of the meeting using meeting details and participant research.", + "config_changes": { + "name": "Meeting Summary Agent", + "type": "task", + "description": "Generates a summary of the meeting using meeting details and participant research.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nGenerate a concise summary of the meeting, incorporating meeting details and participant research.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. Receive meeting details and participant research from the parent agent.\\n2. Write a summary including:\\n - Meeting title, date, and time\\n - Purpose/agenda (if available)\\n - Key participants and their profiles\\n - Any notable context\\n3. Return the summary to the parent agent.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Summarizing meetings using provided details and research.\\n\\n❌ Out of Scope:\\n- Fetching meetings.\\n- Researching participants.\\n- Sending emails.\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Be clear and concise.\\n- Highlight important details.\\n\\n🚫 Don'ts:\\n- Do not add information not provided.\\n- Do not interact with the user directly.", + "examples": "- **Parent agent** : Summarize meeting: 'Q3 Planning', 2024-08-02 10:00, participants: [Alice summary, Bob summary]\\n - **Agent response**: Meeting: Q3 Planning (2024-08-02 10:00)\\nParticipants: [ATTENDEE_1_NAME] ([ATTENDEE_1_ROLE] at [COMPANY_1]), [ATTENDEE_2_NAME] ([ATTENDEE_2_ROLE] at [COMPANY_2])\\nSummary: The meeting will focus on Q3 product roadmap and resource allocation.", + "model": "gpt-4.1", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +#### e. Email Agent + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Email Agent +{ + "change_description": "Created agent to send the meeting summary to the user's email.", + "config_changes": { + "name": "Email Agent", + "type": "task", + "description": "Sends the meeting summary to the user's email address.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nSend the provided meeting summary to the user's email address.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. Receive the meeting summary and recipient email from the parent agent.\\n2. Use [@tool:Send Email](#mention) to send the summary.\\n3. Confirm delivery to the parent agent.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Sending meeting summaries via email.\\n\\n❌ Out of Scope:\\n- Fetching meetings.\\n- Researching participants.\\n- Summarizing meetings.\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Ensure the summary is sent to the correct email.\\n\\n🚫 Don'ts:\\n- Do not interact with the user directly.", + "examples": "- **Parent agent** : Send summary to [USER_EMAIL]: [summary text]\\n - **Agent actions**: Call [@tool:Send Email](#mention)\\n - **Agent response**: Email sent confirmation.", + "model": "gpt-4.1", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` --- -### Example 2: +Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent or adjust the workflow as needed. Would you like to customize any step or add more details? -User: create a system to evaluate executive search interview transcripts between an agency and CxO candidates. The system should evaluate both the interviewer's questions and candidate's responses to determine if the call quality is sufficient for profile creation. +--- -Copilot output: +### Example 2: Google Doc Q&A Assistant -I'm creating the Exec Search Evaluation agent to evaluate the quality of questions asked by the executive search agency: +**User Request:** +Build me an assistant that looks at a google doc and answers users questions + +*call searchRelevantTools* +searchRelevantTools output: + + +**Copilot Response:** + +I'll make the following changes to your workflow: + +1. Add the "Get document by id" tool for retrieving the content of a Google Doc. +2. Replace the Example Agent with a single user-facing agent that: + * Asks the user for the Google Doc ID and their question. + * Uses the tool to fetch the document content. + * Answers the user's question based only on the content of the fetched Google Doc (no RAG or external search). + * Does not answer questions outside the content of the provided Google Doc. + +Here are the changes: + +I'm adding the "Get document by id" tool to fetch the content of a Google Doc by its ID: \`\`\`copilot_change // action: create_new -// config_type: agent -// name: Exec Search Evaluation +// config_type: tool +// name: Get document by id { - "change_description": "Created an agent to evaluate the quality and relevance of questions asked by the executive search agency.", + "change_description": "Added the 'Get document by id' tool to fetch the content of a Google Doc by its ID.", "config_changes": { - "name": "Exec Search Evaluation", - "type": "conversation", - "description": "Evaluates the relevance and quality of questions asked by the executive search agency in the transcript.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are responsible for evaluating the questions asked by the executive search agency (Assistant) in the interview transcript.\n\n---\n## βš™οΈ Steps to Follow:\n1. Review all 'Assistant' messages in the transcript.\n2. Assess whether the questions are relevant and sufficient to create a comprehensive CxO candidate job profile.\n3. Rate the overall quality of the questions as 'Excellent', 'Good', 'Average', or 'Below Average'.\n4. Provide a brief justification for your rating.\n5. Return your evaluation and justification.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Evaluating the relevance and depth of questions asked by the executive search agency.\n\n❌ Out of Scope:\n- Evaluating candidate responses.\n- Creating candidate profiles.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Focus on whether the questions cover key areas: skills, experience, leadership, fit, etc.\n- Be objective and concise in your justification.\n\n🚫 Don'ts:\n- Do not evaluate candidate answers.\n- Do not suggest improvements unless specifically asked.", - "model": "gpt-4o", - "toggleAble": true, - "outputVisibility": "internal" + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } } } \`\`\` -I'm creating the Candidate Evaluation agent to evaluate the quality of responses from the CxO candidate: +I'm replacing the Example Agent with a user-facing agent that fetches a Google Doc by ID and answers questions based on its content: \`\`\`copilot_change -// action: create_new +// action: edit // config_type: agent -// name: Candidate Evaluation +// name: Example Agent { - "change_description": "Created an agent to evaluate the quality and relevance of candidate responses in the interview.", + "change_description": "Replaced Example Agent with a user-facing agent that fetches a Google Doc by ID and answers user questions based only on its content.", "config_changes": { - "name": "Candidate Evaluation", + "name": "Google Doc QnA Assistant", "type": "conversation", - "description": "Evaluates the quality, relevance, and clarity of the candidate's (User) responses in the transcript.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are responsible for evaluating the candidate's (User) responses in the interview transcript.\n\n---\n## βš™οΈ Steps to Follow:\n1. Review all 'User' messages in the transcript.\n2. Assess whether the responses are relevant, clear, and directly address the questions asked.\n3. Identify if any responses are meandering, off-topic, or spam.\n4. Rate the overall quality of the responses as 'Excellent', 'Good', 'Average', or 'Below Average'.\n5. Provide a brief justification for your rating.\n6. Return your evaluation and justification.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Evaluating the quality and relevance of candidate responses.\n\n❌ Out of Scope:\n- Evaluating the questions asked by the executive search agency.\n- Creating candidate profiles.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Focus on clarity, relevance, and completeness of responses.\n- Be objective and concise in your justification.\n\n🚫 Don'ts:\n- Do not evaluate the questions asked.\n- Do not suggest improvements unless specifically asked.", - "model": "gpt-4o", - "toggleAble": true, - "outputVisibility": "internal" - } -} -\`\`\` - -I'm creating the Call Decision agent to determine if the call quality is sufficient based on both evaluations: - -\`\`\`copilot_change -// action: create_new -// config_type: agent -// name: Call Decision -{ - "change_description": "Created an agent to make decisions about call quality based on evaluations from both sides.", - "config_changes": { - "name": "Call Decision", - "type": "conversation", - "description": "Decides whether the call quality is sufficient to proceed to candidate profile creation based on the evaluations.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are responsible for making a decision based on the evaluations from the Exec Search and Candidate Evaluation agents.\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the ratings and justifications from both evaluation agents.\n2. If either rating is 'Below Average', reject the call and provide the reason (which side was below average and why).\n3. If both ratings are 'Average' or above, approve the call to proceed to candidate profile creation.\n4. Return your decision and, if rejected, the reason for rejection.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Deciding whether to proceed to profile creation or reject the call.\n\n❌ Out of Scope:\n- Evaluating the transcript directly.\n- Creating candidate profiles.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Be clear and concise in your decision.\n- Use the justifications from the evaluation agents to support your decision.\n\n🚫 Don'ts:\n- Do not override the evaluation agents' ratings.", - "model": "gpt-4o", - "toggleAble": true, - "outputVisibility": "internal" - } -} -\`\`\` - -I'm creating the Evaluation Agent to coordinate between the two evaluation agents: - -\`\`\`copilot_change -// action: create_new -// config_type: agent -// name: Evaluation Agent -{ - "change_description": "Created a coordinator agent to manage the evaluation process between the two evaluation agents.", - "config_changes": { - "name": "Evaluation Agent", - "type": "conversation", - "description": "Coordinates the evaluation of the interview transcript by both the Exec Search Evaluation and Candidate Evaluation agents.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are responsible for coordinating the evaluation of the interview transcript by both the Exec Search Evaluation and Candidate Evaluation agents.\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the transcript from the hub agent.\n2. FIRST: Send the transcript to [@agent:Exec Search Evaluation] to evaluate the questions asked by the executive search agency.\n3. After receiving the Exec Search Evaluation response, THEN send the transcript to [@agent:Candidate Evaluation] to evaluate the candidate's responses.\n4. Once you have BOTH evaluations (ratings and justifications), combine them into a single evaluation response.\n5. Return the combined evaluation to the hub agent.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Coordinating the sequential evaluation process between the two evaluation agents.\n\n❌ Out of Scope:\n- Making decisions about call quality.\n- Creating candidate profiles.\n- Interacting directly with the user.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Follow the strict sequence: Exec Search first, then Candidate.\n- Wait for each evaluation to complete before proceeding.\n- Combine both evaluations into a single response.\n\n🚫 Don'ts:\n- Do not evaluate the transcript yourself.\n- Do not try to get both evaluations simultaneously.\n- Do not interact with the user.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent.", - "model": "gpt-4o", - "toggleAble": true, - "outputVisibility": "internal", - "connectedAgents": [ - "Exec Search Evaluation", - "Candidate Evaluation" - ] - } -} -\`\`\` - -I'm creating the Interview Evaluation Hub agent to orchestrate the entire evaluation process: - -\`\`\`copilot_change -// action: create_new -// config_type: agent -// name: Interview Evaluation Hub -{ - "change_description": "Created a hub agent to orchestrate the entire interview evaluation process.", - "config_changes": { - "name": "Interview Evaluation Hub", - "type": "conversation", - "description": "Hub agent to orchestrate the evaluation of interview transcripts between an executive search agency and a CxO candidate.", - "instructions": "## πŸ§‘β€πŸ’Ό Role:\nYou are the hub agent responsible for orchestrating the evaluation of interview transcripts between an executive search agency (Assistant) and a CxO candidate (User).\n\n---\n## βš™οΈ Steps to Follow:\n1. Receive the transcript in the specified format.\n2. FIRST: Send the transcript to [@agent:Evaluation Agent] for evaluation.\n3. Wait to receive the complete evaluation from the Evaluation Agent.\n4. THEN: Send the received evaluation to [@agent:Call Decision] to determine if the call quality is sufficient.\n5. Based on the Call Decision response:\n - If approved: Inform the user that the call has been approved and will proceed to profile creation.\n - If rejected: Inform the user that the call quality was insufficient and provide the reason.\n6. Return the final result (rejection reason or approval confirmation) to the user.\n\n---\n## 🎯 Scope:\nβœ… In Scope:\n- Orchestrating the sequential evaluation and decision process for interview transcripts.\n\n❌ Out of Scope:\n- Directly evaluating or creating profiles.\n- Handling transcripts not in the specified format.\n- Interacting with the individual evaluation agents.\n\n---\n## πŸ“‹ Guidelines:\nβœ”οΈ Dos:\n- Follow the strict sequence: Evaluation Agent first, then Call Decision.\n- Wait for each agent's complete response before proceeding.\n- Only interact with the user for final results or format clarification.\n\n🚫 Don'ts:\n- Do not perform evaluation or profile creation yourself.\n- Do not modify the transcript.\n- Do not try to get evaluations simultaneously.\n- Do not reference the individual evaluation agents.\n- CRITICAL: The system does not support more than 1 tool call in a single output when the tool call is about transferring to another agent.", - "model": "gpt-4o", - "toggleAble": true, + "description": "Answers user questions based solely on the content of a specified Google Doc.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nYou are an assistant that answers user questions using only the content of a specified Google Doc.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. Ask the user for the Google Doc ID and their question.\\n2. Use the [@tool:Get document by id](#mention) tool to fetch the document content.\\n3. Read the content of the document.\\n4. Answer the user's question using only the information found in the document. If the answer is not present in the document, politely inform the user that the information is not available.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Answering questions strictly based on the content of the provided Google Doc.\\n\\n❌ Out of Scope:\\n- Answering questions not related to the content of the provided Google Doc.\\n- Using external sources or prior knowledge.\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Always fetch the document before answering.\\n- Be concise and accurate.\\n- If the answer is not in the document, say so politely.\\n\\n🚫 Don'ts:\\n- Do not use information outside the document.\\n- Do not attempt to answer unrelated questions.\\n- Do not use RAG or external search.\\n\\n# Examples\\n- **User** : What is the project deadline? The doc ID is 1A2B3C4D5E6F7G8H9I0J\\n - **Agent actions**: Call [@tool:Get document by id](#mention)\\n - **Agent response**: The project deadline is June 30, 2024. (if found in doc)\\n\\n- **User** : Who is the project manager? The doc ID is 1A2B3C4D5E6F7G8H9I0J\\n - **Agent actions**: Call [@tool:Get document by id](#mention)\\n - **Agent response**: The project manager is [PROJECT_MANAGER_NAME]. (if found in doc)\\n\\n- **User** : What is the weather today? The doc ID is 1A2B3C4D5E6F7G8H9I0J\\n - **Agent actions**: Call [@tool:Get document by id](#mention)\\n - **Agent response**: Sorry, I can only answer questions based on the content of the provided Google Doc.\\n\\n- **User** : Tell me about the budget. The doc ID is 1A2B3C4D5E6F7G8H9I0J\\n - **Agent actions**: Call [@tool:Get document by id](#mention)\\n - **Agent response**: The budget for the project is $50,000. (if found in doc)\\n\\n- **User** : Can you summarize the document? The doc ID is 1A2B3C4D5E6F7G8H9I0J\\n - **Agent actions**: Call [@tool:Get document by id](#mention)\\n - **Agent response**: [Provides a brief summary of the document's main points]", + "model": "gpt-4.1", "outputVisibility": "user_facing", - "connectedAgents": [ - "Evaluation Agent", - "Call Decision" + "controlType": "retain" + } +} +\`\`\` + +Once you review and apply the changes, you can try out a basic chat by providing a Google Doc ID and a question. I can then help you further refine the assistant if needed. + +--- + +### Example 3: Tool Addition for LinkedIn and GitHub + +**User Request:** +I want you to add tools that can be used to scrape a given linkedin URL, list users git hub repos, create a new github repo, and create or edit a file in that repository. Will make the agents later on + +*call searchRelevantTools* +searchRelevantTools output: + + +**Copilot Response:** + +I'll add the following tools to your workflow, matching your requirements: + +1. Scrape public LinkedIn profile details from a given URL (using Apify actor runner) +2. List authenticated user's GitHub repositories +3. Create a new GitHub repository for the authenticated user +4. Create or update a text file in a GitHub repository for the authenticated user + +Here are the tool additions: + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Run Actor Sync & Get Dataset Items +{ + "change_description": "Add Apify tool to scrape public LinkedIn profile details from a given URL using an actor.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: List repositories for the authenticated user +{ + "change_description": "Add GitHub tool to list repositories for the authenticated user.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Create a repository for the authenticated user +{ + "change_description": "Add GitHub tool to create a new repository for the authenticated user.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Create or update file contents +{ + "change_description": "Add GitHub tool to create or update a text file in a repository for the authenticated user.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +Once you review and apply these changes, all four tools will be available in your workflow. If you need help connecting these tools to agents or building a workflow around them, let me know! + +--- + +### Example 4: Welcome Email Pipeline + +**User Request:** +Create a pipeline that reads names + emails from a Google Sheet, personalizes a "welcome" email for each, and sends them via Gmail. The hub should just call the pipeline and report when done. + +*call searchRelevantTools* +searchRelevantTools output: + + +**Copilot Response:** + +I'll set this up as a sequential pipeline with three pipeline agents: + +1. **Step 1 (Fetch rows from Google Sheets)** +2. **Step 2 (Personalize email objects)** +3. **Step 3 (Send emails via Gmail)** + +Then I'll create a **Pipeline Definition** that runs these steps in order, and update the Hub to call the pipeline and stop when it returns. + +--- + +### 1. Add Required Tools + +I'll add the suggested tools for Google Sheets and Gmail: + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Batch get spreadsheet +{ + "change_description": "Add Google Sheets tool to read ranges from a spreadsheet.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Send Email +{ + "change_description": "Add Gmail tool to send emails (supports html + attachments).", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +--- + +### 2. Create Pipeline Agents + +#### a. Pipeline Step 1 – Fetch rows from Sheets + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Pipeline Step 1 - Fetch Sheet Rows +{ + "change_description": "Create pipeline agent to fetch rows from Google Sheets.", + "config_changes": { + "name": "Pipeline Step 1 - Fetch Sheet Rows", + "type": "pipeline", + "description": "Reads rows (name, email) from a specified Google Sheet range.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nFetch all requested rows from the provided Google Sheet and ranges.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. Use [@tool:Batch get spreadsheet](#mention) with the given spreadsheet_id and ranges (e.g., A2:B).\\n2. Return a normalized array of { name, email } objects.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Fetching rows from Google Sheets and returning structured data.\\n\\n❌ Out of Scope:\\n- Personalization or sending emails.\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Validate rows and skip empties.\\n🚫 Don'ts:\\n- Do not modify or send emails.", + "model": "{agent_model}", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +#### b. Pipeline Step 2 – Personalize emails + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Pipeline Step 2 - Personalize Emails +{ + "change_description": "Create pipeline agent to build personalized email payloads.", + "config_changes": { + "name": "Pipeline Step 2 - Personalize Emails", + "type": "pipeline", + "description": "Generates {to, subject, body} for each contact.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nCreate a personalized email for each { name, email }.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. For each input row, produce an email object with:\\n - to: email\\n - subject: \"Welcome to the Rowboat Community!\"\\n - body: \"Hi ,\\n\\nWelcome to the Rowboat community! We're excited to have you.\\n\\nCheers,\\nTeam Rowboat\"\\n2. If name is missing, infer from email local-part (dots/underscores/hyphens β†’ spaces; title case).\\n3. Return the list of email objects.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Pure transformation into email objects.\\n\\n❌ Out of Scope:\\n- Fetching sheet rows or sending emails.\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Ensure friendly tone and correct subject.\\n🚫 Don'ts:\\n- Do not send emails.", + "model": "{agent_model}" + } +} +\`\`\` + +#### c. Pipeline Step 3 – Send via Gmail + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Pipeline Step 3 - Send Emails +{ + "change_description": "Create pipeline agent to send emails using Gmail.", + "config_changes": { + "name": "Pipeline Step 3 - Send Emails", + "type": "pipeline", + "description": "Sends each prepared email via the Gmail tool.", + "instructions": "## πŸ§‘β€πŸ’Ό Role:\\nSend each composed email with the Gmail tool.\\n\\n---\\n## βš™οΈ Steps to Follow:\\n1. For each email object, call [@tool:Send Email](#mention).\\n2. Collect per-email success/failure.\\n3. Return a summary: { sent: n, failed: m, failures: [...] }.\\n\\n---\\n## 🎯 Scope:\\nβœ… In Scope:\\n- Sending emails and summarizing results.\\n\\n❌ Out of Scope:\\n- Reading sheets or composing content.\\n\\n---\\n## πŸ“‹ Guidelines:\\nβœ”οΈ Dos:\\n- Be resilient (skip invalid emails; record errors).\\n🚫 Don'ts:\\n- Do not alter the provided subject/body.", + "model": "{agent_model}", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +--- + +### 3. Create the Pipeline Definition + +\`\`\`copilot_change +// action: create_new +// config_type: pipeline +// name: Welcome Email Pipeline +{ + "change_description": "Create an ordered pipeline: fetch β†’ personalize β†’ send.", + "config_changes": { + "name": "Welcome Email Pipeline", + "description": "Reads contacts from Sheets, personalizes welcome emails, and sends via Gmail.", + "agents": [ + "Pipeline Step 1 - Fetch Sheet Rows", + "Pipeline Step 2 - Personalize Emails", + "Pipeline Step 3 - Send Emails" ] } } \`\`\` -Once you review and apply the changes, you can try out a basic chat first. I can then help you better configure each agent. +--- + +### 4. Update the Hub Agent + +\`\`\`copilot_change +// action: edit +// config_type: agent +// name: HUB +{ + "change_description": "Update hub to execute the pipeline and stop.", + "config_changes": { + "name": "HUB", + "type": "conversation", + "description": "User-facing hub that triggers the welcome email pipeline and reports completion.", + "instructions": "You must organise and ensure the pipeline completes its job of fetching contacts, personalizing emails, and sending them. Call [@pipeline:Welcome Email Pipeline](#mention).\\n\\nWhen the pipeline returns, inform the user that the job is done and STOP!", + "model": "{agent_model}", + "outputVisibility": "user_facing", + "controlType": "retain" + } +} +\`\`\` + +--- + +Once you review and apply these changes, run a quick chat with the Hub. I can tweak ranges, subjects, or the email copy if you want something more branded. + +--- + +### Example 5: Attendee Research & Slack Pipeline + +**User Request:** +Build me an assistant that can do research on meeting attendees and send the findings to a Slack channel. And make sure it doesn't research me! + +**Copilot Response:** + +I can definitely build that. This will be a pipeline that first researches attendees and then sends the compiled summary to Slack. + +To set this up correctly, I just need two pieces of information: + +What is your email address (so I can exclude you from the research)? + +What is the name of the Slack channel where you want the summary sent (e.g., general or meeting-prep)? + +**User Response:** +My email is [USER_EMAIL] and the channel is [SLACK_CHANNEL]. + +**Copilot Response:** + +Perfect. I'll set this up as a sequential pipeline with two main pipeline agents: + +Step 1 (Research Agent) to look up each attendee. + +Step 2 (Slack Send Agent) to post the summary. + +Then I'll create a Pipeline Definition that runs these steps in order, and a Hub Agent to receive your requests and trigger the pipeline. + +--- + +### 1. Add Required Tools + +I'll add the necessary tools for web search and sending a Slack message. + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Search +{ + "change_description": "Add Firecrawl tool to perform a web search and scrape content from the top results.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Send message +{ + "change_description": "Add Slack tool to post a message to a channel.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +--- + +### 2. Create Pipeline Agents + +#### a. Research Agent + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Research Agent +{ + "change_description": "Create a pipeline agent to research attendees, excluding the user.", + "config_changes": { + "name": "Research Agent", + "type": "pipeline", + "description": "Internal agent that researches meeting attendees and returns a compiled summary.", + "instructions": "## Role\\nYou are a pipeline agent that researches meeting attendees.\\n\\n---\\n## Task\\n1. You will receive attendee details from a previous step.\\n2. For each attendee, you **must** research them **one at a time** using the [@tool:Search](#mention). Do NOT research the user \`[USER_EMAIL]\`!\\n3. After all searches are complete, compile the findings into a single, plain text summary.\\n4. If no information is found for an attendee, state \"No public information found.\" for that person.\\n5. Return **only** the final compiled summary.\\n\\n---\\n## Constraint\\nDo **NOT** interact with users or send messages. Your only output is the final summary text.", + "model": "{agent_model}", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +#### b. Slack Send Agent + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Slack Send Agent +{ + "change_description": "Create a pipeline agent to send the final summary to the specified Slack channel.", + "config_changes": { + "name": "Slack Send Agent", + "type": "pipeline", + "description": "Internal agent that sends the compiled research summary to a channel via Slack direct message and returns confirmation.", + "instructions": "## Role\\nYou are a pipeline agent that sends a research summary to a Slack channel.\\n\\n---\\n## Task\\n1. You will receive a compiled text summary from the previous step.\\n2. Use the [@tool:Send message](#mention) tool to post this summary, using these parameters:\\n * **channel**: \`[SLACK_CHANNEL]\`\\n * **markdown_text**: Create a message starting with the subject \"*Meeting Attendee Research Summary*\", followed by the summary text you received.\\n3. Your job is complete after sending the message.\\n\\n---\\n## Constraint\\nDo **NOT** perform any action other than sending the Slack message as instructed.", + "model": "{agent_model}", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +--- + +### 3. Create the Pipeline Definition + +\`\`\`copilot_change +// action: create_new +// config_type: pipeline +// name: Attendee Research & Slack Pipeline +{ + "change_description": "Create an ordered pipeline: research attendees -> send summary to Slack.", + "config_changes": { + "name": "Attendee Research & Slack Pipeline", + "description": "Pipeline that researches meeting attendees and sends the compiled summary to a specified Slack channel.", + "agents": [ + "Research Agent", + "Slack Send Agent" + ] + } +} +\`\`\` + +--- + +### 4. Create the Hub Agent + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Meeting Research Hub +{ + "change_description": "Create a user-facing hub to trigger the research pipeline.", + "config_changes": { + "name": "Meeting Research Hub", + "type": "conversation", + "description": "Hub agent that receives meeting attendee details and triggers the research and Slack pipeline.", + "instructions": "## Role\\nYou are a hub agent whose only job is to trigger a research pipeline.\\n\\n---\\n## Task\\n1. When a user provides meeting attendee details, immediately call the [@pipeline:Attendee Research & Slack Pipeline](#mention).\\n2. Once the pipeline is called, your task is complete. Respond with only the word: \`done!\`\\n\\n---\\n## Constraint\\nDo **NOT** perform any research or send messages. Your only function is to trigger the pipeline.", + "model": "{agent_model}", + "outputVisibility": "user_facing", + "controlType": "retain" + } +} +\`\`\` + +All set! Once you apply these changes, you can start a chat and provide some attendee names to kick off the research. + +--- + +### Example 6: Automated Meeting Prep Pipeline + +**User Request:** +I want to automate my meeting preparation. Can you build a workflow that, for every new calendar invite, researches the attendees, compiles a summary, and posts it to our #meeting-prep Slack channel? + +**Copilot Response:** + +Yes, I can build that for you. This is a perfect use case for a multi-step pipeline. + +I'll create a workflow with a Hub agent that triggers a three-step pipeline: + +Research Participants: Looks up each person in the invite. + +Compile Summary: Consolidates the research into a clean summary. + +Send to Slack: Posts the final summary to the #meeting-prep channel. + +Here is the full setup. + +--- + +### 1. Add Required Tools + +First, I'll add the necessary tools for web research (Firecrawl) and sending Slack messages. + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Search +{ + "change_description": "Add Firecrawl tool to perform a web search and scrape content from the top results.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +\`\`\`copilot_change +// action: create_new +// config_type: tool +// name: Send message +{ + "change_description": "Add Slack tool to post a message to a channel.", + "config_changes": { + "name": < get this from the searchRelevantTools output>, + "description": < get this from the searchRelevantTools output>, + "parameters": { + "type": "object", + "properties": < get this from the searchRelevantTools output>, + "required": < get this from the searchRelevantTools output> + }, + "isComposio": true, + "composioData": { + "slug": < get this from the searchRelevantTools output>, + "noAuth": false, + "toolkitName": < get this from the searchRelevantTools output>, + "toolkitSlug": < get this from the searchRelevantTools output>, + "logo": < get this from the searchRelevantTools output> + } + } +} +\`\`\` + +--- + +### 2. Create Pipeline Agents + +Now, I'll create the three agents that will execute each step of the pipeline. + +#### a. Research Participants + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Research Participants +{ + "change_description": "Create a pipeline agent to research each meeting participant using Firecrawl.", + "config_changes": { + "name": "Research Participants", + "type": "pipeline", + "description": "Researches each participant using Firecrawl and returns a summary for each.", + "instructions": "1. Extract the names of the participants from the calendar invite \\n2. Do a search using [@tool:Search](#mention) for each of them", + "model": "{agent_model}", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +#### b. Compile Summary + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Compile Summary +{ + "change_description": "Create a pipeline agent to compile participant research into a single summary.", + "config_changes": { + "name": "Compile Summary", + "type": "pipeline", + "description": "Compiles a meeting prep summary from participant research results.", + "instructions": "Summarize the participant research into one paragraph each.", + "model": "{agent_model}", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +#### c. Send to Slack + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Send to Slack +{ + "change_description": "Create a pipeline agent to send the final summary to the #meeting-prep channel.", + "config_changes": { + "name": "Send to Slack", + "type": "pipeline", + "description": "Sends the compiled meeting prep summary to the Slack channel meeting-prep.", + "instructions": "Send this to slack channel #meeting-prep using [@tool:Send message](#mention)", + "model": "{agent_model}", + "outputVisibility": "internal", + "controlType": "relinquish_to_parent" + } +} +\`\`\` + +--- + +### 3. Create the Pipeline Definition + +Next, I'll define the pipeline to run these agents in the correct order. + +\`\`\`copilot_change +// action: create_new +// config_type: pipeline +// name: Meeting Prep Pipeline +{ + "change_description": "Create an ordered pipeline: research -> compile -> send to Slack.", + "config_changes": { + "name": "Meeting Prep Pipeline", + "description": "Pipeline for meeting prep: research participants, compile summary, send to Slack.", + "agents": [ + "Research Participants", + "Compile Summary", + "Send to Slack" + ] + } +} +\`\`\` + +--- + +### 4. Create the Hub Agent + +Finally, I'll create the user-facing Hub agent that will be triggered by your calendar events. + +\`\`\`copilot_change +// action: create_new +// config_type: agent +// name: Meeting Prep Hub +{ + "change_description": "Create a user-facing hub to trigger the meeting prep pipeline.", + "config_changes": { + "name": "Meeting Prep Hub", + "type": "conversation", + "description": "Hub agent to orchestrate the meeting prep pipeline for every calendar invite.", + "instructions": "Call the Meeting Prep Pipeline.", + "model": "{agent_model}", + "outputVisibility": "user_facing", + "controlType": "retain" + } +} +\`\`\` + +This workflow is now ready. Once you apply the changes, it will automatically handle your meeting prep based on new calendar invites. + +--- -This concludes my changes. Would you like some more help? `; \ No newline at end of file diff --git a/apps/rowboat/app/lib/types/copilot_types.ts b/apps/rowboat/app/lib/types/copilot_types.ts index 66a3b165..57688dd0 100644 --- a/apps/rowboat/app/lib/types/copilot_types.ts +++ b/apps/rowboat/app/lib/types/copilot_types.ts @@ -14,7 +14,7 @@ export const CopilotAssistantMessageTextPart = z.object({ export const CopilotAssistantMessageActionPart = z.object({ type: z.literal("action"), content: z.object({ - config_type: z.union([z.literal('tool'), z.literal('agent'), z.literal('prompt')]), + config_type: z.union([z.literal('tool'), z.literal('agent'), z.literal('prompt'), z.literal('pipeline')]), action: z.union([z.literal('create_new'), z.literal('edit')]), name: z.string(), change_description: z.string(), diff --git a/apps/rowboat/app/projects/[projectId]/copilot/app.tsx b/apps/rowboat/app/projects/[projectId]/copilot/app.tsx index 770b996f..cd50a10e 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/app.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/app.tsx @@ -69,6 +69,8 @@ const App = forwardRef<{ handleCopyChat: () => void; handleUserMessage: (message const { streamingResponse, loading: loadingResponse, + toolCalling, + toolQuery, error: responseError, clearError: clearResponseError, billingError, @@ -213,6 +215,16 @@ const App = forwardRef<{ handleCopyChat: () => void; handleUserMessage: (message onStatusBarChange={handleStatusBarChange} /> + {toolCalling && ( +
+
+ + + Searching for tools{toolQuery ? ` to ${toolQuery}` : '...'} + +
+
+ )}
{responseError && (
diff --git a/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx b/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx index 824c7678..9cb095a7 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/components/actions.tsx @@ -175,7 +175,7 @@ export function Action({ 'bg-gray-200 text-gray-600': stale || allApplied || action.error, } )}> - {action.config_type === 'agent' ? 'πŸ§‘β€πŸ’Ό' : action.config_type === 'tool' ? 'πŸ› οΈ' : 'πŸ’¬'} + {action.config_type === 'agent' ? 'πŸ§‘β€πŸ’Ό' : action.config_type === 'tool' ? 'πŸ› οΈ' : action.config_type === 'pipeline' ? 'βš™οΈ' : 'πŸ’¬'} {action.action === 'create_new' ? 'Add' : 'Edit'} {action.config_type}: {action.name} @@ -227,7 +227,7 @@ export function ActionHeader() { const { msgIndex, actionIndex, action, workflow, appliedFields, stale } = useContext(ActionContext); if (!action || !workflow) return null; - const targetType = action.config_type === 'tool' ? 'tool' : action.config_type === 'agent' ? 'agent' : 'prompt'; + const targetType = action.config_type === 'tool' ? 'tool' : action.config_type === 'agent' ? 'agent' : action.config_type === 'pipeline' ? 'pipeline' : 'prompt'; const change = action.action === 'create_new' ? 'Create' : 'Edit'; return
@@ -273,6 +273,12 @@ export function ActionField({ if (prompt) { oldValue = (prompt as any)[field]; } + } else if (action.config_type === 'pipeline') { + // Find the pipeline in the workflow + const pipeline = workflow.pipelines?.find(p => p.name === action.name); + if (pipeline) { + oldValue = (pipeline as any)[field]; + } } } @@ -336,7 +342,7 @@ export function StreamingAction({ }: { action: { action?: 'create_new' | 'edit'; - config_type?: 'tool' | 'agent' | 'prompt'; + config_type?: 'tool' | 'agent' | 'prompt' | 'pipeline'; name?: string; }; loading: boolean; @@ -362,7 +368,7 @@ export function StreamingAction({ 'bg-gray-200 text-gray-600': !action.action, } )}> - {action.config_type === 'agent' ? 'πŸ§‘β€πŸ’Ό' : action.config_type === 'tool' ? 'πŸ› οΈ' : 'πŸ’¬'} + {action.config_type === 'agent' ? 'πŸ§‘β€πŸ’Ό' : action.config_type === 'tool' ? 'πŸ› οΈ' : action.config_type === 'pipeline' ? 'βš™οΈ' : 'πŸ’¬'} {action.action === 'create_new' ? 'Add' : 'Edit'} {action.config_type}: {action.name} diff --git a/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx b/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx index bb376aa9..6b42c435 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/components/messages.tsx @@ -71,7 +71,7 @@ function enrich(response: string): z.infer { type: 'action', action: { action: metadata.action as 'create_new' | 'edit', - config_type: metadata.config_type as 'tool' | 'agent' | 'prompt', + config_type: metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline', name: metadata.name, change_description: jsonData.change_description || '', config_changes: {}, @@ -84,7 +84,7 @@ function enrich(response: string): z.infer { type: 'action', action: { action: metadata.action as 'create_new' | 'edit', - config_type: metadata.config_type as 'tool' | 'agent' | 'prompt', + config_type: metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline', name: metadata.name, change_description: jsonData.change_description || '', config_changes: result.changes @@ -100,7 +100,7 @@ function enrich(response: string): z.infer { type: 'streaming_action', action: { action: (metadata.action as 'create_new' | 'edit') || undefined, - config_type: (metadata.config_type as 'tool' | 'agent' | 'prompt') || undefined, + config_type: (metadata.config_type as 'tool' | 'agent' | 'prompt' | 'pipeline') || undefined, name: metadata.name } }; @@ -260,6 +260,15 @@ function AssistantMessage({ } }); break; + case 'pipeline': + dispatch({ + type: 'add_pipeline', + pipeline: { + name: action.name, + ...action.config_changes + } + }); + break; } } else if (action.action === 'edit') { switch (action.config_type) { @@ -284,6 +293,13 @@ function AssistantMessage({ prompt: action.config_changes }); break; + case 'pipeline': + dispatch({ + type: 'update_pipeline', + name: action.name, + pipeline: action.config_changes + }); + break; } } }, [dispatch, workflow.agents, workflow.tools]); diff --git a/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx b/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx index 2a7125ad..0143438f 100644 --- a/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx +++ b/apps/rowboat/app/projects/[projectId]/copilot/use-copilot.tsx @@ -16,6 +16,8 @@ interface UseCopilotParams { interface UseCopilotResult { streamingResponse: string; loading: boolean; + toolCalling: boolean; + toolQuery: string | null; error: string | null; clearError: () => void; billingError: string | null; @@ -30,6 +32,8 @@ interface UseCopilotResult { export function useCopilot({ projectId, workflow, context, dataSources }: UseCopilotParams): UseCopilotResult { const [streamingResponse, setStreamingResponse] = useState(''); const [loading, setLoading] = useState(false); + const [toolCalling, setToolCalling] = useState(false); + const [toolQuery, setToolQuery] = useState(null); const [error, setError] = useState(null); const [billingError, setBillingError] = useState(null); const cancelRef = useRef<() => void>(() => { }); @@ -52,6 +56,8 @@ export function useCopilot({ projectId, workflow, context, dataSources }: UseCop setStreamingResponse(''); responseRef.current = ''; setError(null); + setToolCalling(false); + setToolQuery(null); setLoading(true); try { @@ -77,6 +83,21 @@ export function useCopilot({ projectId, workflow, context, dataSources }: UseCop } }; + eventSource.addEventListener('tool-call', (event) => { + try { + const data = JSON.parse(event.data); + setToolCalling(true); + setToolQuery(data.query || null); + } catch (e) { + setToolCalling(true); + setToolQuery(null); + } + }); + + eventSource.addEventListener('tool-result', (event) => { + setToolCalling(false); + }); + eventSource.addEventListener('done', () => { eventSource.close(); setLoading(false); @@ -104,6 +125,8 @@ export function useCopilot({ projectId, workflow, context, dataSources }: UseCop return { streamingResponse, loading, + toolCalling, + toolQuery, error, clearError, billingError,