Agent 主机层:拼提示 · 管上下文 · 控权限 · 派子agent
30 秒导读: 上一章 讲的 loop 是一台无状态的"发一次请求、跑一批工具、判断要不要再来一轮"的机器。它自己不记历史、不知道系统提示、不管权限。本章讲的
Agent类,就是把这台机器装配成一个能 真正用起来的编码 agent 的主机:它准备好 loop 每一步要的原料(系统提示、消息历史、工具表),在 loop 的每个钩子上插入自己的逻辑(压缩、权限、去重、记账),并把结果记进可回放的日志。
本章范围锁定 packages/agent-core/src/agent/。不深入具体工具怎么实现(留给 03-tools),不讲 provider 协议细节(留给 04-providers)。
1. 这是什么(零基础也能懂)
一句话定义: Agent 是一个长期存活的对象,它握着一次会话的全部可变状态(消息历史、配置、权限模式、计划/目标状态),并在每次该请求模型时,把这些状态渲染成无状态 loop 需要的输入。
loop 和 Agent 的分工,用一个类比:
- loop = 一台榨汁机。 你塞进去水果(消息 + 工具表),它吐出果汁(模型回复 + 工具结果),它不关心水果哪来的、果汁往哪去。
- Agent = 厨房。 它负责买菜、洗菜、切好(拼系统提示、投影历史)、决定这次榨什么、榨完把渣清理掉(压缩)、把成品装盘记账(records)。榨汁机可以换,厨房的流程不变。
为什么需要这一层? 因为一个无状态循环缺了三样东西才能变成 agent:
| 缺的东西 | Agent 怎么补 | 在哪 |
|---|---|---|
| 它是谁、能干什么 | 系统提示 + 画像(profile)装配 | profile/、services/prompt/ |
| 它记得什么 | 上下文记忆 + 投影 + 压缩 | agent/context/、agent/compaction/ |
| 它被允许做什么 | 工具执行前的权限门控 | agent/permission/ |
一个关键设计约束(全书都要记住): Agent 必须能独立使用——它的构造函数不强迫调用方先造一个 Session,不要求 agentId 或 session。它可以接一个可选的 sessionId 作为请求配置的提示(比如映射到 provider 的 prompt_cache_key),但实例本身不持有 sessionId,也不依赖 Session 的生命周期、元数据或父子关系(依据:仓库根 CLAUDE.md "General Coding Rules";以 agent/index.ts 构造函数为准 packages/agent-core/src/agent/index.ts:176-234)。
这条约束贯穿本章:凡是需要"多个 agent 协作/父子关系"的东西(比如子 agent 编排),都不在 agent/ 里,而在 session/ 里。agent/ 只知道"我是一个 agent",不知道"我是谁的孩子"。