agno 如何用 continue_from 回退到历史消息点,或 fork 出新的 run 与 session?
2026/9/10 5:58:05 网站建设 项目流程

agno 如何用 continue_from 回退到历史消息点,或 fork 出新的 run 与 session?

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

如果你已经跑完(或跑挂过)一个 agno Agent 的 run,想在某个历史消息位置重新接上去、或者从某个状态分叉出一条互不影响的新对话,就需要用/continue能力:用continue_from指定回退到哪个消息点,用fork=True显式 fork 出新的 sibling run,或用fork_session()把整个 session 复制到一个新 session。本文基于 cookbook 中的三个可运行示例(01_continue_from.py、02_fork_run.py、01_fork_session.py)给出完整操作路径。

前提:先让 run 可回退

/continue系列能力建立在持久化的 run 之上。按 18_checkpointing/README.md 的说明:

  • 默认的checkpoint="runs"只在终态(COMPLETEDPAUSEDCANCELLEDERROR)写库;
  • checkpoint="tool-batch"在每个 tool batch 之后写一次,这份中途持久化正是/continue能回退、能 fork 的基础;
  • checkpoint="tools"(逐 tool 写)留给 3.0,目前会抛NotImplementedError

所以示例中的 Agent 统一配置了dbcheckpoint="tool-batch"。示例使用的模型为OpenAIResponses(id="gpt-5.4"),运行前需要OPENAI_API_KEY(TEST_LOG.md 中标注了示例依赖该 key)。01 示例用本地 SQLite(tmp/checkpoint_time_travel.db),02 示例用 Postgres:

from agno.agent import Agent from agno.db.sqlite import SqliteDb from agno.models.openai import OpenAIResponses agent = Agent( name="travel-agent", model=OpenAIResponses(id="gpt-5.4"), db=SqliteDb( session_table="checkpoint_demo", db_file="tmp/checkpoint_time_travel.db", ), checkpoint="tool-batch", tools=[get_population], # 示例中的 mock 工具 )

continue_from 的三种边界写法

continue_from决定回退到哪个消息位置,有三种写法(见 20_time_travel/README.md):

写法语义
"end"(默认)保留完整 transcript,等同于普通追问
"last_user"保留到最后一个 user 消息为止,丢弃 user 之后的尾部(tool 会被重新调用)
K(int)保留messages[:K],精确的消息索引边界

无论选哪种边界,边界都会自动吸附到 tool-call 安全索引:如果截断会把 assistant 的 tool_call 与其 result 拆散,边界会回移到该 exchange 的开头,保证恢复后的 transcript 始终合法。

回退到历史消息点并继续

先跑一个基础 run,拿到run_idsession_id

first = await agent.arun(input="What is the population of Paris?") # first.run_id / first.session_id 是后续所有 /continue 操作的定位参数

然后用acontinue_run指定边界回退并传入新的input。回退到最后一条 user 消息之后、换一个问题:

rewound = await agent.acontinue_run( run_id=first.run_id, session_id=first.session_id, continue_from="last_user", input="Actually, what is the population of Tokyo instead?", )

"end"/"last_user"落不到你想要的位置时(比如还要多丢掉一组 tool 结果),用数字索引。示例中先用 1 起序号打印first.messages找出目标位置,再把索引从len(run.messages)往前数出 K 传入:

for i, m in enumerate(first.messages or [], start=1): print(f" [{i}] {m.role}: {(m.content or '')[:60]}") rewound_to_index = await agent.acontinue_run( run_id=first.run_id, session_id=first.session_id, continue_from=1, # 只保留第 1 条消息(原始用户问题) input="Instead, what is the population of Tokyo?", )

需要说明:对COMPLETED的 run 执行/continue自动 fork出一个新的 sibling run(保持 "1 run = 1 model loop" 约定),源 run 原样保留。所以这里的rewound并不是在原地改写first,而是同一 session 里的一个新 run。

显式 fork 出新的 run

如果对象不是 COMPLETED run(例如中途被取消、还在 RUNNING),想让/continue明确创建 sibling 而不是原地恢复,传fork=True。02 示例(02_fork_run.py)展示了从已知状态分叉的用法——例如从同一个状态出发跑不同 prompt 做 eval 或 A/B 对比:

fork = await agent.acontinue_run( run_id=original.run_id, session_id=original.session_id, continue_from="last_user", input="What's the weather in Tokyo and Lagos?", )

fork 出来的 run 带三个可核对的字段:

  • run_id:全新的 run id;
  • forked_from_run_id:指向源 run;
  • forked_from_message_index:截断索引;
  • session_id:与源 run 相同——fork 和源 run 共存于同一个 session,session 的runs数组因此形成一个 DAG(每个 fork 通过forked_from_run_id指回来源)。

对应的服务端入口是POST /runs/{run_id}/continue(见 21_fork_session/README.md 中的对照表)。

fork 出新的 session

run 级 fork 是"同一个 session 内多一条 run";如果你想把整个对话复制出去、开一条完全独立的对话线,用fork_session()。它会深拷贝源 session 的每个 run,生成全新的session_id和全新的run_id,源 session 不受影响(对应入口POST /sessions/{session_id}/fork):

original_sid = "trip-planning-original" user_id = "demo-user" # 先在源 session 里积累两轮对话 await agent.arun(input="I'm planning a trip to Japan...", session_id=original_sid, user_id=user_id) await agent.arun(input="Tell me more about Kyoto.", session_id=original_sid, user_id=user_id) # 分叉:拿到一个全新的 session_id new_sid = await agent.afork_session( source_session_id=original_sid, user_id=user_id, )

之后可以在两个 session 中各自独立继续,互不影响:

forked_run = await agent.arun( input="Actually, what about Osaka's street food scene?", session_id=new_sid, user_id=user_id, ) original_run = await agent.arun( input="Which Kyoto temples are must-see?", session_id=original_sid, user_id=user_id, )

两点注意:

  • 源 session 按调用方的user_id作用域读取——你只能 fork 自己的 session;
  • 血缘字段有两层:run.forked_from_session_id记录 run 的最初源 session(跨嵌套 fork 保持不变);session.session_data["forked_from_session_id"]记录直接父 session(每次再 fork 会被覆盖)。对 root → mid → leaf 的链,leaf.runs[*].forked_from_session_id == root,而leaf.session.session_data["forked_from_session_id"] == mid

fork 与 fork_session 的区分:

fork=True(run 级)fork_session()(session 级)
粒度RunSession
结果同 session 内的新 sibling run含所有 run 副本的新 session
入口POST /runs/{run_id}/continuePOST /sessions/{session_id}/fork
血缘字段run.forked_from_run_idrun.forked_from_session_id

验证回退与 fork 的结果

三个示例都给出了同一类核对方式:直接查库里的 session,确认源 run 保留、fork 已加入。

run 级(01 / 02 示例末尾):

session = agent.db.get_session(session_id=first.session_id, session_type="agent") print(f"Runs in session: {len(session.runs or [])} (source preserved, forks added)") # 02 示例进一步逐条打印每个 run 的来源 for r in session.runs or []: marker = f" (forked from {r.forked_from_run_id})" if r.forked_from_run_id else "" print(f" - {r.run_id} [{r.status}]{marker}")

判断依据:len(session.runs)随每次 fork 增加;新 run 的forked_from_run_id非空且指向源 run;源 run 的status保持原值。

session 级(01_fork_session.py 末尾):

for sid, label in [(original_sid, "original"), (new_sid, "forked")]: s = agent.db.get_session(session_id=sid, session_type="agent") forked_from_session_id = (s.session_data or {}).get("forked_from_session_id") print(f"{label}: {sid} ({len(s.runs or [])} runs)" + (f" forked_from_session_id={forked_from_session_id}" if forked_from_session_id else ""))

判断依据:original 的 run 数不变、无forked_from_session_id;forked session 携带forked_from_session_id,且 run 数与源一致(全部为副本)。

限制与相近能力

  • continue_from="last_user"会丢掉 user 之后的整段尾部(tool_calls、结果、回复),tool 会被重新调用;如果只想"重跑最后的总结、保留工具结果",用regenerate=True——它只丢弃末尾无 tool 调用的 assistant 回合,永不重调 tool,且总是 fork 出新 run(replace_original=False可让源 run 保持可见以便对比)。对照见 19_regenerate/README.md 中的比较表。
  • 数字边界K的取值需要先发现合法索引:直接检查run.messages(示例做法),或调用 checkpoint timeline 接口(参考 03_checkpoint_endpoints.py)。
  • 边界自动吸附到 tool-call 安全索引,无需手工保证 K 落在"安全"位置。
  • 回退/fork 依赖checkpoint="tool-batch"提供的中途持久化;它是session.runsJSON 列上的真实写放大,文档建议用于长研究类 run,而非高频闲聊 agent。

运行示例

.venvs/demo/bin/python cookbook/02_agents/20_time_travel/01_continue_from.py .venvs/demo/bin/python cookbook/02_agents/20_time_travel/02_fork_run.py .venvs/demo/bin/python cookbook/02_agents/21_fork_session/01_fork_session.py

需要进一步看 checkpoint 与/continue的接口细节,可继续读 18_checkpointing/;只想重做最后一条回答则看 19_regenerate/。

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

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

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

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

立即咨询