diff --git a/Dockerfile.qdrant b/Dockerfile.qdrant new file mode 100644 index 00000000..9e3b3934 --- /dev/null +++ b/Dockerfile.qdrant @@ -0,0 +1,3 @@ +FROM qdrant/qdrant:latest + +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/README.md b/README.md index bc806147..7b7460ce 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,17 @@ Powered by OpenAI's Agents SDK, Rowboat is the fastest way to build multi-agents export OPENAI_API_KEY=your-openai-api-key ``` -2. Clone the repository and start Rowboat docker +2. Clone the repository and start Rowboat ```bash git clone git@github.com:rowboatlabs/rowboat.git cd rowboat - docker-compose up --build + ./start.sh ``` 3. Access the app at [http://localhost:3000](http://localhost:3000). +Note: We have added native RAG support including file-uploads and URL scraping. See the [RAG](https://docs.rowboatlabs.com/using_rag) section of our docs for this. + Note: See the [Using custom LLM providers](https://docs.rowboatlabs.com/setup/#using-custom-llm-providers) section of our docs for using custom providers like OpenRouter and LiteLLM. ## Demo diff --git a/apps/copilot/app.py b/apps/copilot/app.py index 40b29321..26672d26 100644 --- a/apps/copilot/app.py +++ b/apps/copilot/app.py @@ -1,6 +1,6 @@ from flask import Flask, request, jsonify, Response, stream_with_context -from pydantic import BaseModel, ValidationError -from typing import List +from pydantic import BaseModel, ValidationError, Field +from typing import List, Optional from copilot import UserMessage, AssistantMessage, get_response from streaming import get_streaming_response from lib import AgentContext, PromptContext, ToolContext, ChatContext @@ -9,11 +9,24 @@ from functools import wraps from copilot import copilot_instructions_edit_agent import json +class DataSource(BaseModel): + id: str = Field(alias='_id') + name: str + description: Optional[str] = None + active: bool = True + status: str # 'pending' | 'ready' | 'error' | 'deleted' + error: Optional[str] = None + data: dict # The discriminated union based on type + + class Config: + populate_by_name = True + class ApiRequest(BaseModel): messages: List[UserMessage | AssistantMessage] workflow_schema: str current_workflow_config: str context: AgentContext | PromptContext | ToolContext | ChatContext | None = None + dataSources: Optional[List[DataSource]] = None class ApiResponse(BaseModel): response: str @@ -52,7 +65,10 @@ def health(): @require_api_key def chat_stream(): try: - request_data = ApiRequest(**request.json) + raw_data = request.json + print(f"Raw request JSON: {json.dumps(raw_data)}") + + request_data = ApiRequest(**raw_data) print(f"received /chat_stream request: {request_data}") validate_request(request_data) @@ -61,7 +77,8 @@ def chat_stream(): messages=request_data.messages, workflow_schema=request_data.workflow_schema, current_workflow_config=request_data.current_workflow_config, - context=request_data.context + context=request_data.context, + dataSources=request_data.dataSources ) for chunk in stream: diff --git a/apps/copilot/copilot.py b/apps/copilot/copilot.py index 3f4697d0..817275f5 100644 --- a/apps/copilot/copilot.py +++ b/apps/copilot/copilot.py @@ -1,7 +1,7 @@ from openai import OpenAI from flask import Flask, request, jsonify from pydantic import BaseModel, ValidationError -from typing import List, Dict, Any, Literal +from typing import List, Dict, Any, Literal, Optional import json from lib import AgentContext, PromptContext, ToolContext, ChatContext from client import PROVIDER_COPILOT_MODEL @@ -15,6 +15,15 @@ class AssistantMessage(BaseModel): role: Literal["assistant"] content: str +class DataSource(BaseModel): + _id: str + name: str + description: Optional[str] = None + active: bool = True + status: str # 'pending' | 'ready' | 'error' | 'deleted' + error: Optional[str] = None + data: dict # The discriminated union based on type + with open('copilot_edit_agent.md', 'r', encoding='utf-8') as file: copilot_instructions_edit_agent = file.read() @@ -23,6 +32,7 @@ def get_response( workflow_schema: str, current_workflow_config: str, context: AgentContext | PromptContext | ToolContext | ChatContext | None = None, + dataSources: Optional[List[DataSource]] = None, copilot_instructions: str = copilot_instructions_edit_agent ) -> str: # if context is provided, create a prompt for the context @@ -53,6 +63,16 @@ def get_response( else: context_prompt = "" + # Add dataSources to the context if provided + data_sources_prompt = "" + if dataSources: + data_sources_prompt = f""" +**NOTE**: The following data sources are available: +```json +{json.dumps([ds.model_dump() for ds in dataSources])} +``` +""" + # add the workflow schema to the system prompt sys_prompt = copilot_instructions.replace("{workflow_schema}", workflow_schema) @@ -66,6 +86,7 @@ The current workflow config is: ``` {context_prompt} +{data_sources_prompt} User: {last_message.content} """ diff --git a/apps/copilot/copilot_multi_agent.md b/apps/copilot/copilot_multi_agent.md index 10969e77..0272e105 100644 --- a/apps/copilot/copilot_multi_agent.md +++ b/apps/copilot/copilot_multi_agent.md @@ -51,10 +51,10 @@ When the user asks you to create agents for a multi agent system, you should fol ## Section 3: Agent visibility and design patterns -1. Agents can have 2 types of visibility - user_facing or internal. +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. +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): @@ -62,7 +62,7 @@ When the user asks you to create agents for a multi agent system, you should fol A. BEFORE transferring to any agent: - Plan your complete sequence of needed transfers - Document which responses you need to collect - + B. DURING transfers: - Transfer to only ONE agent at a time - Wait for that agent's COMPLETE response and then proceed with the next agent @@ -70,7 +70,7 @@ When the user asks you to create agents for a multi agent system, you should fol - Only then proceed with the next transfer - Never attempt parallel or simultaneous transfers - 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. - + C. AFTER receiving a response: - Do not transfer to another agent until you've processed the current response - If you need to transfer to another agent, wait for your current processing to complete @@ -87,7 +87,7 @@ 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. +- 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. @@ -101,7 +101,7 @@ When the user asks you to edit an existing agent, you should follow the steps be 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. -### Section 3.1 : Adding Examples to an Agent +### Section 4.1 : 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,6 +124,19 @@ 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 + +When rag data sources are available you will be given the information on it like this: +' The following data sources are available:\n```json\n[{"id": "6822e76aa1358752955a455e", "name": "Handbook", "description": "This is a employee handbook", "active": true, "status": "ready", "error": null, "data": {"type": "text"}}]\n```\n\n\nUser: "can you add the handbook to the agent"\n'}]```' + +You should use the name and description to understand the data source, and use the id to attach the data source to the agent. Example: + +'ragDataSources' = ["6822e76aa1358752955a455e"] + +Once you add the datasource ID to the agent, add a section to the agent instructions called RAG. Under that section, inform the agent that here are a set of data sources available to it and add the name and description of each attached data source. Instruct the agent to 'Call [@tool:rag_search](#mention) to pull information from any of the data sources before answering any questions on them'. + +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: @@ -158,7 +171,7 @@ When creating a new agent, strictly follow the format of this example agent. The 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: [,