跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
编程语言Agent RuntimeTypeScriptTool CallingAgent Loop试读章节

《Agent Runtime 工程化》试读章节:Agent Loop 的完整生命周期

试读章节:Agent Loop 的完整生命周期 这篇放一段《Agent Runtime 工程化》的试读。 不是整章原样贴。 我把第三章里最核心的部分抽出来,重新整理成一篇社区版:一次 Agent Loop 到底应该怎么跑。 很多 Agent

陈堂会发布于 —2 浏览
《Agent Runtime 工程化》试读章节:Agent Loop 的完整生命周期

试读章节:Agent Loop 的完整生命周期

这篇放一段《Agent Runtime 工程化》的试读。

不是整章原样贴。

我把第三章里最核心的部分抽出来,重新整理成一篇社区版:一次 Agent Loop 到底应该怎么跑。

很多 Agent 教程会把主循环写成:

用户输入
调用模型
模型调用工具
工具结果返回模型
模型输出答案

这张图没错。

只是太薄。

真正写 Runtime 时,你会发现每一步都有边界。

模型不是直接执行者。模型只是提出下一步。

Runtime 才负责校验、审批、执行、记录和停止。

一次 Agent Loop 不只是 while

最小循环可以写得很短:

while (step < maxSteps) {
  const response = await model.complete(messages, tools);

  if (response.final) return response.text;

  for (const call of response.toolCalls) {
    const result = await tools[call.name](call.input);
    messages.push(toToolMessage(result));
  }
}

教学时,这段代码很好。

但生产里,它至少漏了这些问题:

工具名不存在怎么办?
参数缺字段怎么办?
路径越过 workspace 怎么办?
写文件是否需要用户批准?
工具输出太大怎么办?
工具失败后能不能重试?
这一步是否写入 trace?
中断后从哪里恢复?
为什么最终停止?

所以本书里,我更愿意把 Agent Loop 看成一个生命周期,而不是一个 while loop。

第一步:创建 Run

用户看到一句话:

帮我修复这个测试失败。

Runtime 看到的是一次 run。

它至少要创建:

type RuntimeIds = {
  sessionId: string;
  runId: string;
  turnId: string;
  traceId: string;
};

如果没有这些 ID,后面所有东西都串不起来。

你不知道某个 tool result 属于哪次用户请求。
不知道 checkpoint 对应哪次运行。
不知道 trace 里第 7 步为什么出现一个写文件操作。
也不知道用户取消的是哪个任务。

Agent Runtime 的第一步,不是调用模型。

是给这次执行建立身份。

第二步:构建上下文

Context Builder 负责本轮模型看见什么。

它不应该简单把所有历史拼起来。

它要选择:

当前用户目标
安全策略
最近消息
当前 run state
可用工具
相关文件片段
工具 observation
历史摘要
预算报告

一个最小上下文结果可以是:

type ContextPackage = {
  messages: RuntimeMessage[];
  digest: {
    messageCount: number;
    estimatedTokens: number;
    includedFiles: string[];
    droppedItems: string[];
    budgetLimit: number;
  };
};

digest 很重要。

模型答错时,你要能查:

它有没有看见正确文件?
用户约束有没有被挤掉?
工具结果是否被截断?
repo map 是否误导了它?

没有 context digest,排障时只能说'模型忘了'。

这句话没法修 bug。

第三步:调用模型

模型调用应该通过 Runtime 自己的中间表示。

不要把某个 SDK 的原始字段撒到系统各处。

本书用的核心类型是:

type ModelResponse =
  | { kind: "final"; text: string; usage?: TokenUsage }
  | { kind: "tool_calls"; calls: ToolCall[]; usage?: TokenUsage };

type ToolCall = {
  id: string;
  name: string;
  input: unknown;
};

模型可能返回最终答案,也可能返回一组 tool calls。

Runtime 要记住一件事:

模型输出不是可信事实。

它是一份待校验的计划。

第四步:校验工具调用

工具系统不能只是:

tools[call.name](call.input)

Runtime 必须先通过 Tool Registry 生成计划。

class ToolRegistry {
  plan(call: ToolCall): ToolPlan {
    const tool = this.tools.get(call.name);
    if (!tool) {
      return { call, validationErrors: ["UNKNOWN_TOOL"] };
    }

    const validation = validateObjectSchema(tool.inputSchema, call.input);
    if (!validation.ok) {
      return { call, tool, validationErrors: validation.errors };
    }

    return { call, tool, validationErrors: [] };
  }
}

工具不存在,不能执行。

参数形状不对,不能执行。

参数形状对,也不一定能执行。

比如:

{"path":"../../.ssh/id_rsa"}

它可能符合 schema,因为 path 是字符串。

但它不符合 workspace policy。

所以 schema validation 后面还要有 policy validation。

第五步:权限审批

Permission Gate 要在 execute 前运行。

顺序不能反。

一个最小决策对象可以是:

type PermissionDecision = {
  callId: string;
  toolName: string;
  action: "allow" | "deny" | "ask";
  reason: string;
  risk: "read" | "write" | "shell" | "network" | "dangerous";
  decidedAt: string;
  preview?: string;
};

只读工具可以自动执行。

写文件要给 preview。

shell 要走命令策略。

网络工具要说明域名和用途。

破坏性操作默认拒绝。

权限审批不是 UI 小插曲。它是执行历史的一部分,必须进入 trace 和 checkpoint。

否则恢复执行时,Runtime 可能忘记用户已经拒绝过某个危险动作。

第六步:执行工具

