Skip to content

Commit d9a5365

Browse files
committed
feat(glob): auto-summarize large file lists with AnalysisAgent #453
Trigger AnalysisAgent to summarize glob results when file count or character count exceeds thresholds. Update documentation with smart file search and agent collaboration guidelines. Add explicit handling for ask-agent and task-boundary tools in orchestrator.
1 parent 2e56c28 commit d9a5365

File tree

5 files changed

+239
-12
lines changed

5 files changed

+239
-12
lines changed

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/CodingAgentTemplate.kt

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,66 @@ Each tool's parameters are validated against its JSON Schema. Refer to the schem
4444
4. **Test Your Changes**: Run tests or build commands to verify changes
4545
5. **Handle Errors Gracefully**: When a tool fails, analyze the error and try alternative approaches
4646
47+
## Smart File Search Guidelines
48+
49+
When searching for files, use **specific and targeted patterns** to avoid overwhelming context:
50+
51+
**DO:**
52+
- ✅ Use specific patterns: `src/**/*.kt`, `**/test/**/*.java`, `**/config/*.yml`
53+
- ✅ Target specific directories: `/glob pattern="*.ts" path="src/main"`
54+
- ✅ Use grep with specific patterns to narrow down first
55+
- ✅ For broad exploration, use `/ask-agent` to get a summary instead
56+
57+
**DON'T:**
58+
- ❌ Avoid `**/*` or overly broad patterns (returns too many files, wastes context)
59+
- ❌ Don't glob the entire codebase without a specific goal
60+
61+
**Smart Strategy:**
62+
1. If you need to understand the project structure, use grep for specific keywords first
63+
2. Use targeted glob patterns based on what you found
64+
3. For very large result sets (100+ files), the system will automatically invoke a SummaryAgent to provide a concise overview
65+
66+
## Agent Communication & Collaboration
67+
68+
When dealing with complex information or large content, you can **communicate with specialized SubAgents** to get focused analysis:
69+
70+
**Available SubAgents:**
71+
- `analysis-agent`: Analyzes and summarizes any content (logs, file lists, code, data)
72+
- `error-agent`: Analyzes errors and provides recovery suggestions
73+
- `code-agent`: Deep codebase investigation and architectural analysis
74+
75+
**When to Use `/ask-agent`:**
76+
1. **After automatic summarization**: When a tool (like glob) triggers auto-summarization, you can ask follow-up questions
77+
```
78+
/ask-agent
79+
```json
80+
{"agentName": "analysis-agent", "question": "What are the main patterns in the file structure you analyzed?"}
81+
```
82+
```
83+
84+
2. **For specific insights**: Ask targeted questions about previously analyzed content
85+
```
86+
/ask-agent
87+
```json
88+
{"agentName": "analysis-agent", "question": "Which files are most likely related to authentication?"}
89+
```
90+
```
91+
92+
3. **To avoid re-reading large content**: If you need different perspectives on the same data
93+
```
94+
/ask-agent
95+
```json
96+
{"agentName": "analysis-agent", "question": "Can you identify the main dependencies in the files you saw?"}
97+
```
98+
```
99+
100+
**Example Workflow:**
101+
1. `/glob pattern="**/*.kt"` → Auto-triggers AnalysisAgent (returns summary)
102+
2. Review the summary, then ask: `/ask-agent` to get specific insights
103+
3. Based on insights, use targeted `/read-file` or `/grep` commands
104+
105+
This approach keeps your context efficient while getting deep insights from specialized agents!
106+
47107
## Task Progress Communication
48108
49109
For complex multi-step tasks (5+ steps), use `/task-boundary` to help users understand your progress:
@@ -163,6 +223,66 @@ ${'$'}{toolList}
163223
3. **增量更改**: 一次做一个更改并验证其有效性
164224
4. **测试更改**: 运行测试或构建命令来验证更改
165225
226+
## 智能文件搜索指南
227+
228+
搜索文件时,使用**具体且有针对性的模式**以避免上下文超载:
229+
230+
**应该做:**
231+
- ✅ 使用具体的模式:`src/**/*.kt`、`**/test/**/*.java`、`**/config/*.yml`
232+
- ✅ 针对特定目录:`/glob pattern="*.ts" path="src/main"`
233+
- ✅ 先使用 grep 配合具体模式来缩小范围
234+
- ✅ 对于广泛探索,使用 `/ask-agent` 获取摘要
235+
236+
**不应该做:**
237+
- ❌ 避免 `**/*` 或过于宽泛的模式(返回太多文件,浪费上下文)
238+
- ❌ 不要在没有明确目标的情况下 glob 整个代码库
239+
240+
**智能策略:**
241+
1. 如果需要了解项目结构,先使用 grep 搜索特定关键词
242+
2. 根据发现的内容使用有针对性的 glob 模式
243+
3. 对于非常大的结果集(100+ 文件),系统会自动调用 SummaryAgent 提供简洁概述
244+
245+
## Agent 通信与协作
246+
247+
处理复杂信息或大量内容时,你可以**与专业的 SubAgent 通信**来获取专注的分析:
248+
249+
**可用的 SubAgent:**
250+
- `analysis-agent`: 分析和总结任何内容(日志、文件列表、代码、数据)
251+
- `error-agent`: 分析错误并提供恢复建议
252+
- `code-agent`: 深度代码库调查和架构分析
253+
254+
**何时使用 `/ask-agent`:**
255+
1. **自动总结之后**: 当工具(如 glob)触发自动总结后,你可以询问后续问题
256+
```
257+
/ask-agent
258+
```json
259+
{"agentName": "analysis-agent", "question": "你分析的文件结构中有哪些主要模式?"}
260+
```
261+
```
262+
263+
2. **获取特定见解**: 就之前分析的内容提出针对性问题
264+
```
265+
/ask-agent
266+
```json
267+
{"agentName": "analysis-agent", "question": "哪些文件最可能与身份验证相关?"}
268+
```
269+
```
270+
271+
3. **避免重复读取大内容**: 需要从不同角度看待相同数据时
272+
```
273+
/ask-agent
274+
```json
275+
{"agentName": "analysis-agent", "question": "你能识别出文件中的主要依赖关系吗?"}
276+
```
277+
```
278+
279+
**示例工作流:**
280+
1. `/glob pattern="**/*.kt"` → 自动触发 AnalysisAgent(返回摘要)
281+
2. 查看摘要,然后询问:`/ask-agent` 获取特定见解
282+
3. 基于见解,使用有针对性的 `/read-file` 或 `/grep` 命令
283+
284+
这种方法既保持上下文高效,又能从专业 Agent 获得深度见解!
285+
166286
## 任务进度沟通
167287
168288
对于复杂的多步骤任务(5+ 步骤),使用 `/task-boundary` 帮助用户了解你的进度:

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/orchestrator/ToolOrchestrator.kt

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,16 @@ class ToolOrchestrator(
253253
ToolType.Glob -> executeGlobTool(tool, params, basicContext)
254254
ToolType.Grep -> executeGrepTool(tool, params, basicContext)
255255
ToolType.WebFetch -> executeWebFetchTool(tool, params, basicContext)
256+
ToolType.AskAgent -> executeAskAgentTool(tool, params, basicContext)
256257
else -> {
257-
// For new tools (task-boundary, ask-agent, etc.), use generic execution
258-
logger.debug { "Executing tool generically: $toolName" }
259-
executeGenericTool(tool, params, basicContext)
258+
// Handle special tools that need parameter conversion
259+
if (toolName == "task-boundary") {
260+
executeTaskBoundaryTool(tool, params, basicContext)
261+
} else {
262+
// For truly generic tools, use generic execution
263+
logger.debug { "Executing tool generically: $toolName" }
264+
executeGenericTool(tool, params, basicContext)
265+
}
260266
}
261267
}
262268
}
@@ -512,6 +518,52 @@ class ToolOrchestrator(
512518
return invocation.execute(context)
513519
}
514520

