PentAGI 把长任务拆成三层嵌套循环,用每轮落库实现断点续跑、用动态重排替代静态计划,这套骨架可以直接搬到任何要连续跑几小时的 Agent 系统里。它不是靠一个更聪明的提示词把模型哄住,而是假设模型一定会犯错、进程一定会重启、上下文一定会超窗,然后为每一种失败都准备了明确的退路。读它的源码,比读任何一篇 Agent 框架介绍都更能看清「编排」两个字到底要写多少代码。
口径:源码为 vxcontrol/pentagi 的 commit cffed94(2026-10-23),模板行数按文件逐行统计;循环阈值取自 providers/performer.go 常量区。本文只做静态源码分析,未重启服务做运行时验证。
它解决的是长任务不中断,不是模型聊得更好
一个渗透测试任务可能连续跑几小时,中间要调用几十次终端命令、网页抓取和外部搜索。把这件事交给大模型,最先崩掉的不是推理能力,而是工程假设:进程会重启、模型会只回一段话而不调工具、消息链会超出上下文窗口、用户会在跑到一半时插一句新要求。用内存里的一个 for 循环扛这些,等于把状态全押在进程活着这件事上。
PentAGI 的解法是把状态全部外置。每轮模型回复、每个工具结果都写回 PostgreSQL 的消息链表,进程启动时先扫描未完成的任务继续跑;出错不判死,统一回到等待人接手的状态;模型输出格式不规范时,有专门的反思角色把它拽回结构化工具调用。三个决策都不新鲜,但放在同一套代码里互相咬合,才是值得拆的地方。
代价也很直接:规划、重规划、反思、纠错、摘要都要额外调模型。它用确定的调用成本换不中断,这个取舍适合长任务,不适合短问答。
几个可以直接核对的数字
| 指标 | 数值 | 说明 |
|---|---|---|
| 提示词模板 | 39 个 | 源码目录 prompts 下的模板文件数 |
| 提示词总行数 | 4419 行 | 按文件逐行统计,非字符数 |
| 主类 agent 轮数上限 | 100 轮 | 调度者、助手与三类执行角色 |
| 其余专家轮数上限 | 20 轮 | 搜索、记忆、顾问等辅助角色 |
| 收尾预留轮数 | 3 轮 | 最后三轮不再调模型,转为写结论 |
三层循环:谁管人、谁管计划、谁管这一轮
最外层是会话循环,一个任务流一个常驻协程,从输入通道收用户的话。它负责状态机、停止、报告和容器资源,启动时先把上次没跑完的子任务续起来。这一层决定了人什么时候能插话,以及插话之后是接着当前任务跑还是新建一个任务。
中间层是任务循环。用户每说一句话就产生一个任务,模型先把目标拆成一串子任务,之后每跑完一个子任务,再让模型根据已完成结果重排剩下的步骤——先把未开始的子任务删掉再重建。这不是静态 DAG,是带条件边的动态计划。
最内层才是通常说的 agentloop,位于 providers/performer.go:一个无限 for,每轮问一次模型、执行它给出的工具、把结果回填,再进入下一轮。三层各自管一件事,谁都不越界,这是整套代码最好读的地方。
三层职责对照
| 层 | 代码位置 | 循环内容 | 退出条件 |
|---|---|---|---|
| 外层会话 | controller/flow.go:1288 | 常驻收输入,先续跑未完成任务 | 上下文取消或用户停止 |
| 中层任务 | controller/task.go:341 | 取子任务、执行、重排剩余计划 | 队列清空后写报告 |
| 内层回合 | providers/performer.go:58 | 问模型、执行工具、回填结果 | 终止工具、等待输入或撞上限 |
三层分别对应「人何时插话」「计划怎么改」「这一轮做什么」。
背景:容易看漏的一点:coder、pentester、advice、memorist、search 这些不是外部系统工具,而是「派给另一个 agent」的调用。调度者用一次工具调用把活分出去,专家各自跑一条独立消息链,结果当成工具响应回填。所谓多 agent 是这么实现的,不是同一个大模型换几顶帽子。
内层一轮的八步与三道护栏
单轮顺序很规整:轮首按窗口压缩消息链;带重试调用模型,最多三次、间隔五秒;如果模型没给出任何工具调用,交给反思角色纠偏;把模型消息追加进链并写库;逐个执行工具;把工具结果作为工具消息追加并再次写库;命中终止工具就跳出;轮尾再做一次链压缩,失败只记告警不中断。
护栏挂在三处。第一处是重复检测:连续重复调用同一工具先软提示,超过阈值直接中止链,避免模型在错误上来回打转。第二处是参数修复:工具执行失败时按 JSON 结构让模型改参数再试,但如果是任务状态不允许这类非参数错误,立刻把错误交回模型,不白烧重试次数。第三处是执行导师:调用次数到阈值时请另一个模型复查进度,把建议拼回本次工具响应。
退出条件只有四条:调到终止工具、调到向人提问的工具、撞上轮数上限、或者出错。出错统一回到等待状态,代码注释里写得很直白:即便发生错误,也要把任务流设为等待新输入。这也解释了界面上偶尔看到任务停住不动——多数时候不是卡死,是在等人。
循环阈值与来源
| 约束 | 取值 | 代码出处 |
|---|---|---|
| 主类角色轮数上限 | 100 | performAgentChain 上限分支 |
| 辅助角色轮数上限 | 20 | 同一分支的默认值 |
| 收尾预留 | 最后 3 轮 | 接近上限时改走反思收尾 |
| 模型调用重试 | 3 次 / 5 秒 | callWithRetries |
| 工具执行重试 | 3 次 | execToolCall |
| 连续重复软提示次数 | 4 次后中止 | 重复检测器 |
数值全部来自源码常量,运维可用环境变量覆盖两个轮数上限。
注意:硬上限不是简单报错退出。最后三轮不再调用模型,而是塞一条「快到迭代上限了」的消息,让反思角色把已有结论收住。长任务被迫截断时还能留下可读结果,这个细节比多给几十轮预算更实用。
提示词:39 个模板按角色分工
提示词不是一份大文件。源码把 39 个模板放在同一个目录,合计 4419 行,通过编译期嵌入塞进二进制,每个模板声明自己允许使用哪些变量,渲染前按白名单校验。按用途看,执行专家类占掉接近一半篇幅,说明这套系统把力气花在「每个专家该怎么干活」,而不是把总调度提示词写得越来越长。
运行时用模板引擎把工具名、用户语言、执行上下文、容器内工作目录、镜像名、当前时间和用户文件清单注入模板,渲染结果作为消息链开头落库。网页设置页存的自定义内容会从数据库覆盖默认值;覆盖前重新校验变量,空值或校验不过一律回落默认,注释里点名是为了防止模板把缺失变量渲染成一个不存在的工具名。
按用途汇总的模板行数

