agno Team 回答重生成(Regenerate)实战:重新生成团队最终回答而不重跑成员委派
2026/9/9 12:28:12 网站建设 项目流程

agno Team 回答重生成(Regenerate)实战:重新生成团队最终回答而不重跑成员委派

【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno

团队型 Agent 在多次与用户交互时,往往会出现"委派结果都对,但最后的总结答得不好"的情形。agno 为 Team 提供了一等公民的Regenerate(重生成)能力:通过Team.continue_run(..., regenerate=True)只丢弃上一次运行末尾的纯文本回答,保留中间全部工具交换(即各成员 Agent 的委派结果),用一个全新的run_id重新生成一份最终答案——成员不会被重复委派、中间结果不会被浪费。本文基于仓库中的 Team Regenerate 官方示例,完整讲解它的触发方式、replace_original可见性语义、fork 血统与成员行的保留规则,并下沉到 libs/agno/agno/team/_run.py 与 libs/agno/agno/run/team.py 的源码实现,帮助你在自己的多 Agent 应用中安全地使用"只重写结论、不重跑过程"。

一、什么是 Team Regenerate:能力边界一图看懂

根据 24_regenerate 目录下的 README,regenerate=True的语义可拆成三点:

  1. 只丢弃尾部的助手回答:Team 运行中会产生多条消息,其中末尾那一条"不含任何 tool_calls 的纯文本 assistant 消息"就是最终总结。Regenerate 只把它丢掉。
  2. 保留中间工具交换:成员委派在 Team 视角下就是"调用了一个工具",成员输出以 tool-role 消息形式出现在 Team 会话中。这些中间消息原样保留,因此成员委派不会重新执行,模型基于同一份委派结果生成全新总结。
  3. 总是 fork:重生成不是在原 run 上原地改写,而是产生一个新的run_id与全新的RunMetrics,源 run 永远被保留在存储中。

一句话概括:Regenerate = "同样的事实(成员输出),换一种说法(新的最终总结)"。

二、运行前准备与示例文件结构

示例位于 cookbook/03_teams/24_regenerate/01_regenerate.py,该目录同时包含 TEST_LOG.md 用于记录验证状态。运行方式:

.venvs/demo/bin/python cookbook/03_teams/24_regenerate/01_regenerate.py

需要注意两点前提:

  • 示例使用OpenAIResponses(id="gpt-5.4"),依赖OPENAI_API_KEY环境变量;据 TEST_LOG 记录,该脚本在无 Key 环境下仅做语法/编译校验(Status: NOT RUN)。
  • Regenerate 依赖会话持久化,成员与 Team 都配置了SqliteDb;该功能建立在 23_checkpointing 团队断点续跑 基础之上,README 亦明确标注 Foundation 为该目录。

三、分步实战:原始运行 → Regenerate → 会话检查

示例脚本演示了"旅游问答团队"(天气 + 人口两个子 Agent)的完整重生成链路,值得逐段拆解。

3.1 构造成员与团队(含持久化)

weather_agent = Agent( name="weather-agent", role="Answers weather questions.", model=OpenAIResponses(id="gpt-5.4"), tools=[get_weather], db=SqliteDb(session_table="team_demo", db_file=DB_FILE), ) population_agent = Agent( name="population-agent", role="Answers population questions.", model=OpenAIResponses(id="gpt-5.4"), tools=[get_population], db=SqliteDb(session_table="team_demo", db_file=DB_FILE), ) team = Team( name="travel-team", model=OpenAIResponses(id="gpt-5.4"), members=[weather_agent, population_agent], db=SqliteDb(session_table="team_demo", db_file=DB_FILE), instructions=( "Delegate weather questions to weather-agent and population " "questions to population-agent. Summarize in one sentence." ), )

要点:三个对象共享同一个SqliteDb(session_table="team_demo", db_file=DB_FILE),其中DB_FILE = f"tmp/team_regenerate_{int(time.time())}.db"按时间戳生成独立库文件,便于反复试验;团队成员get_weather/get_population是纯内存 mock 函数(Paris/Tokyo/Lagos 三城市),确保流程可离线重现。

3.2 STEP 1:创建原始 run

original = await team.arun( input="What's the weather and population of Paris?", session_id="team-sess-1", ) print(f" run_id: {original.run_id}") print(f" content: {original.content}")

这一轮中,Team 的模型循环会依次把问题委派给weather-agentpopulation-agent,两个成员的返回以 tool-role 消息沉淀进会话,最后模型生成一句总结。返回的original.run_id是后续重生成的"源坐标"。

3.3 STEP 2:带"转向指令"的 Regenerate

