news 2026/6/10 20:18:21

C# 实现简版 Claude Code | 子代理与上下文隔离(4)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C# 实现简版 Claude Code | 子代理与上下文隔离(4)

该系列文章基于github.com/shareAI-lab/learn-claude-code写就,该仓库以大道至简的风格剖析了Claude Code的核心原理,值得大家学习。由于该仓库是基于Python语言,为方便.NET开发者学习,我已经将代码基于.NET 10的dotnet file重写,源码已上传至github,源码地址见文末。

v3: 子代理机制 - 上下文隔离的艺术

本文是 Learn Claude Code (C# 版) 系列的第四篇,对应代码文件v3_subagent.cs

问题:上下文污染

v2 添加了规划能力。但对于大型任务:

You: 探索代码库的架构,然后重构 auth 模块

单 Agent 的执行过程:

[探索中...] cat src/Services/AuthService.cs -> 500 行 [探索中...] cat src/Models/User.cs -> 300 行 [探索中...] cat src/Controllers/AuthController.cs -> 400 行 ... 15 个文件后 ... [现在重构...] "等等,AuthService 里那个方法签名是什么来着?"

模型的上下文被探索细节填满了。当真正开始重构时:

  • 关键代码片段已经"滚出"上下文窗口

  • 模型需要重新读取文件

  • 效率低下,成本增加

这就是上下文污染——前置任务的输出污染了后续任务的工作空间。

解决方案:子代理

主 Agent 历史: [Task: 探索代码库] -> 子代理探索 20 个文件(在自己的上下文中) -> 只返回: "Auth 在 src/Services/AuthService.cs,依赖 User, Token 模型..." [现在用干净上下文重构]

进程隔离 = 上下文隔离

每个子代理有:

  1. 自己的干净消息历史

  2. 过滤后的工具集

  3. 专门的系统提示

  4. 只向父代理返回最终摘要

Agent 类型注册表

record AgentConfig(string Description, string[] Tools, string Prompt); var agentTypes = new Dictionary<string, AgentConfig> { ["explore"] = new( "只读代理,用于探索代码、查找文件、搜索", ["bash", "read_file"], // 没有写权限! "你是一个探索代理。搜索和分析,但不要修改文件。返回简洁的摘要。" ), ["code"] = new( "完整代理,用于实现功能和修复 bug", ["*"], // 所有工具 "你是一个编程代理。高效地实现请求的更改。" ), ["plan"] = new( "规划代理,用于设计实现策略", ["bash", "read_file"], // 只读 "你是一个规划代理。分析代码库并输出编号的实现计划。不要做更改。" ) };

类型

工具

用途

explore

bash, read_file

安全探索,不会意外修改

code

全部

实际实现

plan

bash, read_file

设计方案,不执行

Task 工具定义

var taskTool = new Tool { Name = "Task", Description = $""" 为专注的子任务生成子代理。 子代理在隔离上下文中运行 - 它们看不到父代理的历史。 用这个来保持主对话干净。 Agent 类型: - explore: 只读探索 - code: 完整实现 - plan: 设计策略 示例: - Task(explore): "查找所有使用 auth 模块的文件" - Task(plan): "设计数据库迁移策略" - Task(code): "实现用户注册表单" """, InputSchema = new InputSchema { Type = "object", Properties = new Dictionary<string, JsonElement> { ["description"] = ..., // 简短描述,3-5 词 ["prompt"] = ..., // 详细指令 ["agent_type"] = ... // explore | code | plan }, Required = ["description", "prompt", "agent_type"] } };

子代理执行核心

async Task<string> RunTaskAsync(string description, string prompt, string agentType) { var config = agentTypes[agentType]; // 1. 专门的系统提示 var subSystem = $""" 你是一个位于 {workDir} 的 {agentType} 子代理。 {config.Prompt} 完成任务并返回清晰简洁的摘要。 """; // 2. 过滤后的工具集 var subTools = GetToolsForAgent(agentType); // 3. 关键:隔离的消息历史! var subMessages = new List<Message> { prompt.AsUserMessage() }; // 4. 运行相同的 Agent 循环 while (true) { var response = await client.CreateMessageAsync( modelId, subMessages, new MessageParameters { System = subSystem, Tools = subTools }); if (response.StopReason != StopReason.ToolUse) { // 5. 只返回最终文本 return ExtractText(response); } // 执行工具,添加结果... } }

关键点:

  1. 隔离的subMessages- 不包含父代理的历史

  2. 过滤的工具- explore 不能写文件

  3. 专门的系统提示- 引导子代理行为

  4. 只返回摘要- 父代理不看中间细节

进度显示

Console.WriteLine($" [{agentType}] {description}"); var start = DateTime.Now; var toolCount = 0; // 执行中... Console.Write($"\r [{agentType}] {description} ... {toolCount} tools, {elapsed:F1}s"); // 完成 Console.WriteLine($"\r [{agentType}] {description} - done ({toolCount} tools, {elapsed:F1}s)");

输出效果:

> Task: 探索 auth 架构 [explore] 探索 auth 架构 ... 5 tools, 2.3s [explore] 探索 auth 架构 - done (8 tools, 4.1s)

用户看到子代理在工作,但不会被中间输出淹没。

典型工作流程

You: 重构 auth 模块使用 JWT 让我先了解现有架构: > Task: 探索 auth 架构 [explore] 探索 auth 架构 - done (12 tools, 5.2s) 摘要:Auth 模块位于 src/Services/AuthService.cs,使用 Session 认证。 依赖:User 模型、TokenRepository。主要方法:Login(), Logout(), ValidateToken()。 接下来设计迁移方案: > Task: 设计 JWT 迁移 [plan] 设计 JWT 迁移 - done (5 tools, 3.1s) 迁移计划: 1. 添加 JWT 依赖包 2. 创建 JwtService 类 3. 修改 AuthService 使用 JwtService 4. 更新 TokenRepository 5. 添加配置项 现在执行实现: > Task: 实现 JWT 认证 [code] 实现 JWT 认证 - done (15 tools, 12.3s) 完成:创建了 JwtService.cs,修改了 AuthService.cs, 更新了 appsettings.json,添加了 3 个单元测试。 任务完成!主要改动: - 新增 JwtService 处理 token 生成和验证 - AuthService 现在使用 JwtService 而非 Session - 配置文件新增 Jwt 配置节

深层洞察

分治策略

子代理本质上是分治法的应用:

  • :把大任务分解为独立子任务

  • :每个子代理独立解决其任务

  • :父代理整合子代理的摘要

上下文是稀缺资源

Claude 的上下文窗口有限(128K-200K tokens)。如何使用这个有限资源?

策略

效果

全部放主 Agent

快速溢出,早期信息丢失

子代理隔离

主 Agent 保持干净,按需获取摘要

子代理是上下文管理的工具。

工具权限控制

为什么 explore 不能写文件?

  1. 安全- 探索时意外修改很危险

  2. 专注- 强制子代理只做一件事

  3. 可预测- 你知道 explore 任务不会产生副作用

这是最小权限原则的应用。

v0 的递归 vs v3 的子代理

v0 用 bash 调用自身实现子代理:

dotnet run v0_bash_agent.cs "探索 src/"

v3 用显式的 Task 工具:

Task(explore, "探索 src/")

区别:

  • v0 更"纯粹"——一切通过 bash

  • v3 更"可控"——明确的类型和权限

防止无限递归

注意 v3 的设计:

List<Tool> GetToolsForAgent(string agentType) { var allowed = agentTypes[agentType].Tools; if (allowed.Contains("*")) return baseTools; // 不包括 Task 工具! return baseTools.Where(t => allowed.Contains(t.Name)).ToList(); }

子代理没有Task工具——不能再生成子代理。这防止了无限递归。

(生产系统可能允许有限深度的嵌套,但需要仔细控制。)

运行示例

Mini Claude Code v3 (with Subagents) - /path/to/project Agent 类型: explore, code, plan 输入 'exit' 退出。 You: 分析这个项目的架构并给出改进建议 让我先探索项目结构: > Task: 分析项目结构 [explore] 分析项目结构 - done (8 tools, 3.5s) 项目是一个 ASP.NET Core Web API: - Controllers/: 3 个控制器 - Services/: 5 个服务类 - Models/: 8 个模型 - Data/: EF Core DbContext 潜在问题:Services 直接使用 DbContext,没有 Repository 层。 接下来让我设计改进方案: > Task: 设计 Repository 模式 [plan] 设计 Repository 模式 - done (4 tools, 2.1s) 改进计划: 1. 创建 IRepository<T> 接口 2. 实现 GenericRepository<T> 3. 为每个实体创建具体 Repository 4. 修改 Services 使用 Repository 而非 DbContext 5. 更新 DI 配置 基于分析,我的改进建议是: 1. 引入 Repository 层分离数据访问 2. Services 应该依赖接口而非实现 3. 考虑添加 Unit of Work 模式管理事务 需要我实现这些改进吗?

从 v3 到 v4

v3 解决了上下文隔离问题。但还有一个挑战:

You: 处理这个 PDF 文件 You: 构建一个 MCP 服务器

模型怎么知道:

  • 处理 PDF 应该用pdftotext还是PyMuPDF

  • MCP 服务器需要什么协议规范?

这些是领域知识,不是工具能力。v4 将引入Skills 机制——按需加载专业知识。

总结

v3 的哲学:

分而治之,上下文隔离。干净上下文 = 更好结果。

子代理不是新概念,但用得好能显著提升 Agent 的能力边界。关键是理解:上下文是稀缺资源,需要精心管理。

点击阅读原文,获取仓库地址:👇👇👇

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 7:57:35

UNet人脸融合效果展示:轻微美化vs深度换脸对比

UNet人脸融合效果展示&#xff1a;轻微美化vs深度换脸对比 1. 为什么人脸融合需要“分度量”看待&#xff1f; 你有没有试过把一张明星脸换到自己的自拍照上&#xff1f;结果要么像戴了张僵硬面具&#xff0c;要么整张脸糊成一团马赛克。问题不在于技术不行&#xff0c;而在于…

作者头像 李华
网站建设 2026/6/10 7:56:49

Qwen3-ASR-0.6B保姆级教程:零配置镜像部署中英文混合语音识别系统

Qwen3-ASR-0.6B保姆级教程&#xff1a;零配置镜像部署中英文混合语音识别系统 1. 为什么你需要一个本地语音识别工具&#xff1f; 你有没有过这些时刻&#xff1f; 会议录音堆在文件夹里&#xff0c;想整理成文字却要上传到网页、等转写、再下载——结果发现隐私条款写着“音…

作者头像 李华
网站建设 2026/6/10 7:54:31

AI读脸术能否集成到APP?移动端调用API实战教程

AI读脸术能否集成到APP&#xff1f;移动端调用API实战教程 1. 为什么“读脸”能力值得放进你的APP里&#xff1f; 你有没有遇到过这些场景&#xff1a; 社交App想根据用户年龄自动推荐内容&#xff0c;但只能靠手动填写生日&#xff0c;准确率低得可怜&#xff1b;线下门店的…

作者头像 李华
网站建设 2026/6/10 7:59:03

小白必看!Open-AutoGLM手机AI代理部署避坑指南

小白必看&#xff01;Open-AutoGLM手机AI代理部署避坑指南 你有没有想过&#xff0c;让AI替你点外卖、刷短视频、查航班、填表单&#xff1f;不是靠写代码&#xff0c;而是直接说一句&#xff1a;“打开小红书搜最近的咖啡馆”&#xff0c;手机就自动完成整套操作——点击App、…

作者头像 李华
网站建设 2026/6/9 19:55:18

如何用virtual serial port driver实现虚拟串口对联调试

虚拟串口对联调试实战手册:从驱动原理到嵌入式协议验证的完整闭环 你有没有遇到过这样的场景? 凌晨两点,STM32固件在Modbus RTU通信中偶发丢帧,但示波器抓不到异常——因为问题只出现在特定负载下; CI流水线里Python测试脚本反复报 SerialException: could not open po…

作者头像 李华
网站建设 2026/6/10 8:00:53

一位全加器在ALU中的集成方式:图解说明

一位全加器:ALU里那个从不抢镜、却决定一切的“沉默执行者” 你写过 ADD R0, R1, R2 吗? 在RISC-V汇编里敲下这行指令时,你不会想到——真正干活的,不是什么高大上的超前进位电路,而是一个只有5个端口、不到25个晶体管、连名字都朴素得近乎透明的模块: 一位全加器(…

作者头像 李华