This document provides a detailed technical overview of the CoScientist multi-agent system architecture.
- System Overview
- High-Level Architecture
- Agent System
- Data Flow
- Storage Layer
- Tool Integration
- Configuration Management
- External Dependencies
- Sequence Diagrams
CoScientist is a multi-agent system designed for scientific discovery and research automation. It combines:
- Google Agent Development Kit (ADK) for agent orchestration
- Large Language Models for reasoning and decision-making
- FEDOT.MAS for automated experiment pipeline execution
- RAG-based tool retrieval for dynamic MCP server discovery
- Chemical computing services for molecular analysis
- Modularity: Each component is self-contained and loosely coupled
- Extensibility: Easy to add new agents, tools, and capabilities
- Scalability: Stateless design allows horizontal scaling
- Observability: Comprehensive logging for debugging and monitoring
┌─────────────────────────────────────────────────────────────────────────┐
│ CoScientist │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Client │────▶│ Orchestrator│ ────▶│ HypothesesAgent │ │
│ │ (User) │ │ Agent │ └──────────────────────┘ │
│ └──────────────┘ │ │ │
│ │ │ ┌──────────────────────┐ │
│ │ │────▶│ ResearchAgent │ │
│ │ │ └──────────────────────┘ │
│ │ │ │
│ │ │ ┌──────────────────────┐ │
│ │ │────▶│ TaskExecutorAgent │ │
│ │ │ │ (router) │ │
│ │ │ │ ┌──────────────┐ │ │
│ │ │ │ │ ToolPipeline │ │ │
│ │ │ │ │ Agent │ │ │
│ │ │ │ │ (prep → run) │ │ │
│ │ │ │ └──────────────┘ │ │
│ │ │ │ ┌──────────────┐ │ │
│ │ │ │ │ CoderAgent │ │ │
│ │ │ │ └──────────────┘ │ │
│ └──────────────┘ └──────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ External Services │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ FEDOT.MAS │ │ RAG Tools │ │ ChromaDB │ │ Tavily │ │
│ │ (MCP) │ │ (MCP) │ │ (Vector DB) │ │ API │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └───────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Chemical │ │Retrosynthesis│ │ S3 │ │
│ │ Services │ │ Service │ │ Storage │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
CoScientist Agents
│
├── OrchestratorAgent (Root)
│ ├── HypothesesAgent
│ ├── ResearchAgent
│ ├── MedicalAgent
│ └── TaskExecutorAgent (LLM router)
│ ├── ToolPipelineAgent (Sequential)
│ │ ├── ToolPreparerAgent (retrieve → rerank → deploy MCP servers)
│ │ └── ExecutorSwitchAgent (runs exactly one of:)
│ │ ├── ExperimentAgent (runs the deployed MCP tools)
│ │ └── FedotAgent (fallback: reranker produced no usable ranking)
│ └── CoderAgent
│ └── DatasetCollectorAgent
Purpose: Central coordinator for all agent activities
Architecture:
- Type:
LlmAgent(Google ADK) - Model: Configurable LLM via LiteLLM
- Tools: AgentTool instances for sub-agents
Responsibilities:
- Parse user queries
- Plan execution strategy
- Delegate to specialized agents
- Aggregate results
- Manage conversation state
Decision Logic:
if task_needs_ideas:
delegate(hypotheses_agent)
if task_needs_knowledge:
delegate(research_agent)
if task_needs_computation:
delegate(task_executor_agent)Purpose: Generate testable scientific hypotheses
Architecture:
- Type:
LlmAgent - Output Key:
hypotheses - No external tools
Purpose: Gather scientific knowledge from various sources
Architecture:
- Type:
LlmAgent - Output Key:
search_results - Tools:
websearch_toolset_instance(Tavily MCP)
Capabilities:
- Web search via Tavily API
- Literature search
- Knowledge synthesis
Purpose: Discover relevant MCP servers using RAG
Architecture:
- Type:
LlmAgent - Output Key:
retrieved_tools - Output Schema:
RetrievalFinalResult - Tools:
retrieval_toolset_instance
RAG Pipeline:
- Query embedding via APIEmbedder
- Vector search in Qdrant
- Hybrid reranking (BM25 + API Reranker)
- Top-k selection
Purpose: Execute computational experiments with the prepared MCP tools
Architecture:
- Type:
custom:executor_switch— runs exactly ONE of its two children - Children:
ExperimentAgent(default),FedotAgent(fallback)
ExperimentAgent is a ReAct LlmAgent (output_key: fedot_results) that calls
the task's selected MCP servers directly through dynamic_tools, resolved from
filtered_tools + deployed_mcps on every turn.
FedotAgent (tools: [fedot, ...], same output_key) runs only when the tool
reranker never actually JUDGED the retrieved candidates — its reply was
unreadable, or carried no usable {index, score} pair, and the local
cross-encoder could not stand in for it. filtered_tools is then empty for a
reason that says nothing about relevance, so abstaining to CoderAgent would
discard tools retrieval found correctly. Instead fedot_tool receives the whole
unfiltered accumulated_tools pool and FEDOT.MAS's meta-agent does the
selection itself — with a bounded time budget, since a malformed model reply
rather than a deliberate choice triggered the run. Gated by
EXECUTOR__FEDOT_FALLBACK.
A switch rather than two siblings in the sequence because AgentTool returns
the LAST content-bearing event of the agent it wraps, and a stood-down sibling
still emits one — it would overwrite the executor's answer.
Execution Flow (FedotAgent):
- Receive task description
- Build FEDOT.MAS pipeline
- Connect to MCP servers
- Execute pipeline
- Return results
Purpose: Route a task to the execution path that can deliver it
Architecture:
- Type:
LlmAgent(router — no tools of its own) - Output Key:
executor_results - Subordinates (AgentTools):
ToolPipelineAgent(SequentialAgent: [ToolPreparerAgent, ExecutorSwitchAgent]) — runs the task with EXISTING MCP toolsCoderAgent— writes and runs code in the sandbox when no tool matches
Routing: engineering work goes straight to the coder; tool-shaped work goes
to the pipeline. If the pipeline abstains with NO_MATCHING_TOOL (see
redirect_when_no_tools), the router re-issues the same task to the coder
instead of bubbling the abstention up to the orchestrator.
User Query
│
▼
┌─────────────────┐
│ Parse & Validate│
└────────┬────────┘
│
▼
┌─────────────────┐
│ Orchestrator │
│ Decision Making │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌──────────┐
│Hypoth.│ │ Research │
│Agent │ │ Agent │
└───┬───┘ └────┬─────┘
│ │
└────┬─────┘
│
▼
┌─────────────────┐
│ Tool Retriever │
│ Agent │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Experiment │
│ Agent │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Aggregate & │
│ Respond │
└─────────────────┘
class RetrievalFinalResult(BaseModel):
servers_id: List[str] # Selected MCP server IDs
queries: List[str] # Queries used for retrieval
task: str # Original task descriptionclass RetrievalToolResult(BaseModel):
tool: str # Tool name
server_id: str # Server identifier
description: str # Tool description
score: float # Relevance score| Component | Technology | Purpose |
|---|---|---|
| Vector Store | ChromaDB | Paper embeddings |
| Tool Store | PostgreSQL | MCP server metadata |
| Document Store | S3 | PDF/image storage |
| Cache | In-Memory | Session data |
┌─────────────────────────────────────────┐
│ Toolset Registry │
├─────────────────────────────────────────┤
│ │
│ ┌───────────────┐ ┌──────────────-─┐ │
│ │FedotMASToolset│ |WebSearchToolset│ │
│ └───────┬───────┘ └───────┬─────-──┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ FEDOT.MAS │ │ Tavily MCP │ │
│ │ Client │ │ Client │ │
│ └───────────────┘ └───────────────┘ │
│ │
│ ┌───────────────┐ │
│ │RetrievalToolset│ │
│ └───────┬───────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ RAG Client │ │
│ │ (HTTP) │ │
│ └───────────────┘ │
│ │
└─────────────────────────────────────────┘
# Tool Retrieval Flow
class RetrievalToolSet(BaseToolset):
async def retrieve_tools(self, query: str) -> Dict:
# 1. Embed query
embedder = APIEmbedder(settings.api_embedding)
query_embedding = embedder.embed(query)
# 2. Search in vector store
results = await vector_store.search(
query_embedding,
top_k=settings.rag.default_top_k
)
# 3. Rerank results
reranker = HybridReranker(...)
reranked = reranker.rerank(results)
return {"tools": reranked}Settings
├── llm (LLMSettings)
│ ├── main_model
│ ├── service_key
│ ├── openai_api_key
│ └── ...
├── services (ServicesSettings)
│ ├── tavily_api_key
│ └── openalex_api_key
├── storage (StorageSettings)
│ ├── root_dir
│ ├── parse_results
│ └── ...
├── hosts_ports (HostsPortsSettings)
│ ├── chroma_host
│ ├── chem_services_host
│ └── ...
├── s3 (S3Settings)
│ ├── endpoint_url
│ ├── access_key
│ └── ...
└── tool_rag (ToolRAGSettings)
├── postgres
├── embedding
└── ...
LLM__MAIN_MODEL → settings.llm.main_model
SERVICES__TAVILY_API_KEY → settings.services.tavily_api_key
STORAGE__LOGGING_PATH → settings.storage.logging_path
HOSTS_PORTS__CHROMA_HOST → settings.hosts_ports.chroma_host
S3__ENDPOINT_URL → settings.s3.endpoint_url
Application Logs ──▶ Logger ──┬──▶ Console Handler
│
└──▶ File Handler ──▶ logs/app.log
CoScientist
├── agents/
│ └── agents.py
│ ├── Requires: google-adk
│ ├── Requires: litellm
│ └── Requires: config
├── chemical_utils/
│ └── Requires: Chemical Services API
├── config/
│ └── Requires: pydantic-settings
├── logging/
│ └── Built-in Python logging
├── paper_parser/
│ ├── Requires: marker
│ ├── Requires: langchain
│ └── Requires: S3/boto3
├── storage/
│ ├── Requires: rag_tools
│ └── Requires: pydantic
└── tools/
├── Requires: fedotmas
├── Requires: rag_tools
└── Requires: google-adk tools
| Term | Definition |
|---|---|
| MCP | Model Context Protocol - Standard for tool integration |
| RAG | Retrieval-Augmented Generation |
| FEDOT | Framework for Evolutionary Data Operations |
| ADK | Agent Development Kit |
| Toolset | Collection of related tools |
| Agent | Autonomous entity that can make decisions |