ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

一个 AI 干不完的活,WES Code 让它派分身去做

一个 AI 干不完的活,WES Code 让它派分身去做

目录

    • 什么时候派、怎么派
    • 给分身划好边界
    • 干完了,交回什么
    • 写在最后

有些活,一个人做太慢:一次要查清三个模块的调用关系,或者把一批文件逐个审一遍。让 AI 一件件做,也是一样的慢。

WES Code 的 AI 可以把这类活拆开,派出几个子 Agent 分头去做。每个子 Agent 是一个临时的分身,在自己的环境里独立干活,干完把结论交回来,主 Agent 再汇总。

什么时候派、怎么派

什么时候会派。派不派,由 AI 自己判断。这个工具的使用说明里写了三种适合派的情况:

  • 有个子任务比较复杂,需要单独用一套工具,或者换一种专长来做
  • 有几件事互不相干,可以放到后台同时跑
  • 已经注册了一个更合适的专用 Agent

简单的事它会自己直接做,不会为了一个小活专门派人。

子 Agent 是独立运行的,看不到主对话的历史。所以派活时,任务说明要写得完整、自成一体,参数说明里也专门提醒了这一点。

派活的几种方式。派一个子 Agent,最基本的是两个参数:prompt写清要它做什么,description写一句不超过 80 个字的简短说明,显示在界面和日志里。在这个基础上,还有几种派法:

方式怎么用
指定专用 Agent用subagent_type指定一个注册好的 Agent;不指定,就复制一份当前 Agent 的配置
放到后台跑run_in_background设为 true,立刻拿到一个task_id,主 Agent 接着干别的,之后凭task_id查结果
一次派一批用tasks一次给多个任务,最多同时跑 3 个,结果一起交回
换个模型用model给这个子 Agent 单独指定模型

专用 Agent 不只是内置的那几个。在 WES Code 的 Agent 编辑器里,你可以自己建一个:名字、描述、系统提示词、用哪个模型、能用哪些技能,甚至头像都能设。建好的 Agent 和预设的一样,登记在引擎的 Agent 注册表里,派活时用subagent_type指名就行。

子 Agent 用哪个模型,按这个顺序定:这次派活单独指定的,优先;其次是专用 Agent 自己配置的;都没有,就用主 Agent 正在用的那个,也就是你在对话里选的模型。

给分身划好边界

分身多了,最要紧的是管住它们能做什么。派活时,可以给每个子 Agent 划三道边界:

  • read_only:只读。写文件、改文件、打补丁、跑命令统统禁掉,适合分析和审查
  • tools_allow:只给指定的工具,比如只给 read、grep、glob,做一个纯粹的调研员。它只会在主 Agent 已有的工具里收窄,不会多出主 Agent 没有的工具
  • work_scope:只让它在某个子目录里干活,比如src/auth

另外还有一道默认的边界:子 Agent 默认是「叶子」角色,不能再往下派分身,也不能直接弹卡片问你,问题统一由主 Agent 来问。需要它继续往下分派时,可以设成「编排者」角色。指定专用 Agent 时,它的权限还会再按主 Agent 的权限收紧一遍,分身的权限不会超过派它的人。

我把引擎给子 Agent 定权限的那段规则节选出来,喂了 4 种派活方式,看每个分身最后能用什么:

// 给子 Agent 定权限:照 runner.go 里 delegate 的那一段写,只保留角色和这次派活自带的限制funcchildPolicy(task engine.DelegationTask,params engine.RunParams,parentWorkDirstring)engine.RunParams{varroleDeny[]stringswitchtask.Role{caseengine.DelegationRoleOrchestrator:roleDeny=[]string{"ask_user"}default:// leaf — childMustDenyTools = {agent, ask_user}roleDeny=append([]string(nil),childMustDenyTools...)}params.ToolDeny=mergeUnique(params.ToolDeny,roleDeny)iftask.ReadOnly{params.ToolDeny=mergeUnique(params.ToolDeny,[]string{"write","edit","apply_patch","exec",})params.WorkspaceAccess="ro"}iflen(task.ToolsAllow)>0{iflen(params.ToolAllow)==0{params.ToolAllow=task.ToolsAllow}else{params.ToolAllow=intersect(params.ToolAllow,task.ToolsAllow)}}iftask.WorkScope!=""{scopedDir:=filepath.Join(parentWorkDir,task.WorkScope)params.WorkDir=scopedDir params.AllowPaths=[]string{scopedDir}}returnparams}// 4 种派活方式cases:=[]struct{namestringtask engine.DelegationTask}{{"1 默认派一个子 Agent",engine.DelegationTask{}},{"2 派一个编排者",engine.DelegationTask{Role:engine.DelegationRoleOrchestrator}},{"3 只读调研员",engine.DelegationTask{ReadOnly:true,ToolsAllow:[]string{"read","grep","glob"}}},{"4 只管 src/auth",engine.DelegationTask{WorkScope:"src/auth"}},}

运行结果:

1 默认派一个子 Agent 禁用:[agent ask_user] 只允许:[] 工作目录:/home/dev/shop 访问:rw(默认) 2 派一个编排者 禁用:[ask_user] 只允许:[] 工作目录:/home/dev/shop 访问:rw(默认) 3 只读调研员 禁用:[agent ask_user write edit apply_patch exec] 只允许:[read grep glob] 工作目录:/home/dev/shop 访问:ro 4 只管 src/auth 禁用:[agent ask_user] 只允许:[] 工作目录:/home/dev/shop/src/auth 访问:rw(默认)

「只允许」是空的,表示沿用主 Agent 的全部工具。只读调研员那一行最能说明问题:禁掉了 6 个工具,只剩 read、grep、glob 三个,访问方式也变成了只读。

干完了,交回什么

子 Agent 干完,交回的不只是一段结论。结论后面还附着一份改动报告:改了哪些文件,每个工具用了几次,一共调了多少次工具。上面测试的最后一段,就是这份报告的样子:

[side-effects] Files modified: src/auth/login.go, src/auth/login_test.go Tools used: read:6, edit:3, exec:5 Total tool calls: 14

主 Agent 看一眼就知道分身动了哪里。子 Agent 改过的文件,也会同步记到主 Agent 名下,后面要回头核对时不会漏掉。

一次派一批的时候,交回的是一份汇总:每个任务的说明、状态、结果和用了多久,一个都不少。

同时能派多少,也有默认的护栏:

  • 同时运行的 Agent 最多 10 个,每个 Agent 最多带 8 个子 Agent
  • 分身最多往下派 3 层,不会无限地层层分派下去
  • 单个子 Agent 忙着跑工具时最多 20 分钟,两轮之间闲着时最多 5 分钟

写在最后

把活拆给几个分身,难的从来不是「派出去」,而是「管得住」:每个分身能用什么、能碰哪里、做完了改了什么,都要清清楚楚。WES Code 把这些都做进了派活这一步。下次遇到一大批相互独立的活,不妨直接跟 AI 说一句:「分头去做」。

文中的子 Agent 是 WES Code 里的功能,官网是 weisyn.com。你平时会让 AI 一次做一件事,还是让它分头并行,欢迎评论区聊聊。

觉得有用的朋友,欢迎点赞、收藏、关注,后面会继续分享 AI 编程的实战经验。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进