mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-03 19:25:19 +02:00
Copilot composio prompting (#198)
* added an example on using composio to copilot * temp save * simple example * updated the prompt * added "searching for tools..." banner * Merge branch 'dev' of github.com:rowboatlabs/rowboat into copilot-composio-prompting * changed tools default when added from "mockTool: true" to false (so they are enabled by default) * added pipelines to copilot * fixed bug that made copilot agents default to conversation type * Merge branch 'dev' of github.com:rowboatlabs/rowboat into copilot-composio-prompting * Refactor agent configuration in workflow editor to enforce controlType and outputVisibility rules. Added logic to fix existing agents with incorrect settings on mount. Updated task agent creation to ensure proper defaults for pipeline and conversational agents. Increased maxSteps for text streaming in copilot. * Merge branch 'dev' of github.com:rowboatlabs/rowboat into copilot-composio-prompting * added example * Add automated meeting preparation pipeline example with multi-step workflow for researching participants, compiling summaries, and posting to Slack. * Update example agents to use placeholders for user email and Slack channel in instructions and examples, enhancing privacy and flexibility in the meeting preparation pipeline. * Refactor sections in copilot_multi_agent.ts for clarity and consistency, including renaming section headers and improving instructions. Update query handling in copilot.ts to streamline event processing. * made the tool card block params genereic and linked to the searchRelevantTools so that it relies on that instead of examples * revert unrequired changes * Refactor agent configuration handling in workflow editor to streamline agent creation and ensure correct default values for controlType and outputVisibility. Removed redundant code for fixing existing agents and improved clarity in agent initialization logic. --------- Co-authored-by: Ramnique Singh <30795890+ramnique@users.noreply.github.com>
This commit is contained in:
parent
fa396aa0a4
commit
a8f0a132af
10 changed files with 1206 additions and 267 deletions
|
|
@ -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`));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string, unknown>, 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<typeof WorkflowPipeline>;
|
||||
schema = WorkflowPipeline;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return { error: `Unknown config type: ${configType}` };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<typeof CopilotChatContext> | null): string {
|
||||
let prompt = '';
|
||||
|
|
@ -97,13 +111,17 @@ ${JSON.stringify(simplifiedDataSources)}
|
|||
|
||||
async function searchRelevantTools(query: string): Promise<string> {
|
||||
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<string> {
|
|||
|
||||
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<typeof WorkflowTool>[] = composioTools.map(tool => ({
|
||||
name: tool.name,
|
||||
|
|
@ -144,6 +171,10 @@ async function searchRelevantTools(query: string): Promise<string> {
|
|||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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 = `
|
||||
|
||||
<core_identity>
|
||||
|
||||
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
|
||||
|
||||
</core_identity>
|
||||
|
||||
## Section 1 : Agent Behavior
|
||||
<building_multi_agent_systems>
|
||||
|
||||
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)
|
||||
|
||||
</building_multi_agent_systems>
|
||||
|
||||
## Section 2 : Planning and Creating a Multi-Agent System
|
||||
<about_agents>
|
||||
|
||||
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: [<date>, <time>] User: <user-message> [<date>, <time>] Assistant: <assistant-message>\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**: Call [@agent:Evaluation Agent](#mention)\n\n- **Agent receives Evaluation Agent result** :\n - **Agent actions**: Call [@agent:Call Decision](#mention)\n\n- **Agent receives Call Decision result (approved)** :\n - **Agent response**: The call has been approved. Proceeding to candidate profile creation.\n\n- **Agent receives Call Decision result (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: [<date>, <time>] User: <user-message> [<date>, <time>] Assistant: <assistant-message>\n\n- **User** : What happens after evaluation?\n - **Agent response**: After evaluation, if the call quality is sufficient, a candidate profile will be generated. Otherwise, you will receive feedback on why the call was rejected.
|
||||
\`\`\`
|
||||
|
||||
### Section 4.1 : Adding Examples to an Agent
|
||||
IMPORTANT: Use {agent_model} as the default model for new agents.
|
||||
|
||||
## Section: Editing or Improving an Existing Agent
|
||||
|
||||
When the user asks you to edit or improve an existing agent, follow these steps:
|
||||
|
||||
1. Understand the user’s intent.
|
||||
- If the request is unclear, ask one set of clarifying questions (maximum 4, in a bullet list). Keep this to a single turn.
|
||||
2. Preserve existing structure.
|
||||
- Retain as much of the original agent’s instructions as possible. Only change what is necessary based on the user’s request.
|
||||
3. Strengthen the agent’s clarity and reliability.
|
||||
- Review the instructions line by line. Identify any areas that are underspecified or ambiguous.
|
||||
- Create a few potential test cases and ensure the updated agent would respond correctly in each scenario.
|
||||
4. Return the full modified agent.
|
||||
- Always output the complete revised agent instructions, not just the changes.
|
||||
|
||||
### Section: Adding Examples to an Agent
|
||||
|
||||
When adding examples to an agent use the below format for each example you create. Add examples to the example field in the agent config. Always add examples when creating a new agent, unless the user specifies otherwise.
|
||||
|
||||
|
|
@ -124,7 +157,7 @@ Style of Response
|
|||
|
||||
If the user doesn't specify how many examples, always add 5 examples.
|
||||
|
||||
### Section 4.2 : Adding RAG data sources to an Agent
|
||||
### Section: Adding RAG data sources to an Agent
|
||||
|
||||
When rag data sources are available you will be given the information on it like this:
|
||||
\`\`\`
|
||||
|
|
@ -143,66 +176,75 @@ Once you add the datasource ID to the agent, add a section to the agent instruct
|
|||
|
||||
Note: the rag_search tool searches across all data sources - it cannot call a specific data source.
|
||||
|
||||
## Section 5 : Improving an Existing Agent
|
||||
|
||||
When the user asks you to improve an existing agent, you should follow the steps below:
|
||||
</about_agents>
|
||||
|
||||
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.
|
||||
<agent_tools>
|
||||
|
||||
## 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
|
||||
</agent_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 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.
|
||||
<about_pipelines>
|
||||
|
||||
## 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: [<date>, <time>] User: <user-message> [<date>, <time>] Assistant: <assistant-message>\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**: Call [@agent:Evaluation Agent](#mention)\n\n- **Agent receives Evaluation Agent result** :\n - **Agent actions**: Call [@agent:Call Decision](#mention)\n\n- **Agent receives Call Decision result (approved)** :\n - **Agent response**: The call has been approved. Proceeding to candidate profile creation.\n\n- **Agent receives Call Decision result (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: [<date>, <time>] User: <user-message> [<date>, <time>] Assistant: <assistant-message>\n\n- **User** : What happens after evaluation?\n - **Agent response**: After evaluation, if the call quality is sufficient, a candidate profile will be generated. Otherwise, you will receive feedback on why the call was rejected.
|
||||
\`\`\`
|
||||
### Pipeline Agent Instructions:
|
||||
Pipeline agents should follow this structure:
|
||||
- Focus on their specific step in the process
|
||||
- Process input from the previous step
|
||||
- Return clear output for the next step
|
||||
- Use tools as needed for their specific task
|
||||
- Do NOT transfer to other agents (only use tools)
|
||||
|
||||
IMPORTANT: Use {agent_model} as the default model for new agents.
|
||||
### Example Pipeline Usage:
|
||||
When a hub agent needs to execute a pipeline, it should:
|
||||
1. Call the pipeline using pipeline syntax
|
||||
2. Pass the required input to the pipeline
|
||||
3. Wait for the pipeline to complete all steps
|
||||
4. Receive the final result from the pipeline
|
||||
|
||||
</about_pipelines>
|
||||
|
||||
## Section 10: General Guidelines
|
||||
<general_guidlines>
|
||||
|
||||
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.
|
||||
|
||||
</general_guidlines>
|
||||
`;
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
</div>
|
||||
{toolCalling && (
|
||||
<div className="shrink-0 px-4 py-2.5 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-sm dark:shadow-gray-950/20 rounded-2xl mx-1 mb-2">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<Spinner size="sm" className="ml-2" />
|
||||
<span>
|
||||
Searching for tools{toolQuery ? ` to ${toolQuery}` : '...'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="shrink-0 px-1 pb-6">
|
||||
{responseError && (
|
||||
<div className="mb-4 p-2 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg flex gap-2 justify-between items-center text-sm">
|
||||
|
|
|
|||
|
|
@ -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' ? '⚙️' : '💬'}
|
||||
</span>
|
||||
<span className="font-semibold text-sm text-zinc-800 dark:text-zinc-100 truncate flex-1">
|
||||
{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 <div className="flex gap-2 items-center py-1 px-1">
|
||||
|
|
@ -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' ? '⚙️' : '💬'}
|
||||
</span>
|
||||
<span className="font-semibold text-sm text-zinc-800 dark:text-zinc-100 truncate flex-1">
|
||||
{action.action === 'create_new' ? 'Add' : 'Edit'} {action.config_type}: {action.name}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ function enrich(response: string): z.infer<typeof CopilotResponsePart> {
|
|||
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<typeof CopilotResponsePart> {
|
|||
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<typeof CopilotResponsePart> {
|
|||
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]);
|
||||
|
|
|
|||
|
|
@ -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<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [billingError, setBillingError] = useState<string | null>(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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue