@@ -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