521+
private suspend fun executeAskAgentTool(
522+
tool: Tool,
523+
params: Map<String, Any>,
524+
context: cc.unitmesh.agent.tool.ToolExecutionContext
525+
): ToolResult {
526+
val askAgentTool = tool as cc.unitmesh.agent.tool.impl.AskAgentTool
527+
528+
val agentName = params["agentName"] as? String
529+
?: return ToolResult.Error("agentName parameter is required")
530+
val question = params["question"] as? String
531+
?: return ToolResult.Error("question parameter is required")
532+
val contextMap = params["context"] as? Map<*, *>
533+
534+
val askAgentParams = cc.unitmesh.agent.tool.impl.AskSubAgentParams(
535+
agentName = agentName,
536+
question = question,
537+
context = contextMap?.mapKeys { it.key.toString() }?.mapValues { it.value.toString() } ?: emptyMap()
538+
)
539+
540+
val invocation = askAgentTool.createInvocation(askAgentParams)
541+
return invocation.execute(context)
542+
}
543+
544+
private suspend fun executeTaskBoundaryTool(
545+
tool: Tool,
546+
params: Map<String, Any>,
547+
context: cc.unitmesh.agent.tool.ToolExecutionContext
548+
): ToolResult {
549+
val taskBoundaryTool = tool as cc.unitmesh.agent.tool.impl.TaskBoundaryTool
550+
551+
val taskName = params["taskName"] as? String
552+
?: return ToolResult.Error("taskName parameter is required")
553+
val status = params["status"] as? String
554+
?: return ToolResult.Error("status parameter is required")
555+
val summary = params["summary"] as? String ?: ""
556+
557+
val taskBoundaryParams = cc.unitmesh.agent.tool.impl.TaskBoundaryParams(
558+
taskName = taskName,
559+
status = status,
560+
summary = summary
561+
)
562+
563+
val invocation = taskBoundaryTool.createInvocation(taskBoundaryParams)
564+
return invocation.execute(context)
565+
}
566+
515567
private fun isSuccessResult(result: ToolResult): Boolean {
516568
return when (result) {
517569
is ToolResult.Success -> true

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/impl/GlobTool.kt

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,17 @@ class GlobInvocation(
8686
params: GlobParams,
8787
tool: GlobTool,
8888
private val fileSystem: ToolFileSystem,
89-
private val gitIgnoreParser: GitIgnoreParser? = null
89+
private val gitIgnoreParser: GitIgnoreParser? = null,
90+
private val analysisAgent: cc.unitmesh.agent.subagent.AnalysisAgent? = null
9091
) : BaseToolInvocation<GlobParams, ToolResult>(params, tool) {
9192

93+
companion object {
94+
// Threshold for triggering AnalysisAgent (file count)
95+
const val FILE_COUNT_THRESHOLD = 100
96+
// Threshold for triggering AnalysisAgent (character count)
97+
const val CHAR_COUNT_THRESHOLD = 10000
98+
}
99+
92100
override fun getDescription(): String {
93101
val searchPath = params.path ?: "project root"
94102
val typeDesc = if (params.includeDirectories) "files and directories" else "files"
@@ -118,7 +126,7 @@ class GlobInvocation(
118126
val limitedMatches = sortedMatches.take(params.maxResults)
119127
val resultText = formatResults(limitedMatches, matches.size)
120128

121-
val metadata = mapOf(
129+
val metadata = mutableMapOf(
122130
"pattern" to params.pattern,
123131
"search_path" to searchPath,
124132
"total_matches" to matches.size.toString(),
@@ -129,7 +137,49 @@ class GlobInvocation(
129137
"respect_gitignore" to params.respectGitIgnore.toString()
130138
)
131139

132-
ToolResult.Success(resultText, metadata)
140+
// Check if result is too long and should trigger AnalysisAgent
141+
val shouldSummarize = limitedMatches.size >= FILE_COUNT_THRESHOLD ||
142+
resultText.length >= CHAR_COUNT_THRESHOLD
143+
144+
if (shouldSummarize && analysisAgent != null) {
145+
metadata["triggered_analysis"] = "true"
146+
metadata["original_result_length"] = resultText.length.toString()
147+
148+
// Trigger AnalysisAgent to summarize the results
149+
val analysisContext = cc.unitmesh.agent.subagent.ContentHandlerContext(
150+
content = resultText,
151+
contentType = "file-list",
152+
source = "glob",
153+
metadata = metadata.mapValues { it.value.toString() }
154+
)
155+
156+
val analysisResult = analysisAgent.execute(analysisContext) { progress ->
157+
// Log progress if needed
158+
}
159+
160+
if (analysisResult.success) {
161+
// Return summarized result with reference to full list
162+
val summarizedText = buildString {
163+
appendLine("⚠️ Large file list detected (${limitedMatches.size} files, ${resultText.length} chars)")
164+
appendLine("🤖 AnalysisAgent automatically triggered to provide a summary:")
165+
appendLine()
166+
appendLine(analysisResult.content)
167+
appendLine()
168+
appendLine("💡 Tip: Use more specific glob patterns to reduce result size:")
169+
appendLine(" - Instead of: **/*")
170+
appendLine(" - Try: src/**/*.kt or **/test/**/*.java")
171+
appendLine()
172+
appendLine("📋 Full file list available in metadata if needed.")
173+
}
174+
175+
metadata["full_result"] = resultText
176+
metadata["analysis_metadata"] = analysisResult.metadata.toString()
177+
178+
return@safeExecute ToolResult.Success(summarizedText, metadata.toMap())
179+
}
180+
}
181+
182+
ToolResult.Success(resultText, metadata.toMap())
133183
}
134184
}
135185

@@ -284,12 +334,15 @@ class GlobInvocation(
284334
}
285335

286336
class GlobTool(
287-
private val fileSystem: ToolFileSystem
337+
private val fileSystem: ToolFileSystem,
338+
private val analysisAgent: cc.unitmesh.agent.subagent.AnalysisAgent? = null
288339
) : BaseExecutableTool<GlobParams, ToolResult>() {
289340

290341
override val name: String = "glob"
291342
override val description: String =
292-
"""Efficiently finds files matching specific glob patterns (e.g., `src/**/*.ts`, `**/*.md`), returning absolute paths sorted by modification time (newest first). Ideal for quickly locating files based on their name or path structure, especially in large codebases.""".trimIndent()
343+
"""Efficiently finds files matching specific glob patterns (e.g., `src/**/*.ts`, `**/*.md`), returning absolute paths sorted by modification time (newest first). Ideal for quickly locating files based on their name or path structure, especially in large codebases.
344+
345+
⚠️ IMPORTANT: Avoid overly broad patterns like `**/*` as they can return too many files and waste context. Use specific patterns instead. When results are too large (100+ files), the system automatically triggers AnalysisAgent to provide a concise summary.""".trimIndent()
293346

294347
override val metadata: ToolMetadata = ToolMetadata(
295348
displayName = "Find Files",
@@ -321,7 +374,7 @@ class GlobTool(
321374
null
322375
}
323376

324-
return GlobInvocation(params, this, fileSystem, gitIgnoreParser)
377+
return GlobInvocation(params, this, fileSystem, gitIgnoreParser, analysisAgent)
325378
}
326379

327380
private fun validateParameters(params: GlobParams) {

mpp-core/src/commonMain/kotlin/cc/unitmesh/agent/tool/registry/BuiltinToolsProvider.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ class BuiltinToolsProvider : ToolProvider {
3232

3333
// Search tools
3434
tools.add(GrepTool(dependencies.fileSystem))
35-
tools.add(GlobTool(dependencies.fileSystem))
35+
36+
// GlobTool with AnalysisAgent support for auto-summarization of large results
37+
val analysisAgent = dependencies.subAgentManager?.getSubAgent<cc.unitmesh.agent.subagent.ContentHandlerContext, cc.unitmesh.agent.tool.ToolResult.AgentResult>("analysis-agent") as? cc.unitmesh.agent.subagent.AnalysisAgent
38+
tools.add(GlobTool(dependencies.fileSystem, analysisAgent))
3639

3740
if (dependencies.shellExecutor.isAvailable()) {
3841
tools.add(ShellTool(dependencies.shellExecutor))

mpp-core/src/jsMain/kotlin/cc/unitmesh/agent/config/ToolConfigExports.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ class JsToolConfigFile(
128128
}
129129

130130
return ToolConfigFile(
131-
enabledBuiltinTools = enabledBuiltinTools.toList(),
132131
enabledMcpTools = enabledMcpTools.toList(),
133132
mcpServers = mcpServersMap,
134133
)
@@ -149,7 +148,7 @@ class JsToolConfigFile(
149148
}
150149

151150
return JsToolConfigFile(
152-
enabledBuiltinTools = config.enabledBuiltinTools.toTypedArray(),
151+
enabledBuiltinTools = emptyArray(), // Deprecated: Built-in tools are always enabled
153152
enabledMcpTools = config.enabledMcpTools.toTypedArray(),
154153
mcpServers = mcpServersJs
155154
)

0 commit comments

Comments
 (0)