Skip to content

Commit b9185c3

Browse files
committed
feat(agent): enhance CodebaseInvestigatorAgent with structured output and improved configuration
- Refactor CodebaseInvestigatorAgent to use Zod schema for structured output - Add comprehensive input/output configuration with validation - Improve model settings with better temperature and thinking budget - Update routing chain visualization to show strategy execution order - Enhance agent documentation and prompt engineering - Implement proper output processing with JSON formatting - Restructure tool configuration for better clarity and maintainability
1 parent 69c9dca commit b9185c3

3 files changed

Lines changed: 691 additions & 364 deletions

File tree

src/pages/AgentFramework.tsx

Lines changed: 270 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -405,44 +405,159 @@ if (functionCall.name === 'complete_task') {
405405
taskCompleted = true;
406406
}`;
407407

408-
const builtInAgentsCode = `// CodebaseInvestigatorAgent - 代码库探索 Agent
409-
export const CodebaseInvestigatorAgent: LocalAgentDefinition = {
408+
const builtInAgentsCode = `// CodebaseInvestigatorAgent - 使用 Zod schema 定义结构化输出
409+
const CodebaseInvestigationReportSchema = z.object({
410+
SummaryOfFindings: z.string()
411+
.describe("Investigation conclusions and insights for the main agent."),
412+
ExplorationTrace: z.array(z.string())
413+
.describe("Step-by-step list of actions and tools used."),
414+
RelevantLocations: z.array(z.object({
415+
FilePath: z.string(),
416+
Reasoning: z.string(),
417+
KeySymbols: z.array(z.string()),
418+
})).describe("Relevant files and key symbols within them."),
419+
});
420+
421+
export const CodebaseInvestigatorAgent: LocalAgentDefinition<
422+
typeof CodebaseInvestigationReportSchema
423+
> = {
410424
kind: 'local',
411-
name: 'codebase-investigator',
412-
description: 'Explores and analyzes codebases to answer questions.',
413-
promptConfig: {
414-
systemPrompt: \`You are a codebase investigator...
415-
Work systematically using available tools.
416-
When done, call complete_task with your findings.\`,
425+
name: 'codebase_investigator',
426+
displayName: 'Codebase Investigator Agent',
427+
description: \`The specialized tool for codebase analysis, architectural mapping,
428+
and understanding system-wide dependencies. Invoke for vague requests,
429+
bug root-cause analysis, or comprehensive feature implementation.\`,
430+
431+
// 输入配置
432+
inputConfig: {
433+
inputs: {
434+
objective: {
435+
description: "Comprehensive description of user's goal with context.",
436+
type: 'string',
437+
required: true,
438+
},
439+
},
440+
},
441+
442+
// 结构化输出配置(使用 Zod schema)
443+
outputConfig: {
444+
outputName: 'report',
445+
description: 'The final investigation report as a JSON object.',
446+
schema: CodebaseInvestigationReportSchema,
417447
},
448+
449+
// 输出处理函数
450+
processOutput: (output) => JSON.stringify(output, null, 2),
451+
452+
// 模型配置
418453
modelConfig: {
419-
model: 'gemini-2.0-flash', // 使用 Flash 模型
420-
temp: 1,
454+
model: DEFAULT_GEMINI_MODEL, // 根据主模型选择 Flash/Pro
455+
temp: 0.1, // 低温度,更精确
421456
top_p: 0.95,
422-
thinkingBudget: 1024,
457+
thinkingBudget: -1, // 无限制
423458
},
459+
460+
// 运行配置
424461
runConfig: {
425462
max_time_minutes: 5,
426463
max_turns: 15,
427464
},
465+
466+
// 工具配置 - 仅只读工具
428467
toolConfig: {
429-
tools: ['Read', 'Glob', 'Grep', 'Bash', 'LSP'],
430-
},
431-
inputConfig: {
432-
inputs: {
433-
query: { type: 'string', description: 'The question to investigate', required: true },
434-
},
468+
tools: [LS_TOOL_NAME, READ_FILE_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME],
435469
},
436-
};
437470
438-
// IntrospectionAgent - 自省分析 Agent
439-
export const IntrospectionAgent: LocalAgentDefinition = {
440-
kind: 'local',
441-
name: 'introspection-agent',
442-
description: 'Analyzes and reflects on conversation history.',
443-
// ...
471+
// 提示词配置
472+
promptConfig: {
473+
query: \`Your task is to do a deep investigation of the codebase...
474+
<objective>\${objective}</objective>\`,
475+
systemPrompt: \`You are **Codebase Investigator**, a hyper-specialized AI agent...
476+
Your **SOLE PURPOSE** is to build a complete mental model of the code.
477+
- **DO:** Find key modules, classes, and functions
478+
- **DO:** Understand *why* the code is written the way it is
479+
- **DO NOT:** Write the final implementation code yourself
480+
When finished, call \\\`complete_task\\\` with your structured report.\`,
481+
},
444482
};`;
445483

484+
const delegateToolCode = `// DelegateToAgentTool - 父 Agent 委托任务给子 Agent
485+
export class DelegateToAgentTool extends BaseDeclarativeTool<DelegateParams, ToolResult> {
486+
constructor(
487+
private readonly registry: AgentRegistry,
488+
private readonly config: Config,
489+
messageBus?: MessageBus,
490+
) {
491+
const definitions = registry.getAllDefinitions();
492+
493+
// 动态生成 discriminated union schema
494+
const agentSchemas = definitions.map((def) => {
495+
const inputShape: Record<string, z.ZodTypeAny> = {
496+
agent_name: z.literal(def.name).describe(def.description),
497+
};
498+
// 添加 Agent 定义的输入参数
499+
for (const [key, inputDef] of Object.entries(def.inputConfig.inputs)) {
500+
inputShape[key] = mapTypeToZod(inputDef.type);
501+
}
502+
return z.object(inputShape);
503+
});
504+
505+
const schema = z.discriminatedUnion('agent_name', agentSchemas);
506+
507+
super(
508+
'delegate_to_agent',
509+
'Delegate to Agent',
510+
registry.getToolDescription(), // 动态描述包含所有可用 Agent
511+
Kind.Think,
512+
zodToJsonSchema(schema),
513+
);
514+
}
515+
}
516+
517+
// 执行委托
518+
class DelegateInvocation extends BaseToolInvocation<DelegateParams, ToolResult> {
519+
async execute(signal: AbortSignal): Promise<ToolResult> {
520+
const definition = this.registry.getDefinition(this.params.agent_name);
521+
522+
// 使用 SubagentToolWrapper 创建实际执行
523+
const wrapper = new SubagentToolWrapper(definition, this.config);
524+
const { agent_name, ...agentArgs } = this.params;
525+
const invocation = wrapper.build(agentArgs);
526+
527+
return invocation.execute(signal);
528+
}
529+
}`;
530+
531+
const activityEventCode = `// SubagentActivityEvent - Agent 执行过程中的活动事件
532+
export interface SubagentActivityEvent {
533+
isSubagentActivityEvent: true;
534+
agentName: string;
535+
type: 'TOOL_CALL_START' | 'TOOL_CALL_END' | 'THOUGHT_CHUNK' | 'ERROR';
536+
data: Record<string, unknown>;
537+
}
538+
539+
// 在 LocalAgentExecutor 中发射事件
540+
private emitActivity(
541+
type: SubagentActivityEvent['type'],
542+
data: Record<string, unknown>,
543+
): void {
544+
if (this.onActivity) {
545+
const event: SubagentActivityEvent = {
546+
isSubagentActivityEvent: true,
547+
agentName: this.definition.name,
548+
type,
549+
data,
550+
};
551+
this.onActivity(event);
552+
}
553+
}
554+
555+
// 使用示例
556+
this.emitActivity('TOOL_CALL_START', { name: functionCall.name, args });
557+
this.emitActivity('THOUGHT_CHUNK', { text: subject });
558+
this.emitActivity('TOOL_CALL_END', { name: functionCall.name, output: result });
559+
this.emitActivity('ERROR', { error: errorMessage, context: 'tool_call' });`;
560+
446561
return (
447562
<div className="space-y-8">
448563
<div>
@@ -640,18 +755,19 @@ export const IntrospectionAgent: LocalAgentDefinition = {
640755
{/* 内置 Agent */}
641756
<Layer title="内置 Agent">
642757
<p className="text-[var(--text-secondary)] mb-4">
643-
Gemini CLI 内置了两个常用 Agent,可通过设置启用:
758+
Gemini CLI 内置了两个常用 Agent,可通过设置启用。
759+
注意 CodebaseInvestigator 使用 <strong>Zod schema</strong> 定义结构化输出:
644760
</p>
645761

646-
<CodeBlock code={builtInAgentsCode} language="typescript" title="内置 Agent 定义" />
762+
<CodeBlock code={builtInAgentsCode} language="typescript" title="codebase-investigator.ts - 使用 Zod Schema" />
647763

648764
<div className="mt-6 grid grid-cols-1 md:grid-cols-2 gap-4">
649765
<HighlightBox title="CodebaseInvestigator" variant="blue">
650766
<p className="text-sm mb-2">代码库探索和分析 Agent</p>
651767
<ul className="text-sm space-y-1 text-[var(--text-muted)]">
652-
<li>探索代码结构和实现</li>
653-
<li>使用 Flash 模型(快速)</li>
654-
<li>工具: Read, Glob, Grep, Bash, LSP</li>
768+
<li><strong>结构化输出</strong>: Zod schema 验证</li>
769+
<li><strong>只读工具</strong>: ls, Read, Glob, Grep</li>
770+
<li><strong>Scratchpad</strong>: 系统化探索方法</li>
655771
<li>• 最多 15 轮,5 分钟超时</li>
656772
</ul>
657773
</HighlightBox>
@@ -667,6 +783,54 @@ export const IntrospectionAgent: LocalAgentDefinition = {
667783
</div>
668784
</Layer>
669785

786+
{/* delegate_to_agent 工具 */}
787+
<Layer title="delegate_to_agent 委托工具">
788+
<p className="text-[var(--text-secondary)] mb-4">
789+
父 Agent 通过 <code className="text-[var(--cyber-blue)]">delegate_to_agent</code> 工具
790+
将任务委托给子 Agent。该工具动态生成 <strong>discriminated union schema</strong>
791+
根据 agent_name 参数路由到不同的 Agent。
792+
</p>
793+
794+
<CodeBlock code={delegateToolCode} language="typescript" title="delegate-to-agent-tool.ts" />
795+
796+
<HighlightBox title="工作原理" variant="blue" className="mt-4">
797+
<ol className="text-sm space-y-2 list-decimal list-inside">
798+
<li>从 AgentRegistry 获取所有已注册的 Agent 定义</li>
799+
<li>为每个 Agent 生成 Zod schema(包含 agent_name 和其 inputConfig)</li>
800+
<li>使用 <code>z.discriminatedUnion</code> 合并为统一的工具 schema</li>
801+
<li>执行时根据 agent_name 查找定义,通过 SubagentToolWrapper 创建执行</li>
802+
</ol>
803+
</HighlightBox>
804+
</Layer>
805+
806+
{/* 活动事件 */}
807+
<Layer title="SubagentActivityEvent 活动事件">
808+
<p className="text-[var(--text-secondary)] mb-4">
809+
Agent 执行过程中会发射活动事件,用于 UI 展示和监控。支持 4 种事件类型:
810+
</p>
811+
812+
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-6">
813+
<div className="bg-[var(--bg-card)] rounded-lg p-3 text-center border border-[var(--cyber-blue)]/30">
814+
<div className="text-lg font-bold text-[var(--cyber-blue)]">TOOL_CALL_START</div>
815+
<div className="text-xs text-[var(--text-muted)]">工具调用开始</div>
816+
</div>
817+
<div className="bg-[var(--bg-card)] rounded-lg p-3 text-center border border-[var(--terminal-green)]/30">
818+
<div className="text-lg font-bold text-[var(--terminal-green)]">TOOL_CALL_END</div>
819+
<div className="text-xs text-[var(--text-muted)]">工具调用结束</div>
820+
</div>
821+
<div className="bg-[var(--bg-card)] rounded-lg p-3 text-center border border-[var(--purple)]/30">
822+
<div className="text-lg font-bold text-[var(--purple)]">THOUGHT_CHUNK</div>
823+
<div className="text-xs text-[var(--text-muted)]">思考片段</div>
824+
</div>
825+
<div className="bg-[var(--bg-card)] rounded-lg p-3 text-center border border-red-500/30">
826+
<div className="text-lg font-bold text-red-400">ERROR</div>
827+
<div className="text-xs text-[var(--text-muted)]">错误事件</div>
828+
</div>
829+
</div>
830+
831+
<CodeBlock code={activityEventCode} language="typescript" title="types.ts - 活动事件" />
832+
</Layer>
833+
670834
{/* 终止模式 */}
671835
<Layer title="终止模式">
672836
<p className="text-[var(--text-secondary)] mb-4">
@@ -751,6 +915,83 @@ export const IntrospectionAgent: LocalAgentDefinition = {
751915
</div>
752916
</Layer>
753917

918+
{/* 关键文件与入口 */}
919+
<Layer title="关键文件与入口">
920+
<div className="overflow-x-auto">
921+
<table className="w-full text-sm">
922+
<thead>
923+
<tr className="border-b border-[var(--border-subtle)]">
924+
<th className="text-left py-2 px-3 text-[var(--text-muted)]">文件</th>
925+
<th className="text-left py-2 px-3 text-[var(--text-muted)]">职责</th>
926+
</tr>
927+
</thead>
928+
<tbody>
929+
<tr className="border-b border-[var(--border-subtle)]/50">
930+
<td className="py-2 px-3 font-mono text-[var(--cyber-blue)] text-xs">agents/types.ts</td>
931+
<td className="py-2 px-3 text-[var(--text-secondary)]">核心类型定义:AgentTerminateMode、BaseAgentDefinition、LocalAgentDefinition、RemoteAgentDefinition</td>
932+
</tr>
933+
<tr className="border-b border-[var(--border-subtle)]/50">
934+
<td className="py-2 px-3 font-mono text-[var(--cyber-blue)] text-xs">agents/registry.ts</td>
935+
<td className="py-2 px-3 text-[var(--text-secondary)]">AgentRegistry - Agent 注册、发现和管理</td>
936+
</tr>
937+
<tr className="border-b border-[var(--border-subtle)]/50">
938+
<td className="py-2 px-3 font-mono text-[var(--cyber-blue)] text-xs">agents/local-executor.ts</td>
939+
<td className="py-2 px-3 text-[var(--text-secondary)]">LocalAgentExecutor - 本地 Agent 执行循环</td>
940+
</tr>
941+
<tr className="border-b border-[var(--border-subtle)]/50">
942+
<td className="py-2 px-3 font-mono text-[var(--cyber-blue)] text-xs">agents/toml-loader.ts</td>
943+
<td className="py-2 px-3 text-[var(--text-secondary)]">TOML 配置解析和 Zod 验证</td>
944+
</tr>
945+
<tr className="border-b border-[var(--border-subtle)]/50">
946+
<td className="py-2 px-3 font-mono text-[var(--cyber-blue)] text-xs">agents/delegate-to-agent-tool.ts</td>
947+
<td className="py-2 px-3 text-[var(--text-secondary)]">DelegateToAgentTool - 委托工具实现</td>
948+
</tr>
949+
<tr className="border-b border-[var(--border-subtle)]/50">
950+
<td className="py-2 px-3 font-mono text-[var(--cyber-blue)] text-xs">agents/subagent-tool-wrapper.ts</td>
951+
<td className="py-2 px-3 text-[var(--text-secondary)]">SubagentToolWrapper - 统一 Local/Remote 调用</td>
952+
</tr>
953+
<tr className="border-b border-[var(--border-subtle)]/50">
954+
<td className="py-2 px-3 font-mono text-[var(--cyber-blue)] text-xs">agents/codebase-investigator.ts</td>
955+
<td className="py-2 px-3 text-[var(--text-secondary)]">CodebaseInvestigatorAgent 内置定义</td>
956+
</tr>
957+
<tr className="border-b border-[var(--border-subtle)]/50">
958+
<td className="py-2 px-3 font-mono text-[var(--cyber-blue)] text-xs">agents/a2a-client-manager.ts</td>
959+
<td className="py-2 px-3 text-[var(--text-secondary)]">A2AClientManager - 远程 Agent 通信</td>
960+
</tr>
961+
</tbody>
962+
</table>
963+
</div>
964+
</Layer>
965+
966+
{/* 设计决策 */}
967+
<Layer title="设计决策">
968+
<div className="space-y-4">
969+
<HighlightBox title="为什么使用 Zod Schema?" variant="blue">
970+
<p className="text-sm">
971+
<strong>结构化输出验证</strong>:Agent 返回的结果通过 Zod schema 验证,
972+
确保输出符合预期格式。如果验证失败,Agent 会被要求重试,
973+
避免返回格式错误的结果给父 Agent。
974+
</p>
975+
</HighlightBox>
976+
977+
<HighlightBox title="为什么 Agent 不能调用 delegate_to_agent?" variant="purple">
978+
<p className="text-sm">
979+
<strong>防止递归和复杂性</strong>:TOML 加载器会验证 tools 配置,
980+
拒绝包含 delegate_to_agent 的 Agent 定义。这防止了 Agent 之间的递归调用,
981+
简化了执行模型和调试。
982+
</p>
983+
</HighlightBox>
984+
985+
<HighlightBox title="为什么有 60 秒恢复期?" variant="green">
986+
<p className="text-sm">
987+
<strong>优雅降级</strong>:当 Agent 因超时或达到轮次上限时,
988+
给予 60 秒的恢复期让它调用 complete_task。这比直接终止更友好,
989+
允许 Agent 返回部分结果而不是完全失败。
990+
</p>
991+
</HighlightBox>
992+
</div>
993+
</Layer>
994+
754995
<RelatedPages pages={relatedPages} />
755996
</div>
756997
);

0 commit comments

Comments
 (0)