一、相关知识
从Prompt Egineering->Context Engineering->Loop Engineering,AI应用的构建范式正在发生深刻转移。本质是:将系统的控制权从"概率性的大模型"剥离,交还给"确定性的工程系统"。
大模型是油门,提供智能和创意; Loop系统是刹车与方向盘,负责兜底、纠偏、导航。
Loop Engineering愿景:不用在手动敲Prompt,而是设计一个自动发现工作、分配任务、交叉检查、持久记忆的闭环系统。
二、微观执行与宏观编排
拆解两个概念:
- Agent Loop(微观执行层):包裹在大模型外围的 while True 逻辑,解决单个 Agent 怎么稳定跑完一次任务这一问题,它是发动机。
- Loop Engineering(宏观编排层):多 Agent 调度、外部工具对接、长期状态记忆的系统架构,解决怎么让 Agent 体系脱离人工值守、长期自主运转 ,它是整车工程。
三、生产级Agent Loop遵守的工程准则
- 硬边界熔断和独立裁决:不依赖模型主动停下,终止权必须交给代码硬预算与独立校验逻辑。
python
@dataclass
class HardBudget:
max_turns: int = 50 # 最多多少轮
max_seconds: int = 600 # 超时
max_tokens: int = 200_000 # token 预算
max_cost_usd: float = 5.0 # money 预算
指纹去重并且将进度哈希(防止死循环和幽灵循环):同一参数调用超阈值判死循环;连续多轮上下文哈希不变判空转。
python
def detect_infinite_retry(run: AgentRun, call: ToolCall, *, repeat_threshold: int = 5) -> tuple[bool, str]:
fp = sha256(json.dumps({"name": call.name, "params": call.params}, sort_keys=True))
count = run._fingerprint_counter.get(fp, 0) + 1
run._fingerprint_counter[fp] = count
if count > repeat_threshold: return True, f"Dead loop: '{call.name}' called {count}×"
return False, ""
class ProgressMonitor: # 幽灵循环检测:内部维护滑动窗口 hash,连续不变则 raise
def check(self, run: AgentRun) -> None: ...
- 提议-裁决分离与权限门禁:将模型降级为提议者,用工程代码做最终决策和权限拦截。
python
def validate_tool_call(call: ToolCall, registry: ToolRegistry) -> tuple[bool, str]:
if call.name not in registry:
return False, f"工具 {call.name} 不存在"
# ... 参数类型强校验略
python
@dataclass
class ToolCallPolicy:
highRiskTools: set[str]
def decide(self, call: ToolCall, user_context: UserContext) -> str:
if call.name in self.highRiskTools:
return "ask"
# 权限上下文注入:强制追加当前用户的数据权限范围
call.params["scope"] = user_context.data_scope
return "allow"
- 显示状态机与上下文防挤压:拒绝隐式循环,用分层上下文防止指令失忆,让系统可观测、可调试。
python
class RunState(StrEnum):
RUNNING = "running";
SUSPENDED = "suspended";
COMPLETED = "completed"
FAILED = "failed";
CANCELLED = "cancelled"
class RunPhase(StrEnum):
PREPARE = "prepare" # 组装上下文(L0-L3分层防挤压)
THINK = "think" # 等模型输出
DECIDE = "decide" # 独立裁决意图
EXECUTE = "execute" # 执行工具(串行/并行由 ToolRunner 决定)
DONE = "done" # 目标达成,循环终止
- 持久化可恢复:用细粒度的CheckPoint替代聊天记录,确保系统在崩溃后可以原地续跑
python
class CheckpointStore(Protocol):
async def save(self, run: AgentRun) -> None: ... # 框架保证原子写入
async def load(self, run_id: str) -> AgentRun | None: ...
# 在 AgentLoop 构造时注入;每轮 EXECUTE 结束后自动触发
loop = AgentLoop(..., checkpoint_writer=store.save, checkpoint_loader=store.load)
Checkpoint 具体存在哪里?
常见的存储方案有以下几种:
| 存储介质 | 适用场景 | 特点 |
|---|---|---|
| PostgreSQL / MySQL | 生产环境中的 Agent 状态持久化 | 事务可靠,支持按会话或任务查询 |
| Redis | 高频状态缓存、短期恢复 | 读写快,但需要配置持久化;通常不单独承担长期可靠存储 |
| SQLite | 单机 Agent、个人助理应用 | 部署简单,适合轻量场景 |
| 对象存储 | 大型上下文、文件、执行产物 | 适合保存大体积数据,通常由数据库记录引用 |
例如,你可以在 PostgreSQL 中设计一张 agent_checkpoints 表:
agent_checkpoints 表结构示例
|-----------------|---------------------|
| 字段 | 含义 |
| thread_id | 会话或任务的唯一标识 |
| checkpoint_id | 检查点 ID,区分不同版本 |
| state | 当前执行状态,例如消息、变量、任务进度 |
| step | 执行到哪个节点或步骤 |
| status | 运行中、等待、完成或失败 |
| created_at | 检查点创建时间 |
这只是一个简化的自定义设计,实际框架可能会采用不同的数据结构,也可能将状态拆分为多张表。
- 同步和隔离:在概率模型之上构建确定性的协调机制,用工作空间隔离和串行化写入防止并发冲突
四、完整落地代码
python
# ── 所有运行状态的唯一容器(宪法三:显式状态机) ──
@dataclass
class AgentRun:
run_id: str; task: str
state: RunState = RunState.RUNNING
phase: RunPhase = RunPhase.PREPARE
turn_count: int = 0
input_tokens: int = 0; output_tokens: int = 0; cost_usd: float = 0.0
messages: list[Message] = field(default_factory=list)
tool_history: list[ToolCall] = field(default_factory=list)
pending_tool_call: ToolCall | None = None
_fingerprint_counter: dict[str, int] = field(default_factory=dict) # 死循环检测
_retry_counter: dict[str, int] = field(default_factory=dict) # 红绿灯重试计数
fault_class: str | None = None; last_error: str | None = None # 崩溃现场
# ── 5 条宪法的汇聚点:AgentLoop ──
class AgentLoop:
def __init__(self, llm, dispatcher, *, budget, checkpoint_writer, checkpoint_loader, ...):
self._runner = ToolRunner(dispatcher) # 宪法五:串行/并行路由 + 红绿灯重试
self._progress = ProgressMonitor() # 宪法一:幽灵循环检测
async def _loop_events(self, run: AgentRun):
while True:
# 宪法一:硬边界熔断 + 幽灵循环检测(每轮最先执行,越权必断)
check_budget(run, self._budget)
self._progress.check(run)
# 宪法三:上下文分层防挤压(系统指令 > 当前状态 > 知识 > 历史)
# ContextManager 负责:超长时摘要压缩历史、系统区永不压缩
run.phase = RunPhase.PREPARE
system, messages_to_send = await self._context_manager.prepare(run)
# 宪法三:THINK 阶段 → 将组装好的上下文送给 LLM
run.phase = RunPhase.THINK
response = await self._llm.complete(messages_to_send, system=system, tools=...)
run.turn_count += 1; run.add_tokens(response)
# 宪法一 & 宪法二:独立裁决(防幻觉终止)
run.phase = RunPhase.DECIDE
if response.stop_reason == "end_turn":
# 铁律:模型说做完不算,goal_verifier 验证才算。
# 宏观层面 Claude Code 的 Stop Hook 也是同理:
# 将终止权从"执行模型"交给"校验模型"(Maker/Checker 分离)。
if await self._goal_achieved(run):
run.state = RunState.COMPLETED; yield RunCompletedEvent(run); return
run.messages.append(Message(role="user", content="目标尚未达成,请继续。"))
continue
# 宪法二 & 宪法五:批量执行(含死循环检测、幂等注入、审批拦截、并行/串行、红绿灯重试)
run.phase = RunPhase.EXECUTE
async for event in self._runner.run_batch(run, response.tool_calls):
yield event
# 宪法四:每轮执行结束后写 Checkpoint(挂了接着跑)
if self._write_checkpoint:
await self._write_checkpoint(run)