forked = await team.acontinue_run( run_id=original.run_id, session_id="team-sess-1", regenerate=True, additional_instructions="This time, only mention weather. Skip population.", ) print(f" run_id: {forked.run_id} (new)") print(f" forked_from_run_id: {forked.forked_from_run_id}") print(f" regenerated_from: {forked.regenerated_from}") print(f" content: {forked.content}")

这是全篇最关键的一次调用,注意三个细节:

  • 同步 API 是team.continue_run(...),异步 API 是team.acontinue_run(...);两者的方法签名在 libs/agno/agno/team/team.py 中成对出现(continue_run定义在 1113 行附近,acontinue_run定义在 1198 行附近),并都接受regeneratereplace_originaladditional_instructionsinputcontinue_fromfork等快照分发参数。
  • regenerate=True触发重生成;additional_instructions="This time, only mention weather..."提供转向指引,代码注释称之为steering(转向)——不传时则按原样重述一次结论。
  • 结果对象forked同时携带两类血统字段:forked_from_run_id(来自 fork 机制)与regenerated_from(专用于标识"从哪个 run 重生成而来"),都指向源 run。

3.4 STEP 3:会话内验证明细

session = team.db.get_session(session_id="team-sess-1", session_type="team") team_runs = [r for r in (session.runs or []) if hasattr(r, "member_responses")] agent_runs = [r for r in (session.runs or []) if not hasattr(r, "member_responses")]

脚本用"是否含member_responses字段"区分 Team 行与成员 Agent 行,然后打印两类结论:

  • Team 行 = 2(original + fork,且都持久化):源 run 与重生成 run 各自作为一条独立 run 记录存在,用forked_from_run_id前缀输出能看出从属关系。
  • 成员行 = 0 新增:成员 run不会被克隆,仍然挂在原始 Team 之下(通过parent_run_id标识其归属)。

这正是 Regenerate 与"整段复制"的分水岭:已经发生的委派是历史事实(fact of history),不是需要拷贝的状态(state to copy)。README 的表述与脚本输出完全一致:"Member rows the original team produced stay attached to the original team"。

四、replace_original:源 run 的"可见性开关"

README 明确指出 Regenerate总是 fork——新run_id+ 新指标,源 run 永远保留。那么"重生成后旧的总结还显不显示?"就由replace_original控制,它只影响源 run 在历史中的可见性

replace_original源 run 状态历史可见性典型用途
True(默认)被标记为REGENERATED隐藏,由新 run 顶替其位置"旧答案作废,只展示重写后的"
False保持COMPLETED保留,两个尝试都显示"AB 对比,让用户/裁判选择"

结合源码,libs/agno/agno/team/_run.py 的_normalize_regenerate_params_team(7252 行起)还会做这些一致性校验:

  • replace_original只在regenerate=True时有意义,否则抛ValueError("replace_original only makes sense with regenerate=True")
  • regenerate=True不允许再传fork=True——因为"是否 fork"已由replace_original推导,README 注释称之为1-run-1-loop invariant
  • regenerate=True要求调用前已加载到目标 run 的run_response,否则无法计算截断点;
  • additional_instructionsinput互斥,二者只能选其一作为转向输入。

五、源码深读:截断点如何计算、成员为何不被重跑

5.1 边界计算:_find_regenerate_checkpoint_team

Regenerate 的落点是一个消息截断索引,由 libs/agno/agno/team/_run.py 中的_find_regenerate_checkpoint_team(7158 行起)计算:

messages = run_response.messages or [] i = len(messages) while i > 0 and messages[i - 1].role == "assistant" and not messages[i - 1].tool_calls: i -= 1 if i == 0: raise ValueError("Cannot regenerate: team run has no non-assistant messages to regenerate from.") return i

逻辑是:从消息尾部向前扫描,凡是"assistant 角色且不含 tool_calls"的消息一律剔除(这通常正是末尾的最终总结,中间推理性质的 assistant 消息同理被剥掉),遇到第一条带工具调用的 assistant 消息或非 assistant 消息即停。因此成员委派(对 Team 而言就是工具调用)及其 tool-role 结果消息全部留在边界之前。

值得对照的是同一文件中的_find_last_user_message_index_team(7177 行起):它对应continue_from="last_user"的语义——回退到最后一条 user 消息,把其后的中间工具交换也一并丢弃。源码 docstring 明确点出二者差异:"last_user"regenerate=True语义不同,前者把中间工具调用也丢掉。也就是说:

  • continue_from="last_user"→ 从"用户提问后"整段重来(重跑委派);
  • regenerate=True→ 只重写"末尾总结"(不重跑委派)。