执行专家类模板独占 2022 行,接近总量一半;调度者自身只有 678 行。
篇幅最长的八个单模板

渗透执行、交互助手、代码开发三个角色的单份提示词都超过 300 行。
模板分类清单
| 类别 | 模板数 | 行数 |
|---|---|---|
| 调度与交互 | 2 | 678 |
| 执行专家 | 7 | 2022 |
| 规划与重排 | 6 | 623 |
| 收尾与纠偏 | 7 | 452 |
| 上下文处理 | 4 | 270 |
| 辅助决策 | 6 | 230 |
| 专家提问模板 | 7 | 144 |
合计 39 个模板、4419 行,按文件逐行统计。
两个值得抄的提示词设计
第一个是双通道语言。总调度模板明确规定:工具调用里的备注字段用用户语言写,而派发给专家的问题字段一律用英文,专家回传的详细结果也按英文处理;收尾时再把英文结论翻回用户语言,但 CVE 编号、IP、端口、路径和工具名保持原样。这样既让界面上的过程记录能读,又避免多轮翻译把技术标识改坏。中文提问能拿到中文报告、专家之间仍用英文协作,靠的就是这条规则,而不是模型自觉。
第二个是把授权边界写进几乎每个角色模板的开头。模板用一段结构化声明写明这是已获书面授权的测试环境、所有派发任务都已预先授权、不需要再向人确认,并要求不要生成征求许可的步骤。这是合规声明,也是流程保险:否则模型很容易在每一步都追问一句「你有授权吗」,把自动化堵死。
要点:把「什么话用哪种语言」「哪些标识不许翻译」「授权前提是什么」写成模板里的硬规则,比在运行时靠提示词拼接可靠得多。任何需要多角色协作的 Agent 系统都能直接借用这两条。
工程取舍、风险与能搬走的部分
取舍对照
| 选择 | 换来什么 | 付出什么 |
|---|---|---|
| 每轮两次写库 | 可续跑、可审计、可回放 | 长任务数据库写入压力大 |
| 三层循环加动态重排 | 能被中途打断,计划随结果调整 | 规划与纠错都是额外模型调用 |
| 出错统一回等待 | 一次模型抖动不会判死任务 | 界面看着像卡住,需要解释 |
| 100 / 20 轮硬上限 | 防止无限打转 | 复杂任务可能被提前收尾 |
| 提示词编译进二进制 | 部署不依赖外部文件 | 改默认模板必须重新构建镜像 |
真正能搬走的不是某个函数,是三条边界:状态外置到数据库而不是留在内存;把模型输出当成不可信输入,为格式错误、参数错误、重复调用各准备一条退路;把计划做成可重排的队列,而不是一次性生成后不再改。这三条在你做过的调度系统、容器编排和机器人任务管理里都能找到对应物,只是这里把它们收进了同一个进程。
要警惕的是成本结构。反思、纠错、导师、重排、摘要都是独立模型调用,用便宜模型跑长任务时,这些「保险」的调用次数可能比真正干活的工具调用还多。源码里用轮数上限和角色分级来控制它,但上线前仍应按自己的单价重新算一遍。
代码速查与来源
下面这份速查按阅读顺序列,从外层一路走到内层,再跳到提示词装配。文件路径相对仓库 backend/pkg 目录,行号对应 commit cffed94。
controller/flow.go:1288 外层会话循环
controller/flow.go:1353 用户输入转为任务
controller/task.go:341 中层子任务队列循环
controller/subtasks.go:70 首次拆分计划
controller/subtasks.go:106 每步重排剩余计划
controller/subtask.go:294 中内层接缝
providers/provider.go:708 进入 agent 链
providers/provider.go:672 系统提示词渲染
providers/performer.go:58 内层循环主体
providers/performer.go:282 单工具执行与护栏
templates/templates.go:20 模板编译期嵌入
templates/templates.go:522 默认提示词组装
controller/prompter.go:37 自定义覆盖与校验
tools/registry.go:12 工具常量与分类
csum/chain_summary.go:98 消息链压缩
引用与来源
- vxcontrol/pentagi 仓库 — commit cffed94,2026-10-23
- 内层循环源码 performer.go — 轮数上限与单轮步骤
- 提示词模板目录 — 39 个模板,逐行统计
- 官方快速上手文档 — 部署形态与运行前提






评论前必须登录!
立即登录 注册