diff --git a/README.md b/README.md index eba7f05..6adffe6 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,25 @@ mgrep --web "best practices for error handling in TypeScript" Web search queries the `mixedbread/web` store in addition to your local store, merging results based on relevance. Use `--answer` (or `-a`) to get a concise summary instead of raw results. +## mgrep as Subagent + +For complex questions that require information from multiple sources, `mgrep` can act as a subagent that automatically refines queries and performs multiple searches to find the best answer. + +```bash +# Enable agentic search for complex multi-part questions +mgrep --agentic "What are the yearly numbers for 2020, 2021, 2022, 2023, 2024?" + +# Combine with --answer for a synthesized response from multiple sources +mgrep --agentic -a "How does authentication work and where is it configured?" +``` + +When `--agentic` is enabled, mgrep will: +- Automatically break down complex queries into sub-queries +- Perform multiple searches as needed to gather comprehensive results +- Combine findings from different parts of your codebase + +This is particularly useful for questions that span multiple files or concepts, where a single search might miss important context. + ## Commands at a Glance | Command | Purpose | @@ -185,6 +204,7 @@ directory for a pattern. | `-c`, `--content` | Show content of the results | | `-a`, `--answer` | Generate an answer to the question based on the results | | `-w`, `--web` | Include web search results alongside local files | +| `--agentic` | Enable agentic search to automatically refine queries and perform multiple searches | | `-s`, `--sync` | Sync the local files to the store before searching | | `-d`, `--dry-run` | Dry run the search process (no actual file syncing) | | `--no-rerank` | Disable reranking of search results | @@ -281,6 +301,7 @@ searches. - `MGREP_CONTENT`: Show content of the results (set to `1` or `true` to enable) - `MGREP_ANSWER`: Generate an answer based on the results (set to `1` or `true` to enable) - `MGREP_WEB`: Include web search results (set to `1` or `true` to enable) +- `MGREP_AGENTIC`: Enable agentic search for complex multi-source queries (set to `1` or `true` to enable) - `MGREP_SYNC`: Sync files before searching (set to `1` or `true` to enable) - `MGREP_DRY_RUN`: Enable dry run mode (set to `1` or `true` to enable) - `MGREP_RERANK`: Enable reranking of search results (set to `0` or `false` to disable, default: enabled) diff --git a/src/commands/search.ts b/src/commands/search.ts index 32fd88f..880379e 100644 --- a/src/commands/search.ts +++ b/src/commands/search.ts @@ -251,6 +251,11 @@ export const search: Command = new CommanderCommand("search") "Include web search results from mixedbread/web store", parseBooleanEnv(process.env.MGREP_WEB, false), ) + .option( + "--agentic", + "Enable agentic search to automatically refine queries and perform multiple searches", + parseBooleanEnv(process.env.MGREP_AGENTIC, false), + ) .argument("", "The pattern to search for") .argument("[path]", "The path to search in") .allowUnknownOption(true) @@ -267,6 +272,7 @@ export const search: Command = new CommanderCommand("search") maxFileSize?: number; maxFileCount?: number; web: boolean; + agentic: boolean; } = cmd.optsWithGlobals(); if (exec_path?.startsWith("--")) { exec_path = ""; @@ -324,13 +330,18 @@ export const search: Command = new CommanderCommand("search") ], }; + const searchOptions = { + rerank: options.rerank, + ...(options.agentic && { agentic: true }), + }; + let response: string; if (!options.answer) { const results = await store.search( storeIds, pattern, parseInt(options.maxCount, 10), - { rerank: options.rerank }, + searchOptions, filters, ); response = formatSearchResponse(results, options.content); @@ -339,7 +350,7 @@ export const search: Command = new CommanderCommand("search") storeIds, pattern, parseInt(options.maxCount, 10), - { rerank: options.rerank }, + searchOptions, filters, ); response = formatAskResponse(results, options.content); diff --git a/src/lib/store.ts b/src/lib/store.ts index 656c31c..ef95bb7 100644 --- a/src/lib/store.ts +++ b/src/lib/store.ts @@ -57,6 +57,16 @@ export interface StoreInfo { }; } +/** + * Options for configuring search behavior. + */ +export interface SearchOptions { + /** Whether to rerank results for improved relevance */ + rerank?: boolean; + /** Enable agentic search to automatically refine queries and perform multiple searches */ + agentic?: boolean; +} + /** * Interface for store operations */ @@ -98,7 +108,7 @@ export interface Store { storeIds: string[], query: string, top_k?: number, - search_options?: { rerank?: boolean }, + search_options?: SearchOptions, filters?: SearchFilter, ): Promise; @@ -119,7 +129,7 @@ export interface Store { storeIds: string[], question: string, top_k?: number, - search_options?: { rerank?: boolean }, + search_options?: SearchOptions, filters?: SearchFilter, ): Promise; @@ -209,7 +219,7 @@ export class MixedbreadStore implements Store { storeIds: string[], query: string, top_k?: number, - search_options?: { rerank?: boolean }, + search_options?: SearchOptions, filters?: SearchFilter, ): Promise { const response = await this.client.stores.search({ @@ -240,7 +250,7 @@ export class MixedbreadStore implements Store { storeIds: string[], question: string, top_k?: number, - search_options?: { rerank?: boolean }, + search_options?: SearchOptions, filters?: SearchFilter, ): Promise { const response = await this.client.stores.questionAnswering({ @@ -421,7 +431,7 @@ export class TestStore implements Store { _storeIds: string[], query: string, top_k?: number, - search_options?: { rerank?: boolean }, + search_options?: SearchOptions, filters?: SearchFilter, ): Promise { const db = await this.load(); @@ -446,10 +456,13 @@ export class TestStore implements Store { const lines = file.content.split("\n"); for (let i = 0; i < lines.length; i++) { if (lines[i].toLowerCase().includes(query.toLowerCase())) { + const rerankSuffix = search_options?.rerank + ? "" + : " without reranking"; + const agenticSuffix = search_options?.agentic ? " with agentic" : ""; results.push({ type: "text", - text: - lines[i] + (search_options?.rerank ? "" : " without reranking"), + text: lines[i] + rerankSuffix + agenticSuffix, score: 1.0, metadata: file.metadata, chunk_index: results.length - 1, @@ -486,7 +499,7 @@ export class TestStore implements Store { storeIds: string[], question: string, top_k?: number, - search_options?: { rerank?: boolean }, + search_options?: SearchOptions, filters?: SearchFilter, ): Promise { const searchRes = await this.search( diff --git a/tsconfig.json b/tsconfig.json index 546eb2c..a146951 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,5 +15,7 @@ "sourceMap": true, "skipLibCheck": true, "moduleResolution": "NodeNext" - } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "bench"] }