5.2 fork 血统与成员引用:Team 行的深拷贝,成员行的零拷贝

_resolve_continue_from_teamregenerate=True会自动推导边界(不允许手工指定continue_from,否则报错)。而真正执行时,fork 出的新 run 会携带两类血统字段,它们定义在 libs/agno/agno/run/team.py 的 TeamRun 模型上(810–819 行附近):

  • forked_from_run_id/forked_from_message_index:fork 血统与截断坐标;
  • regenerated_from:本 run 由哪个 run 重生成而来(即"父 run 的run_id")。

关于成员数据的处理,01_regenerate.py 顶部的 docstring 说得非常直白:fork 出的 Team 其member_responses字段引用(deep-copy)了同一份成员数据,但不会向会话写入任何新的成员 run 行。这与 Agent fork 不克隆"工具执行行"完全对仗——成员之于 Team,就像工具之于 Agent,是一个被调度的资源而非被复制的状态。同时深拷贝也保证了 fork 无法污染源数据。

5.3 状态流转与默认替换

regenerate=True且未显式关闭替换时,源码在写新 run 的同时会调用_mark_team_run_regenerated(7354 行起)把父 run 状态翻转为RunStatus.regenerated并持久化,使历史构建器(history builders)在拼上下文时跳过它——这正是replace_original=True默认行为的落点。示例代码forked_from_run_id[:8]与 run 状态的一并打印,可以让开发者直接观察这条状态变迁。

六、同步/异步双 API 与团队模型循环语义

Regenerate 是/continue快照分发能力(snapshot dispatch)的一部分,参数在 libs/agno/agno/team/team.py 的continue_runacontinue_run中被透传:

  • continue_run(同步,1078–1160 行签名区):可直接传入上次的run_response,或以run_id + session_id定位;
  • acontinue_run(异步,1163–1247 行签名区):同样的参数集,适用于async def main()场景(示例即采用此写法,最后以asyncio.run(main())收尾)。

无论哪种形态,重生成都是让 fork 出的 Team 用新的run_id重放 Team 自己的模型循环(仅一次模型循环 + 复用既有成员输出),因此 fork run 会获得一套全新的RunMetrics(token 消耗、耗时、工具调用数等),便于与原始 run 做质量对比。

七、成员为何"不在作用域内":与 Agent 面的 Parity 设计

整个 Regenerate 机制最核心的设计决策是:members are out of scope。团队断点(checkpoint)只作用于 Team 自身的状态——从 Team 视角看,成员只是它委派出去的工具,成员的输出只是会话中一条 tool-role 消息。这一点在 23_checkpointing 的 README 中被表述为 "Fork / regenerate / time-travel operate on the team's own state, not member state"。

由此带来一个工程师必须养成的习惯:如果你希望"重生成时连委派过程一起重来",那不应使用 Regenerate,而应使用同目录族的time-travel / fork / 新的 run能力(详见 03_teams 下的25_time_travel/26_fork_session/兄弟目录)。Regenerate 的适用场景非常聚焦:委派结果有效、需要换一种总结口径或修正措辞——例如示例中"这次只提天气、跳过人口"的转向需求。

八、测试支撑与验证手段

该功能不是孤例代码,而是有系统化单测覆盖的:

  • 单元测试位于 libs/agno/tests/unit/team/test_team_checkpointing.py,其中TestTeamRegenerateSugarTestRegenerateSugarNormalization两组用例分别覆盖"重生成语法糖的端到端行为"与"参数规范化/校验";
  • TEST_LOG 说明 01 号脚本在无 Key 环境下仅做语法/编译验证,其运行时行为交由上述单测保障。

因此,即便你的环境没有模型 API Key,也可以先跑单测确认 Regenerate 语义;再配置OPENAI_API_KEY后运行示例脚本做端到端观察。推荐关注的输出点有三个:forked.run_id != original.run_idforked.regenerated_from == original.run_id、以及会话中 Team 行数变为 2 而成员行数不变。

结语

Team Regenerate 是 agno 把"Agent 的 fork/regenerate 能力"推广到"多 Agent 团队"后的自然延伸:它通过只截断无工具调用的尾部助手消息实现低成本改写,通过总是 fork + 源 run 保留保证可追溯,通过成员行不克隆守住"已发生的委派是事实"的边界。配合replace_original开关,你既可以"旧答案作废、新答案顶上",也可以"两次尝试并存、供人审阅对比"。在需要反复打磨团队最终输出、又不想为每次微调都付出整轮委派成本的生产场景里,这是一个值得优先纳入工具箱的 API。

【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询