工具执行时,Runtime 要控制:

timeout
AbortSignal
输出上限
错误分类
是否可重试
是否并行
是否有副作用

工具结果不应该只是一段字符串。

type ToolResult = {
  callId: string;
  toolName: string;
  status: "ok" | "error" | "denied" | "timeout" | "cancelled";
  content: string;
  errorCode?: string;
  metadata: {
    durationMs: number;
    outputChars: number;
    truncated: boolean;
    retryable: boolean;
  };
};

content 给模型看。

metadata 给 Runtime 和 trace 看。

如果工具输出被截断,模型必须知道。
如果错误可以重试,Runtime 和模型都要知道。
如果工具被拒绝,不能伪装成普通失败。

这就是结构化 observation 的价值。

第七步:写回观察结果

工具结果要进入 message history。

但不是随便拼一句:

tool returned: ...

它应该作为 RuntimeMessage 写回:

type RuntimeMessage =
  | { role: "system"; content: string }
  | { role: "user"; content: string }
  | { role: "assistant"; content: string; toolCalls?: ToolCall[] }
  | { role: "tool"; toolResult: ToolResult };

这样下一轮模型能看到:

工具执行成功还是失败
错误码是什么
结果有没有截断
是否可重试

很多'模型不聪明'的问题,其实是 observation 写得太烂。

你只告诉它'failed',它当然只能猜。

第八步:保存 Trace 和 Checkpoint

每个 step 结束后,至少要保存两类东西:

trace:用于排障
checkpoint:用于恢复

Trace 记录:

context.build
model.complete
tool.plan
permission.review
tool.execute
checkpoint.save

Checkpoint 记录:

run state
messages
steps
tool ledger
permission decisions
文件变化
budget

没有 trace,失败后不知道哪里错。

没有 checkpoint,中断后只能从头来。

对有副作用的 Agent 来说,从头来很危险。

第九步:判断是否继续

Agent Loop 的停止条件不只有 final。

至少要考虑:

final
max_steps
budget_exhausted
permission_denied
repeated_error
repeated_tool_call
user_cancelled
paused_for_resume

停止原因要进入最终输出。

不要只说:

任务失败。

更好的表达是:

我停止在第 6 步,因为连续三次读取 src/foo.ts 都返回 NOT_FOUND。
建议先确认文件路径,或让我重新列出 src 目录。

用户不需要知道所有内部结构。

但用户需要知道系统为什么停。

一段真实 Runtime 顺序

本书 demo 里的主循环顺序是:

context -> model -> validate -> permission -> execute -> observe -> checkpoint

这条顺序我建议背下来。

不是为了考试。

是因为线上事故经常发生在顺序乱了。

先 execute 后 permission:权限系统变成日志。
先 observe 后 validate:坏工具调用污染历史。
execute 后不 checkpoint:副作用状态丢失。
final 不写 trace:用户只看到结论,看不到证据。

Runtime 的价值,不是把 while loop 写得更花。

是让每一步都有边界、证据和后路。

试读小结

Agent Loop 的完整生命周期可以压成一句话:

模型提出下一步,Runtime 决定这一步能不能被安全执行,并把结果变成下一步的事实。

这就是 Agent Runtime 和普通 LLM App 的分界。

普通 LLM App 主要管理文本。

Agent Runtime 管理行动。


推荐阅读

  • 试读章节:Tool Runtime 与权限审批
  • 一个 Agent Runtime 失败案例的完整 Trace
  • 生产级 Agent 上线前必须检查的 50 件事

目录

  1. 试读章节:Agent Loop 的完整生命周期
  2. 一次 Agent Loop 不只是 while
  3. 第一步:创建 Run
  4. 第二步:构建上下文
  5. 第三步:调用模型
  6. 第四步:校验工具调用
  7. 第五步:权限审批
  8. 第六步:执行工具
  9. 第七步:写回观察结果
  10. 第八步:保存 Trace 和 Checkpoint
  11. 第九步:判断是否继续
  12. 一段真实 Runtime 顺序
  13. 试读小结
  14. 推荐阅读
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Docker 部署 MySQL 8.0 完整指南:从拉取镜像到配置远程访问
  • ChatGPT 与 DALL·E 制作日漫风格小故事全流程
  • Stable Diffusion WebUI 本地安装与配置教程
  • Python 核心语法详解:测试脚本开发基础
  • 在 Linux 桌面里跑 Windows 应用:Winboat 实战笔记
  • AI 编程工具深度对比:Trae、Cursor、Copilot 与 Windsurf
  • 论文阅读--Agent AI 探索多模态交互的前沿领域(一)
  • OpenCode 开源 AI 编程助手使用教程
  • Linux diff 与 patch 命令实战指南
  • 网络安全工程师职业定义、核心技能与认证体系详解
  • Python 为何如此流行?深度解析其核心优势与应用场景
  • 大疆无人机如何导出日志并解析
  • 前端 WebSocket 通信实战与最佳实践
  • OpenClaw 进阶教程:记忆系统、定时任务、多模型与子代理解析
  • Python 爬虫实战指南:从基础请求到分布式框架
  • Kubernetes Python 客户端实战教程
  • Java IO 核心:BufferedReader、BufferedWriter、PrintStream 与 PrintWriter 详解
  • AI 辅助编程工具:GitHub Copilot 安装与使用指南
  • 基于 Rokid AR 眼镜的聚会游戏助手开发实践
  • 百瑞互联 BR8654A02 蓝牙 6.0 SOC 芯片规格介绍

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online