Loop Engineering 深度实践指南:9 种 2026 年最新做法与完整代码

37 阅读31分钟

2026 年 6 月,Loop Engineering 概念在硅谷引爆 —— 它不是又一个转瞬即逝的 buzzword,而是 AI 工程范式从 " 人提示 AI" 到 " 人设计系统,系统驱动 AI" 的根本转折。本文不讲入门概念,直接切入当前工业界和研究界最活跃的九大实践方向,每个方向都配有可运行的代码示例和架构决策指南。


目录

  1. 极简代码循环 vs. 图状态机框架:两条路线之争
  2. 计划-执行双层循环 + 动态重规划
  3. 事件驱动与流式循环
  4. 多 Agent 拓扑循环
  5. 长时持久化与耐久执行
  6. 自优化循环:DSPy 驱动的 Loop 工程
  7. 声明式循环配置
  8. 可观测性驱动的循环断点与人工协同
  9. 安全护栏子循环

前置背景:Loop Engineering 解决了什么?

在进入具体做法之前,需要明确一个根本问题:

人类注意力有限,AI 执行能力无限。 传统的 Prompt Engineering 模式下,人必须坐在终端前按每一次回车、审每一次输出。当模型输出速度远超人类处理速度时,人就成为了整个流程的瓶颈。

Loop Engineering 的核心是将开发者从 " 每一次交互的参与者 " 转变为 " 循环系统的设计者 "——定义目标、设置护栏、设计验证机制,然后让系统自运转。这不仅是效率的量变,而是生产关系的质变。

当前 Loop Engineering 的工程实践已经远远超越了 2025 年的 ReAct 简单循环,形成了多条技术路线。以下是 2026 年最值得关注的九大方向。


1. 极简代码循环 vs. 图状态机框架:两条路线之争

这是 Loop Engineering 工程哲学上最根本的分野,两条路线各有拥趸,而最新趋势是它们正在走向融合。

路线 A:极简 while 循环

Anthropic 内部推崇的路线。直接用 Python/TypeScript 的 while 循环控制 Agent,内部顺序调用 LLM、解析工具、执行、拼接结果。

核心理念:逻辑完全透明,无黑魔法,易调试。每一步发生什么一目了然,适合规则清晰、步骤较短的 Agent。

"""
极简 while 循环 Agent —— 完全透明的执行逻辑
"""

import json
from openai import OpenAI
from typing import Any

client = OpenAI()

# 工具定义
tools: list[dict[str, Any]] = [
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "Read file content",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {"type": "string", "description": "File path"}
                },
                "required": ["path"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "run_tests",
            "description": "Run the test suite and return results",
            "parameters": {
                "type": "object",
                "properties": {
                    "filter": {"type": "string", "description": "Test filter pattern"}
                },
            },
        },
    },
]

def execute_tool(tool_call) -> str:
    """执行工具调用 —— 逻辑完全可见"""
    name = tool_call.function.name
    args = json.loads(tool_call.function.arguments)

    if name == "read_file":
        with open(args["path"]) as f:
            return f.read()
    elif name == "run_tests":
        import subprocess
        result = subprocess.run(
            ["pytest", args.get("filter", ""), "-q"],
            capture_output=True, text=True
        )
        return result.stdout + result.stderr
    return "Unknown tool"

def agent_loop(task: str, max_iterations: int = 10) -> str:
    """
    核心循环:完全使用标准 Python while,无框架依赖。
    每一步:LLM 推理 → 解析工具调用 → 执行 → 拼接结果 → 下一轮。
    """
    messages: list[dict[str, Any]] = [
        {
            "role": "system",
            "content": (
                "You are a coding agent. "
                "Use tools to understand the codebase and make changes. "
                "When you believe the task is done, respond with 'DONE'."
            ),
        },
        {"role": "user", "content": task},
    ]

    for iteration in range(max_iterations):
        # Step 1: LLM 推理
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
        )
        msg = response.choices[0].message

        # Step 2: 如果模型直接返回文本(无工具调用)
        if msg.content and "DONE" in msg.content:
            return msg.content

        # Step 3: 执行工具调用
        if msg.tool_calls:
            tool_results = []
            for tc in msg.tool_calls:
                result = execute_tool(tc)
                tool_results.append({
                    "role": "tool",
                    "tool_call_id": tc.id,
                    "content": result,
                })

            # Step 4: 拼接结果,进入下一轮
            messages.append(msg)
            messages.extend(tool_results)
            continue

        return msg.content or "No response"

    return f"Stopped after {max_iterations} iterations"

# 使用
result = agent_loop("Read README.md and run all tests. Fix any failures.")

优点

  • 每一步都是显式的,断点调试零学习成本
  • 状态管理完全在 messages 列表中,脏了清掉即可
  • 代码量通常不超过 150 行,review 成本低

缺点

  • 复杂分支逻辑(5+ 个条件跳转)会快速膨胀
  • 无法原生表达并行执行
  • 缺乏可视化和 tracing 支持

路线 B:图状态机框架

LangGraph、OpenAI Agents SDK、Google ADK 都走向了图状态机路线。用有向图定义节点(LLM 调用、工具、条件分支)和边,原生支持并行、动态路由、子图嵌套。

核心理念:用结构化的图描述控制流,逻辑更复杂但可追溯、可可视化。

"""
LangGraph 图状态机 Agent
用有向图明确定义节点和边,支持并行分支和条件路由
"""

from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from typing import TypedDict, Annotated, Literal
import operator


# 定义状态
class AgentState(TypedDict):
    messages: Annotated[list, operator.add]
    task_status: str
    tool_results: dict


# 节点定义
def planner(state: AgentState) -> AgentState:
    """规划节点:分析任务,决定下一步"""
    # LLM 调用,生成计划
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Plan the next step. Return a JSON action."},
            *state["messages"],
        ],
    )
    return {"messages": [response.choices[0].message]}


def executor(state: AgentState) -> AgentState:
    """执行节点:执行工具调用"""
    # 工具执行逻辑
    return {"task_status": "executing"}


def evaluator(state: AgentState) -> AgentState:
    """评估节点:判断结果质量"""
    return {"task_status": "evaluated"}


def should_retry(state: AgentState) -> Literal["executor", "planner", END]:
    """条件路由:根据评估结果决定跳转"""
    if state.get("task_status") == "success":
        return END
    elif state.get("retry_count", 0) < 3:
        return "executor"
    else:
        return "planner"


# 构建图
graph = StateGraph(AgentState)

graph.add_node("planner", planner)
graph.add_node("executor", executor)
graph.add_node("evaluator", evaluator)

graph.set_entry_point("planner")
graph.add_edge("planner", "executor")
graph.add_edge("executor", "evaluator")
graph.add_conditional_edges("evaluator", should_retry)

app = graph.compile()

优点

  • 可视化整个循环拓扑,一眼看清决策分支
  • 原生支持并行节点(多个 executor 同时跑)
  • 子图嵌套:一个节点本身可以是另一个 StateGraph
  • 社区生态成熟(LangSmith tracing、Weave 集成)

缺点

  • 调试难度倍增 —— 需要图编译器 + 状态追踪
  • 简单任务引入不必要的抽象层
  • 性能开销:图的调度层比裸 while 慢约 15-30%

融合趋势:轻量图 + 代码节点

2026 年的最佳实践正在向中间路线收敛 —— 用 JSON/YAML 声明图的拓扑结构,用代码直接实现节点逻辑。

"""
轻量图 Loop:JSON 定义拓扑 + 代码实现节点
"""

loop_config = {
    "entry": "planner",
    "nodes": {
        "planner": {
            "type": "llm",
            "model": "gpt-4o",
            "system_prompt": "Plan the next step as JSON.",
            "next": "executor",
        },
        "executor": {
            "type": "code",
            "handler": "my_project.loop_nodes.execute_step",
            "next": "evaluator",
        },
        "evaluator": {
            "type": "llm",
            "model": "gpt-4o-mini",
            "system_prompt": "Evaluate the result. Return PASS or FAIL with reasons.",
            "routes": {
                "PASS": "END",
                "FAIL": "executor",
                "REPLAN": "planner",
            },
        },
    },
    "max_iterations": 20,
    "parallel_nodes": [],
}


class LightweightLoop:
    """轻量循环引擎:解析 JSON 配置,调用代码节点"""

    def __init__(self, config: dict):
        self.config = config
        self.state = {"messages": [], "iteration": 0}

    def run(self, task: str):
        current = self.config["entry"]
        while current != "END" and self.state["iteration"] < self.config.get(
            "max_iterations", 50
        ):
            node = self.config["nodes"][current]
            if node["type"] == "llm":
                self._run_llm_node(node)
            elif node["type"] == "code":
                self._run_code_node(node)

            current = self._resolve_route(node)
            self.state["iteration"] += 1

这种混合路线的优势

  • 图拓扑一目了然(改配置即改流程,无需动代码)
  • 节点逻辑用纯代码实现,调试像普通函数
  • 换 Loop 策略(ReAct → Plan-Execute → Maker-Checker)只需换配置文件

2. 计划-执行双层循环 + 动态重规划

平铺的 ReAct 循环在复杂任务中暴露了根本缺陷:每一步都是局部最优决策,缺乏全局视野,导致大量无效工具调用和路径偏离。

双层架构设计

┌──────────────────────────────────────────────────┐
│               Outer Loop: Planner                 │
│  ┌────────────────────────────────────────────┐  │
│  │  Generate high-level plan with steps        │  │
│  │  Step 1Step 2Step 3Step 4         │  │
│  └──────────────────┬─────────────────────────┘  │
│          │   │   │   │                             │
│          ▼   ▼   ▼   ▼                             │
│  ┌────────────────────────────────────────────┐  │
│  │        Inner Loop: Executor (per step)      │  │
│  │   Perceive → Reason → Act → Observe         │  │
│  │   On failure: signal to Outer Loop          │  │
│  └──────────────────┬─────────────────────────┘  │
│                     │                              │
│                     ▼                              │
│   Dynamic Re-plan based on executor result        │
└──────────────────────────────────────────────────┘

外循环 Planner:LLM 生成高层计划(步骤列表,带依赖关系)。Planner 不参与执行,仅负责战略。

内循环 Executor:逐步执行,每步后将结果反馈给外循环。若某步失败,外循环根据新状态动态修订剩余计划。

这本质上是 Plan-and-Solve 的增强版,加上 ReWOO 的核心思想 —— 把观测(Observation)与推理(Reasoning)脱钩,避免上下文被中间结果污染。

"""
双层循环 Agent:Plan-and-Execute + Dynamic Re-plan
"""

import json
from dataclasses import dataclass, field
from typing import Optional


@dataclass
class PlanStep:
    id: str
    description: str
    depends_on: list[str] = field(default_factory=list)
    status: str = "pending"  # pending | running | success | failed


@dataclass
class ExecutionPlan:
    steps: list[PlanStep]
    current_step_index: int = 0


class DualLoopAgent:
    """
    双层循环:
    - Outer loop: 生成计划 + 当执行失败时动态重规划
    - Inner loop: 逐步执行 + 将结果反馈给 Outer loop
    """

    def __init__(self):
        self.context: list[dict] = []
        self.plan: Optional[ExecutionPlan] = None

    def outer_loop(self, task: str) -> str:
        """Outer Loop:战略层 —— 规划与重规划"""

        # Phase 1: 生成高层计划
        plan_response = self._call_llm(
            system=(
                "You are a planner. Break the task into a JSON array of steps. "
                'Each step: {"id": "step_N", "description": "...", '
                '"depends_on": ["step_X"]}. '
                "Steps must be ordered. Only output JSON, no explanation."
            ),
            user=task,
        )
        steps_data = json.loads(plan_response)

        self.plan = ExecutionPlan(
            steps=[PlanStep(**s) for s in steps_data]
        )

        # Phase 2: 逐步执行(Inner Loop 调 outer)
        while self.plan.current_step_index < len(self.plan.steps):
            step = self.plan.steps[self.plan.current_step_index]
            step.status = "running"

            result = self.inner_loop(step)

            if result["success"]:
                step.status = "success"
                self.plan.current_step_index += 1
                self.context.append({
                    "step": step.id,
                    "result": result["output"],
                })
            else:
                step.status = "failed"
                # 动态重规划:根据失败原因修订剩余计划
                if not self._replan(step, result["error"]):
                    return f"Task failed at step {step.id}: {result['error']}"

        return "All steps completed successfully"

    def inner_loop(self, step: PlanStep) -> dict:
        """Inner Loop:战术层 —— 单步执行 + 迭代修正"""

        # 准备上下文:只包含当前步骤和已完成步骤的结果摘要
        inner_context = [
            {"role": "system", "content": f"Execute step: {step.description}"},
            {
                "role": "user",
                "content": f"Previous results: {json.dumps(self.context[-3:])}",
            },
        ]

        for attempt in range(5):
            result = self._execute_with_tools(inner_context)

            if result["success"]:
                return result

            # 把错误反馈注入下一轮
            inner_context.append({
                "role": "user",
                "content": f"Previous attempt failed: {result['error']}. "
                "Analyze the error and try a different approach.",
            })

        return {"success": False, "error": "Max inner loop iterations reached"}

    def _replan(self, failed_step: PlanStep, error: str) -> bool:
        """动态重规划:根据执行失败原因修订后续步骤"""

        remaining_steps = self.plan.steps[self.plan.current_step_index + 1 :]
        if not remaining_steps:
            return False

        replan_prompt = (
            f"Step '{failed_step.description}' failed with: {error}\n"
            f"Remaining steps to complete the task:\n"
            + "\n".join(f"- {s.description}" for s in remaining_steps)
            + "\n\nRevise or reorder the remaining steps. "
            "If the task is no longer achievable, return empty list."
        )

        response = self._call_llm(
            system="Revise the plan based on the failure. Output JSON array of steps.",
            user=replan_prompt,
        )

        new_steps = json.loads(response)
        if not new_steps:
            return False

        # 替换后续步骤
        self.plan.steps = (
            self.plan.steps[: self.plan.current_step_index + 1]
            + [PlanStep(**s) for s in new_steps]
        )
        self.plan.current_step_index += 1
        return True

    def _call_llm(self, system: str, user: str) -> str:
        # 简化的 LLM 调用接口
        import openai
        client = openai.OpenAI()
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": system},
                {"role": "user", "content": user},
            ],
        )
        return response.choices[0].message.content

    def _execute_with_tools(self, context: list[dict]) -> dict:
        # 简化的工具执行接口
        return {"success": True, "output": "done"}

与 ReWOO 的对比

ReWOO (Reasoning WithOut Observation) 的核心洞察是:观测数据(工具输出)不应混入推理过程——它会把推理链污染成 " 我看到 X,所以做 X",而非 " 基于目标,我需要 X"。

双层循环天然支持这个模式:Inner Loop 负责 " 脏活 "(工具调用 + 观测),Outer Loop 只接收摘要结果做战略决策。两者的上下文窗口保持干净。

效果(社区报告数据):

  • 无效工具调用减少约 40-60%
  • 复杂多步任务的成功率提升约 25%
  • 最长可处理 30+ 步的复杂任务链

3. 事件驱动与流式循环

传统 Agent Loop 是严格的串行模式:" 请求 → 完整响应 → 解析 → 执行 → 下一轮请求 "。每个环节都要等上一个完成,端到端延迟是各环节之和。

事件驱动循环把这个范式彻底翻转:LLM 每吐出部分结构化动作的片段,就立刻触发工具调用,工具执行的同时 LLM 还在继续生成。

Traditional Loop:
LLM generate ──→ parse tool calls ──→ execute tools ──→ concat ──→ LLM generate ──→ ...
[───── wait ─────][── wait ──][── wait ──]

Event-driven Loop:
LLM streaming ───────────────────────────────────────────→
 ├─→ tool call fragment arrives → async execute tool ──→
 │      ├─→ result event pushed
 ├─→ continue generating ───────────────────────────────→
 │      ├─→ state update triggered
 └─→ more tool calls → async execute ───────────────────→
        └─→ next round decision

流式工具调用解析

OpenAI 和 Anthropic 的 API 在 2025 年已经支持流式工具调用(streaming tool calls)。当模型决定调用工具时,API 以增量块的形式推送函数名、参数片段,客户端可以在参数还没有完整接收时就发起准备。

"""
流式循环引擎:边生成边执行
"""

import asyncio
import json
from typing import AsyncIterator


class StreamingLoopEngine:
    """
    核心机制:
    1. LLM 流式输出 → 实时解析工具调用片段
    2. 工具调用立即异步执行
    3. 工具结果以事件形式推回决策循环
    """

    def __init__(self, event_bus=None):
        self.event_bus = event_bus or AsyncEventBus()
        self.pending_tools: dict[str, asyncio.Task] = {}
        self.tool_results: dict[str, str] = {}

    async def run(self, task: str, max_iterations: int = 20):
        """流式循环入口"""

        messages = [{"role": "user", "content": task}]
        accumulated_tool_calls: dict[int, dict] = {}

        for iteration in range(max_iterations):
            stream = await self._stream_llm(messages)

            async for chunk in stream:
                delta = chunk.choices[0].delta

                # 情况 1:纯文本增量
                if delta.content:
                    yield {"type": "text", "content": delta.content}

                # 情况 2:工具调用增量
                if delta.tool_calls:
                    for tc_delta in delta.tool_calls:
                        idx = tc_delta.index
                        if idx not in accumulated_tool_calls:
                            accumulated_tool_calls[idx] = {
                                "id": tc_delta.id or "",
                                "function": {"name": "", "arguments": ""},
                            }

                        if tc_delta.id:
                            accumulated_tool_calls[idx]["id"] = tc_delta.id
                        if tc_delta.function:
                            if tc_delta.function.name:
                                accumulated_tool_calls[idx]["function"][
                                    "name"
                                ] += tc_delta.function.name
                            if tc_delta.function.arguments:
                                accumulated_tool_calls[idx]["function"][
                                    "arguments"
                                ] += tc_delta.function.arguments

                        # 关键:参数够完整就立即发起异步执行
                        if self._args_complete(
                            accumulated_tool_calls[idx]
                        ):
                            await self._dispatch_tool_async(
                                accumulated_tool_calls[idx]
                            )

            # 等待所有待处理的工具调用完成
            results = await self._gather_tool_results()

            # 拼接结果进入下一轮
            messages.append({
                "role": "assistant",
                "tool_calls": list(accumulated_tool_calls.values()),
            })
            for tc_id, result in results.items():
                messages.append({
                    "role": "tool",
                    "tool_call_id": tc_id,
                    "content": result,
                })

            accumulated_tool_calls.clear()

            # 如果模型只返回文本且无工具调用,结束
            if not results:
                break

    def _args_complete(self, tool_call: dict) -> bool:
        """判断工具调用参数是否足够完整以开始执行"""
        try:
            json.loads(tool_call["function"].get("arguments", ""))
            return True
        except json.JSONDecodeError:
            return False

    async def _dispatch_tool_async(self, tool_call: dict):
        """异步派发工具调用,立即返回不阻塞"""
        tc_id = tool_call["id"]
        if tc_id in self.pending_tools:
            return

        task = asyncio.create_task(self._execute_tool(tool_call))
        self.pending_tools[tc_id] = task

    async def _execute_tool(self, tool_call: dict) -> str:
        """执行工具(可能涉及网络 IO)"""
        name = tool_call["function"]["name"]
        args = json.loads(tool_call["function"]["arguments"])
        # 实际工具执行逻辑...
        result = f"Executed {name} with {args}"
        self.tool_results[tool_call["id"]] = result
        return result

    async def _gather_tool_results(self) -> dict:
        """等待所有异步工具调用完成,返回结果"""
        results = {}
        for tc_id, task in list(self.pending_tools.items()):
            try:
                await task
                results[tc_id] = task.result()
            except Exception as e:
                results[tc_id] = f"Error: {e}"
        self.pending_tools.clear()
        return results

    async def _stream_llm(self, messages) -> AsyncIterator:
        """流式调用 LLM API"""
        import openai
        client = openai.AsyncOpenAI()
        return await client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=[...],
            stream=True,
        )

事件总线架构(生产级)

在生产环境中,多个 Agent 可能并行运行,工具调用涉及多个微服务。事件总线模式将循环控制器从中心化调用者转变为事件消费者:

"""
基于 AsyncIO + 事件队列的生产级流式循环
"""

class EventDrivenLoop:
    """
    事件驱动循环引擎:
    - llm_events: LLM 输出事件流
    - tool_events: 工具执行结果事件流
    - 循环控制器订阅两个事件流,根据事件类型驱动状态变更
    """

    def __init__(self):
        self.llm_events: asyncio.Queue = asyncio.Queue()
        self.tool_events: asyncio.Queue = asyncio.Queue()
        self.state = {"step": 0, "status": "running"}

    async def controller(self, task: str):
        """主控制器:事件消费者 + 分发者"""
        # 启动 LLM 生产者
        asyncio.create_task(self._llm_producer(task))

        while self.state["status"] == "running":
            # 同时监听两个事件源
            done, pending = await asyncio.wait(
                [
                    asyncio.create_task(self.llm_events.get()),
                    asyncio.create_task(self.tool_events.get()),
                ],
                return_when=asyncio.FIRST_COMPLETED,
            )

            for task_done in done:
                event = task_done.result()
                if event["source"] == "llm":
                    await self._handle_llm_event(event)
                elif event["source"] == "tool":
                    await self._handle_tool_event(event)

            # 取消未完成的等待
            for p in pending:
                p.cancel()

    async def _llm_producer(self, task: str):
        """LLM 生产者:流式输出 → 推送事件"""
        stream = await self._stream_llm(task)
        async for chunk in stream:
            await self.llm_events.put({
                "source": "llm",
                "type": "chunk",
                "data": chunk,
            })
        await self.llm_events.put({
            "source": "llm",
            "type": "done",
        })

    async def _handle_llm_event(self, event):
        """处理 LLM 事件:解析工具调用 → 异步分发"""
        if event["type"] == "done":
            self.state["status"] = "evaluating"
            return

        tool_call = self._parse_tool_call(event["data"])
        if tool_call:
            # 异步执行工具,结果会通过 tool_events 回来
            asyncio.create_task(self._execute_and_emit(tool_call))

    async def _execute_and_emit(self, tool_call):
        """执行工具并将结果推入事件队列"""
        result = await self._execute_tool(tool_call)
        await self.tool_events.put({
            "source": "tool",
            "tool_call_id": tool_call["id"],
            "result": result,
        })

    async def _handle_tool_event(self, event):
        """处理工具结果事件:更新状态 → 决定下一步"""
        self.state["tool_results"] = self.state.get("tool_results", {})
        self.state["tool_results"][event["tool_call_id"]] = event["result"]

        # 可在此触发下一轮 LLM 调用或直接结束

端到端延迟对比

模式3 次工具调用延迟说明
传统串行LLM 生成 (2s) + Tool1(1s) + LLM(2s) + Tool2(1s) + LLM(2s) + Tool3(1s) = 9s严格串行
流式循环max(LLM 生成, Tool1+Tool2+Tool3 并行) ≈ 3-4s边生成边执行

4. 多 Agent 拓扑循环

单个 Agent 的循环效能有天花板。无论是推理深度、上下文窗口还是单一视角,单 Agent 都有不可逾越的局限。2026 年的主流实践是在一个更大的循环中嵌套多个 Agent,形成 " 协作循环 "。

4.1 管理者-工人循环(Manager-Worker Loop)

最经典也最稳健的多 Agent 拓扑。主 Agent(Manager)分配子任务,启动子 Agent Loop 并等待结果,自身保持监督循环,随时中断或重新分配。

"""
管理者-工人循环:主 Agent 协调多个子 Agent
"""

class ManagerWorkerLoop:
    """
    Manager 自身运行一个监督循环:
    1. 分析任务,拆解为子任务
    2. 分配给 Worker(创建子循环)
    3. 收集结果,评估质量
    4. 不满意则重新分配
    """

    def __init__(self):
        self.workers: dict[str, "WorkerAgent"] = {}
        self.task_queue: asyncio.Queue = asyncio.Queue()

    async def manage(self, main_task: str) -> str:
        """Manager 的主循环"""

        # Step 1: 拆解任务
        subtasks = await self._decompose(main_task)
        for st in subtasks:
            await self.task_queue.put(st)

        results = {}
        failed_tasks = []

        # Step 2: 分配循环(支持重分配)
        while not self.task_queue.empty() or failed_tasks:
            # 优先处理失败重试
            if failed_tasks:
                task = failed_tasks.pop(0)
                task["retry_count"] = task.get("retry_count", 0) + 1
            else:
                task = await self.task_queue.get()

            # 分配 Worker
            worker = await self._select_worker(task)
            worker_id = worker.id

            # 启动 Worker 子循环
            result = await worker.execute(task)
            results[task["id"]] = result

            # Step 3: 质量评估
            quality = await self._evaluate(task, result)
            if quality["pass"]:
                continue
            elif task.get("retry_count", 0) < 3:
                # 重新分配(可能换 Worker)
                task["feedback"] = quality["feedback"]
                failed_tasks.append(task)
            else:
                # 超过重试上限,升级为人工处理
                results[task["id"]]["escalated"] = True

        return self._summarize(results)

    async def _decompose(self, task: str) -> list[dict]:
        """LLM 拆解主任务为子任务列表"""
        response = await self._call_llm(
            system=(
                "Decompose the task into subtasks. Each subtask must be "
                "independently executable. Output JSON array with id and "
                "description. Specify dependencies if any."
            ),
            user=task,
        )
        return json.loads(response)

    async def _select_worker(self, task: dict) -> "WorkerAgent":
        """根据子任务特征选择最合适的 Worker"""
        # 可以基于 skills 匹配、当前负载、历史成功率
        pass

    async def _evaluate(self, task: dict, result: dict) -> dict:
        """评估 Worker 结果,返回 pass + feedback"""
        pass

4.2 生成-批评-修正循环(Generator-Critic-Reviser Loop)

输出 Agent 生成 → 批评 Agent 指出问题 → 生成 Agent 再次修改。形成一个内环迭代,直到质量过关。

"""
生成-批评-修正循环
三个角色各司其职,形成质量迭代内环
"""

class GeneratorCriticLoop:
    """
    内环结构:
    Generator → Critic → [PASS] → 输出
                       → [FAIL] → Generator(带批评意见)→ ...
    """

    def generate(self, task: str, max_rounds: int = 5) -> str:
        output = self._generator(task)

        for round_num in range(max_rounds):
            # 批评 Agent 独立评估(不同模型、不同温度)
            critique = self._critic(output, task)

            if critique["score"] >= 0.9:
                return output

            # 将批评注入 Generator 的下一轮
            output = self._generator(
                task,
                previous_output=output,
                feedback=critique["feedback"],
            )

        return output  # 达到最大轮次,返回当前最优

    def _generator(
        self,
        task: str,
        previous_output: str = None,
        feedback: str = None,
    ) -> str:
        """Generator: 高温度 (0.7-0.9),鼓励创造性"""
        prompt = f"Task: {task}"
        if previous_output:
            prompt += (
                f"\n\nYour previous output:\n{previous_output}"
                f"\n\nCritique: {feedback}"
                "\n\nRevise based on the critique."
            )
        return self._call_llm(prompt, temperature=0.8)

    def _critic(self, output: str, task: str) -> dict:
        """Critic: 低温度 (0.0-0.1),严格评估"""
        response = self._call_llm(
            system=(
                "You are a strict code reviewer. Evaluate the output "
                "against the original task. Return JSON: "
                '{"score": 0.0-1.0, "feedback": "specific issues"}'
            ),
            user=f"Task: {task}\n\nOutput to evaluate:\n{output}",
            temperature=0.0,
        )
        return json.loads(response)

4.3 ChatEval 式多 Agent 辩论循环

多个 Agent 并行独立回答,然后互相辩论多轮,最后裁判 Agent 汇总,整体作为一个大循环。

"""
多 Agent 辩论循环
"""

class DebateLoop:
    """
    辩论流程:
    1. 多个 Expert Agent 并行独立回答
    2. 互相审阅对方的回答,提出反驳
    3. 多轮辩论后,Judge Agent 汇总裁决
    """

    async def debate(self, question: str, num_experts: int = 3, rounds: int = 3):
        # Round 1: 独立回答
        answers = await asyncio.gather(*[
            self._expert_answer(question, expert_id=i)
            for i in range(num_experts)
        ])

        # Round 2-N: 互相辩论
        for round_num in range(1, rounds):
            # 每个 Expert 看到所有其他人的回答
            responses = await asyncio.gather(*[
                self._expert_rebut(
                    question=question,
                    my_answer=answers[i],
                    other_answers=[a for j, a in enumerate(answers) if j != i],
                    expert_id=i,
                )
                for i in range(num_experts)
            ])
            answers = responses

        # 最终裁决
        verdict = await self._judge(question, answers)
        return verdict

    async def _expert_answer(self, question: str, expert_id: int) -> str:
        return await self._call_llm(
            system=f"You are Expert {expert_id}. Give your best answer.",
            user=question,
            temperature=0.7,
        )

    async def _expert_rebut(
        self,
        question: str,
        my_answer: str,
        other_answers: list[str],
        expert_id: int,
    ) -> str:
        others_text = "\n---\n".join(
            f"Expert {j}: {a}" for j, a in enumerate(other_answers)
        )
        return await self._call_llm(
            system=(
                f"You are Expert {expert_id}. Review other experts' answers "
                "and refine your own. Point out flaws in others' reasoning."
            ),
            user=(
                f"Question: {question}\n"
                f"Your answer: {my_answer}\n"
                f"Other answers:\n{others_text}\n"
                "Provide your refined answer."
            ),
            temperature=0.6,
        )

    async def _judge(self, question: str, answers: list[str]) -> dict:
        return await self._call_llm(
            system=(
                "You are a judge. Synthesize all expert opinions and produce "
                "a final verdict. Output JSON with 'verdict' and 'reasoning'."
            ),
            user=f"Question: {question}\n\n"
            + "\n---\n".join(f"Expert {i}: {a}" for i, a in enumerate(answers)),
            temperature=0.0,
        )

拓扑选择指南

拓扑Token 成本质量提升适用场景
Manager-Worker中(子任务并行)大型多文件任务,有明确分工
Generator-Critic低(2x 调用)代码生成、文档写作
多 Agent 辩论高(N×M 轮)最高安全审计、设计评审、关键决策
层级委派中-高中-高大规模系统,需要递归拆解

5. 长时持久化与耐久执行

大多数 Agent 循环的生命周期不超过一次会话。服务器重启、进程崩溃、网络中断——任何中断都意味着从头开始。耐久执行(Durable Execution) 让 Agent 循环具备 " 系统重启后继续 " 的能力。

核心思路

每一步的状态写入持久化存储(数据库 / 对象存储),工作流引擎能重新唤起并从中断的节点继续循环,而不是重新运行整个任务。

基于 Temporal.io 的实现

Temporal 是目前工业界耐久执行的事实标准。每一步是一个 Activity,整个循环是一个 Workflow。

"""
基于 Temporal.io 的耐久执行 Agent Loop
服务器重启后自动从中断节点继续,无需重新运行
"""

from temporalio import workflow, activity
from temporalio.common import RetryPolicy
from dataclasses import dataclass
from typing import Optional


@dataclass
class LoopState:
    """持久化的循环状态 —— 每一步都写入 Temporal 的 Event History"""
    task: str
    current_step: int = 0
    max_steps: int = 20
    messages: list[dict] = None
    step_results: list[dict] = None


@activity.defn
async def llm_reason(state: LoopState) -> dict:
    """Activity:LLM 推理 —— Temporal 保证至多执行一次"""
    # 调用 LLM
    response = await openai_client.chat.completions.create(
        model="gpt-4o",
        messages=state.messages,
        tools=[...],
    )
    return response.choices[0].message.model_dump()


@activity.defn
async def execute_tool(tool_call: dict) -> str:
    """Activity:工具执行 —— 支持重试策略"""
    # 执行工具调用
    pass


@activity.defn
async def evaluate_result(results: list[dict]) -> dict:
    """Activity:结果评估"""
    # 评估是否达到目标
    pass


@workflow.defn
class DurableAgentLoop:
    """
    Workflow 层面的 Agent Loop。
    Temporal 自动持久化每一步状态。
    服务器崩溃后从上次成功节点恢复。
    """

    @workflow.run
    async def run(self, task: str) -> str:
        state = LoopState(task=task)
        state.messages = [{"role": "user", "content": task}]

        # 重试策略:工具调用失败时重试
        tool_retry = RetryPolicy(
            initial_interval=1.0,
            maximum_interval=60.0,
            maximum_attempts=3,
        )

        for state.current_step in range(state.max_steps):
            # 每个 Activity 调用都会自动持久化到 Event History
            response = await workflow.execute_activity(
                llm_reason,
                state,
                start_to_close_timeout=30.0,
            )

            # 检查是否任务完成
            eval_result = await workflow.execute_activity(
                evaluate_result,
                [{"response": response}],
                start_to_close_timeout=10.0,
            )

            if eval_result.get("complete"):
                return eval_result["summary"]

            # 执行工具调用(带重试)
            if response.get("tool_calls"):
                for tc in response["tool_calls"]:
                    tool_result = await workflow.execute_activity(
                        execute_tool,
                        tc,
                        retry_policy=tool_retry,
                        start_to_close_timeout=60.0,
                    )
                    state.messages.append({
                        "role": "tool",
                        "tool_call_id": tc["id"],
                        "content": tool_result,
                    })

        return f"Completed {state.max_steps} steps"


# 部署后,即使 Worker 进程崩溃,
# Temporal Server 会在另一个 Worker 上从中断点恢复执行

关键设计原则

  • State is the source of truth:循环状态(messages、step_results)必须序列化到持久化存储,而非停留在内存
  • Deterministic replay:Workflow 代码必须是确定性的(不能直接调用 randomdatetime.now() 等),Temporal 通过重放 Event History 恢复状态
  • Activity 幂等性:每个 Activity 调用至少在重放时是幂等的

替代方案对比

引擎适用场景复杂度亮点
Temporal.io生产级长时任务(数小时-数天)中高重放机制、多语言 SDK、可视化管理面板
Prefect数据管道 + Agent LoopPython 原生、观测性优秀
AWS Step Functions已有 AWS 基础设施的团队免运维、与 AWS 生态深度集成
Celery + Redis轻量级、快速原型部署简单、Python 生态成熟

6. 自优化循环:DSPy驱动的Loop工程

这项技术将 Loop Engineering 推向了元层面:把 Loop 本身当作可优化对象。在循环的各节点定义可学习的提示词和参数,利用 DSPy 等框架根据每个循环节点的成功 / 失败日志自动优化提示词和 few-shot 示例。

DSPy 在 Loop 优化中的应用

Traditional Loop Engineering:
Human design prompt → Loop run → Human observe failure → Human fix prompt → Loop run → ...

DSPy-driven Loop Engineering:
Human define metrics → Loop run → DSPy auto-collect success/failure logs
                                       ↓
                       Auto-optimize prompts, few-shot, model selection
                                       ↓
                       Loop run (new config) → success rate improved

DSPy 核心机制:将 LLM 调用抽象为 Signature(签名)Module(模块),通过编译器根据训练数据自动优化模块参数。

"""
DSPy 驱动的自优化 Loop:让循环内的每个决策节点自动进化
"""

import dspy
from dspy.teleprompt import BootstrapFewShot

# ============================================
# 定义循环内的可优化模块
# ============================================

class PlanGenerator(dspy.Signature):
    """签名:将任务描述转化为执行计划"""
    task = dspy.InputField(desc="The high-level task to complete")
    tools = dspy.InputField(desc="Available tools with descriptions")
    plan = dspy.OutputField(desc="JSON array of steps with dependencies")


class ToolSelector(dspy.Signature):
    """签名:根据当前状态选择下一步工具"""
    current_state = dspy.InputField(desc="Current state and partial results")
    available_tools = dspy.InputField(desc="Available tools")
    next_tool = dspy.OutputField(desc="Selected tool name")
    tool_args = dspy.OutputField(desc="Arguments for the selected tool")


class ResultEvaluator(dspy.Signature):
    """签名:评估工具输出是否满足当前步骤要求"""
    step_goal = dspy.InputField(desc="What this step should achieve")
    tool_output = dspy.InputField(desc="Output from tool execution")
    passed = dspy.OutputField(desc="True/False")
    reason = dspy.OutputField(desc="Reason for pass/fail")


# ============================================
# 构建可优化的 DSPy Module
# ============================================

class SelfOptimizingLoop(dspy.Module):
    """
    DSPy Module 封装的 Agent Loop。
    每个 LLM 调用节点都是可优化的。
    """

    def __init__(self):
        super().__init__()
        # 每个节点都可以被 DSPy 编译器优化
        self.planner = dspy.ChainOfThought(PlanGenerator)
        self.selector = dspy.ChainOfThought(ToolSelector)
        self.evaluator = dspy.ChainOfThought(ResultEvaluator)

    def forward(self, task: str, tools: list[str]) -> str:
        """执行一次完整的循环"""

        # 节点 1:规划
        plan = self.planner(task=task, tools=str(tools))

        results = []
        for step in json.loads(plan.plan):
            # 节点 2:工具选择
            selection = self.selector(
                current_state=str(results),
                available_tools=str(tools),
            )

            # 执行工具(非 LLM 部分,不可优化)
            tool_result = self._execute(selection.next_tool, selection.tool_args)

            # 节点 3:结果评估
            eval_result = self.evaluator(
                step_goal=step["description"],
                tool_output=tool_result,
            )

            results.append({
                "step": step,
                "result": tool_result,
                "passed": eval_result.passed,
                "reason": eval_result.reason,
            })

            if not eval_result.passed:
                break

        return str(results)

    def _execute(self, tool: str, args: str) -> str:
        """实际工具执行 —— 非 LLM 部分,不需要优化"""
        # ... 工具执行逻辑
        return "tool_result"


# ============================================
# 自动优化流程
# ============================================

def train_loop_with_examples():
    """用历史成功/失败数据训练 Loop"""

    # 准备训练数据:成功的任务执行轨迹
    trainset = [
        dspy.Example(
            task="Fix all ESLint errors in src/",
            tools='["read_file", "write_file", "run_lint"]',
            answer="success",  # 期望结果
        ).with_inputs("task", "tools"),
        # ... 更多训练样本
    ]

    # BootstrapFewShot:从成功轨迹中自动提取 few-shot 示例
    optimizer = BootstrapFewShot(
        metric=lambda example, pred, trace=None: 1.0 if "passed" in pred else 0.0,
        max_bootstrapped_demos=4,
    )

    # 编译优化
    optimized_loop = optimizer.compile(
        SelfOptimizingLoop(),
        trainset=trainset,
    )

    return optimized_loop

# 使用优化后的 Loop
optimized = train_loop_with_examples()
result = optimized(task="Fix all ESLint errors", tools=["read_file", "write_file", "run_lint"])

实际效果

社区报告数据(2026 Q2):

  • 任务成功率在无人工改 prompt 的情况下自动提升 15-35%
  • 原来需要 5-8 次迭代的任务降至 2-4 次
  • 特别适合需要反复执行同一类任务的场景(如每日 CI 修复)

DSPy 优化维度

DSPy 编译器可以优化以下维度:

维度说明编译器策略
Prompt 措辞系统提示的表述方式BootstrapFewShot 自动注入最优 few-shot
Few-shot 选择选哪些示例放入上下文基于任务相似度自动检索
Chain-of-Thought是否需要推理步骤自动添加 "Let's think step by step" 前缀
模型选择哪个模型在这个节点表现最好MIPROv2 自动模型选择
上下文长度多长的历史消息进入下一轮自动裁剪不相关的中间步骤

局限

  • 训练数据获取成本:需要积累足够的成功 / 失败轨迹
  • 编译器运行开销:BootstrapFewShot 需要多次 LLM 调用来优化,适用于稳定场景
  • 过度优化风险:优化后的 prompt 可能过度拟合训练数据中的模式

7. 声明式循环配置

Loop Engineering 的工程门槛正在被声明式配置大幅降低。核心思路:用 YAML/JSON 描述循环结构和策略,引擎解析配置直接生成循环运行时

配置驱动的好处

  • 策略即配置:换 Loop 策略只需换文件,无需改代码
  • 可版本控制:Loop 配置纳入 Git,可 diff、review、回滚
  • 快速实验:A/B 测试不同循环策略时,部署时间从小时级降到分钟级
  • 降低门槛:非工程角色也能理解和调整循环行为

完整配置示例

# loop.yaml —— 声明式 Loop 配置,一行不改代码即可切换策略

version: "2.0"
name: "daily-bug-fixer"

# ============ 循环基础配置 ============
loop:
  type: plan-execute  # react | plan-execute | maker-checker | ralph
  max_iterations: 30
  stop_conditions:
    - type: test_pass
      value: "all tests passing"
    - type: max_cost
      value: 5.0           # 最大 $5
    - type: no_progress
      consecutive_rounds: 3

# ============ 工具集 ============
tools:
  - name: read_file
    source: builtin
  - name: write_file
    source: builtin
    require_approval: true   # 写入文件需要人工审批
  - name: run_tests
    source: mcp
    endpoint: "http://ci-server/mcp/test-runner"
  - name: git_commit
    source: mcp
    endpoint: "http://git-server/mcp"

# ============ 模型配置 ============
models:
  planner:
    provider: anthropic
    model: claude-sonnet-4-20250514
    temperature: 0.3
    max_tokens: 4096
  executor:
    provider: openai
    model: gpt-4o
    temperature: 0.5
  checker:
    provider: anthropic
    model: claude-haiku-4-20250514
    temperature: 0.0

# ============ 子 Agent ============
sub_agents:
  - name: maker
    role: generator
    model: executor
    tools: [read_file, write_file, run_tests]
  - name: checker
    role: evaluator
    model: checker
    tools: [read_file, run_tests]
    independent: true       # 独立上下文窗口

# ============ 安全护栏 ============
guardrails:
  pre_action:
    - rule: "block destructive ops on src/"
      action: reject_and_replan
    - rule: "max file write per iteration: 3"
      action: reject_and_replan
  post_action:
    - rule: "check for secrets in output"
      action: redact_and_alert

# ============ 人工协同 ============
human_in_the_loop:
  triggers:
    - on: write_file
      paths: ["*.env", "*.key", "config/*"]
      action: await_approval
    - on: git_push
      action: await_approval
    - on: cost_exceeded
      threshold: 3.0
      action: notify_and_pause

# ============ 持久化 ============
persistence:
  engine: temporal           # temporal | prefect | redis
  state_file: ".loop/state.json"
  checkpoint_every: 5        # 每 5 步存档一次

# ============ 可观测性 ============
observability:
  tracing: langsmith
  metrics:
    - loop_iteration_count
    - tool_call_success_rate
    - cost_per_iteration
  alerts:
    - condition: "cost > $10/day"
      channel: "#ai-ops"

配置引擎实现

"""
声明式 Loop 配置引擎
解析 YAML → 生成运行时
"""

import yaml
from typing import Literal


class LoopConfigEngine:
    """根据 YAML 配置动态生成 Loop 运行时"""

    STRATEGIES = {
        "react": ReactLoop,
        "plan-execute": PlanExecuteLoop,
        "maker-checker": MakerCheckerLoop,
        "ralph": RalphLoop,
    }

    @classmethod
    def from_yaml(cls, config_path: str) -> "BaseLoop":
        with open(config_path) as f:
            config = yaml.safe_load(f)

        loop_type = config["loop"]["type"]
        strategy_cls = cls.STRATEGIES[loop_type]

        # 根据配置组装 Loop
        loop = strategy_cls(
            max_iterations=config["loop"]["max_iterations"],
            stop_conditions=config["loop"]["stop_conditions"],
            tools=cls._init_tools(config["tools"]),
            models=cls._init_models(config["models"]),
            guardrails=config["guardrails"],
            human_triggers=config["human_in_the_loop"]["triggers"],
        )

        # 如果有子 Agent 配置,注入
        if "sub_agents" in config:
            for sa_config in config["sub_agents"]:
                loop.register_sub_agent(
                    name=sa_config["name"],
                    role=sa_config["role"],
                    model=sa_config["model"],
                    tools=sa_config["tools"],
                    independent=sa_config.get("independent", False),
                )

        return loop

    @classmethod
    def _init_tools(cls, tool_configs: list[dict]) -> dict:
        """初始化工具:支持 builtin 和 MCP 协议"""
        tools = {}
        for tc in tool_configs:
            if tc["source"] == "builtin":
                tools[tc["name"]] = BUILTIN_TOOLS[tc["name"]]
            elif tc["source"] == "mcp":
                tools[tc["name"]] = MCPTool(tc["endpoint"])
        return tools

配置驱动的实验流程

# 快速切换循环策略进行 A/B 测试
# 不需要修改任何 Python 代码

# 策略 A:ReAct
claude loop --config loops/react-config.yaml task.md

# 策略 B:Plan-Execute
claude loop --config loops/plan-execute-config.yaml task.md

# 策略 C:Maker-Checker
claude loop --config loops/maker-checker-config.yaml task.md

# 对比三者的成功率、耗时、成本
claude loop compare --configs loops/*.yaml --task task.md


8. 可观测性驱动的循环断点与人工协同

生产环境中的 Loop 不能是一个黑箱。通过全链路 tracing 实时监控循环状态,运维人员可以在运行时动态注入断点、热修改策略,而不需要下线 Agent。

核心能力

  • 动态断点注入:看到某个 Agent 即将执行高风险操作,通过管理面板挂入人工审批
  • 热修改策略:在不下线 Agent 的情况下调整循环参数
  • 实时状态面板:可视化每个节点的执行时间、Token 消耗、成功率

基于 LangSmith + Weave 的可观测 Loop

"""
可观测 Loop:全链路 tracing + 动态断点 + 热修改
"""

import weave
from langsmith import traceable, run_tree


@weave.op
class ObservableLoop:
    """
    每个节点都被 weave 和 langsmith 追踪。
    运维人员可以在管理面板看到实时状态并注入断点。
    """

    def __init__(self, config: dict):
        self.config = config
        # 热修改支持:从配置中心实时拉取参数
        self.hot_config = HotConfigSource()

    @traceable(run_type="chain", name="agent_loop")
    async def run(self, task: str):
        """主循环 —— 每个 iteration 都是一个 trace span"""

        state = {"task": task, "iteration": 0}

        while state["iteration"] < self.config["max_iterations"]:
            # 检查是否有运维注入的断点
            breakpoint = await self._check_breakpoint(state)
            if breakpoint:
                approval = await self._request_human_approval(
                    breakpoint
                )
                if not approval["approved"]:
                    state["status"] = "paused_by_human"
                    return state

            # 执行一步
            step_result = await self._execute_step(state)

            # 记录 metric
            weave.metrics.increment("loop.iterations")
            weave.metrics.record("loop.cost", step_result["cost"])

            # 检查热修改
            new_config = await self.hot_config.get()
            if new_config != self.config:
                weave.logger.info(f"Hot-reloading config: {new_config}")
                self.config = new_config

            state["iteration"] += 1

        return state

    @traceable(run_type="tool", name="execute_step")
    async def _execute_step(self, state: dict) -> dict:
        """单步执行 —— 独立的 trace span"""
        # LLM 调用 + 工具执行
        pass

    async def _check_breakpoint(self, state: dict) -> dict | None:
        """检查管理面板是否注入了断点"""
        # 从 Redis / 配置中心读取断点配置
        breakpoints = await redis_client.get("loop:breakpoints")
        for bp in breakpoints:
            if bp["condition"](state):
                return bp
        return None

    async def _request_human_approval(self, breakpoint: dict) -> dict:
        """向管理面板发送人工审批请求"""
        # 通过 WebSocket 通知前端
        await ws_manager.send(
            channel=f"loop:{self.loop_id}",
            message={
                "type": "breakpoint",
                "step": breakpoint["step"],
                "action": breakpoint["action"],
                "reason": breakpoint["reason"],
            },
        )
        # 等待人工响应(带超时)
        return await ws_manager.wait_for_response(timeout=300)

管理面板能力清单

能力说明实现方式
实时火焰图每个节点的耗时占比LangSmith Trace View
动态断点在下一步挂起,等待人工确认WebSocket + Redis 断点配置
热修改参数修改 max_iterations / temperature配置中心(etcd / Consul)实时推送
成本仪表盘实时 Token 消耗 + 预算预警Weave / LangSmith dashboards
回放与调试对历史轨迹逐步回放Temporal Replay / LangSmith Playground
策略 A/B同时运行多个策略版本对比基于 trace tag 分流

9. 安全护栏子循环

在金融、医疗等强监管行业,Agent 的每一个行动都必须经过安全审核。安全护栏子循环在每一个行动前后嵌入一个轻量的安全检查循环,形成 " 行动前审核 → 行动后审计 " 的双层防线。

架构设计

┌──────────────────────────────────────────────┐
│               Main Agent Loop                 │
│                                              │
│  ┌────────────────────────────────────────┐  │
│  │        Pre-Action Safety Loop          │  │
│  │  Check: permission? budget?            │  │
│  │         compliance?                    │  │
│  │              ↓                         │  │
│  │    PASS → Execute      FAIL            │  │
│  │              ↓           ↓             │  │
│  │          Action    Request replan      │  │
│  └────────────────────────────────────────┘  │
│                      ↓                        │
│  ┌────────────────────────────────────────┐  │
│  │       Post-Action Audit Loop           │  │
│  │  Check: sensitive info leak?           │  │
│  │         compliance?                    │  │
│  │              ↓                         │  │
│  │    PASS → Continue      FAIL           │  │
│  │              ↓            ↓            │  │
│  │        Next Step    Block + Alert      │  │
│  └────────────────────────────────────────┘  │
└──────────────────────────────────────────────┘

实现

"""
安全护栏子循环:每个 Action 前后的双层安全检查
"""

from enum import Enum
from dataclasses import dataclass


class SafetyVerdict(Enum):
    ALLOW = "allow"
    BLOCK = "block"
    REQUIRE_REPLAN = "require_replan"
    REDACT = "redact"


@dataclass
class SafetyRule:
    name: str
    description: str
    severity: str  # critical | high | medium
    check_fn: callable


class SafetyGuardLoop:
    """
    安全护栏子循环:
    - 行动前:权限、预算、合规检查
    - 行动后:敏感信息、输出审核
    """

    def __init__(self):
        self.pre_rules: list[SafetyRule] = [
            SafetyRule(
                name="block_destructive",
                description="Block destructive operations on critical paths",
                severity="critical",
                check_fn=self._check_destructive,
            ),
            SafetyRule(
                name="budget_limit",
                description="Check if action would exceed budget",
                severity="high",
                check_fn=self._check_budget,
            ),
            SafetyRule(
                name="permission_check",
                description="Verify agent has permission for this action",
                severity="critical",
                check_fn=self._check_permission,
            ),
            SafetyRule(
                name="rate_limit",
                description="Check API rate limits",
                severity="high",
                check_fn=self._check_rate_limit,
            ),
        ]
        self.post_rules: list[SafetyRule] = [
            SafetyRule(
                name="no_secrets",
                description="Scan output for secrets (API keys, tokens, passwords)",
                severity="critical",
                check_fn=self._scan_for_secrets,
            ),
            SafetyRule(
                name="pii_detection",
                description="Check for personally identifiable information",
                severity="critical",
                check_fn=self._check_pii,
            ),
            SafetyRule(
                name="output_size",
                description="Verify output size is within limits",
                severity="medium",
                check_fn=self._check_output_size,
            ),
            SafetyRule(
                name="compliance",
                description="Verify output against compliance policies",
                severity="high",
                check_fn=self._check_compliance,
            ),
        ]

        # 安全事件日志
        self.safety_log: list[dict] = []

    async def pre_action_check(
        self,
        action: dict,
        agent_state: dict,
    ) -> SafetyVerdict:
        """行动前安全检查循环"""

        for rule in self.pre_rules:
            verdict = await rule.check_fn(action, agent_state)

            self.safety_log.append({
                "phase": "pre",
                "action": action["name"],
                "rule": rule.name,
                "verdict": verdict,
                "timestamp": time.time(),
            })

            if verdict == SafetyVerdict.BLOCK:
                # 阻断 + 告警
                await self._alert(
                    f"BLOCKED: {action['name']} by rule {rule.name}"
                )
                return SafetyVerdict.BLOCK

            if verdict == SafetyVerdict.REQUIRE_REPLAN:
                # 要求 Agent 重新规划
                return SafetyVerdict.REQUIRE_REPLAN

        return SafetyVerdict.ALLOW

    async def post_action_audit(
        self,
        action: dict,
        output: str,
    ) -> SafetyVerdict:
        """行动后审计循环"""

        for rule in self.post_rules:
            verdict = await rule.check_fn(output)

            self.safety_log.append({
                "phase": "post",
                "action": action["name"],
                "rule": rule.name,
                "verdict": verdict,
                "timestamp": time.time(),
            })

            if verdict == SafetyVerdict.BLOCK:
                await self._alert(
                    f"POST-BLOCKED: output of {action['name']} "
                    f"flagged by {rule.name}",
                    severity=rule.severity,
                )
                return SafetyVerdict.BLOCK

            if verdict == SafetyVerdict.REDACT:
                # 自动脱敏后继续
                output = await self._redact(output, rule.name)

        return SafetyVerdict.ALLOW

    # --- 具体规则实现 ---

    async def _check_destructive(
        self, action: dict, state: dict
    ) -> SafetyVerdict:
        """阻断对关键路径的破坏性操作"""
        CRITICAL_PATHS = [
            "/etc/",
            "C:\\Windows\\",
            ".git/",
            "production/",
        ]
        if action["name"] in ("delete", "rm", "drop"):
            target = action.get("args", {}).get("path", "")
            for cp in CRITICAL_PATHS:
                if cp in target:
                    return SafetyVerdict.BLOCK
        return SafetyVerdict.ALLOW

    async def _check_budget(
        self, action: dict, state: dict
    ) -> SafetyVerdict:
        """预算检查"""
        estimated_cost = self._estimate_cost(action)
        current_spend = state.get("total_cost", 0)
        budget = state.get("budget", float("inf"))

        if current_spend + estimated_cost > budget:
            return SafetyVerdict.BLOCK
        return SafetyVerdict.ALLOW

    async def _scan_for_secrets(self, output: str) -> SafetyVerdict:
        """扫描输出中的敏感信息"""
        import re
        patterns = [
            r"[A-Za-z0-9_]{20,}==",       # Base64 token
            r"sk-[A-Za-z0-9]{32,}",        # OpenAI API key
            r"ghp_[A-Za-z0-9]{36}",        # GitHub token
            r"AKIA[0-9A-Z]{16}",           # AWS Access Key
            r"password\s*[:=]\s*\S+",      # Password in plaintext
        ]
        for pattern in patterns:
            if re.search(pattern, output):
                return SafetyVerdict.REDACT
        return SafetyVerdict.ALLOW

    async def _check_pii(self, output: str) -> SafetyVerdict:
        """PII 检测"""
        import re
        patterns = [
            r"\b\d{3}-\d{2}-\d{4}\b",                              # SSN
            r"\b\d{16}\b",                                          # Credit card
            r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",  # Email
        ]
        for pattern in patterns:
            if re.search(pattern, output):
                return SafetyVerdict.REDACT
        return SafetyVerdict.ALLOW

    async def _alert(self, message: str, severity: str = "high"):
        """发送安全告警"""
        # 推送 Slack / PagerDuty / 安全运营中心
        pass

    async def _redact(self, output: str, rule_name: str) -> str:
        """自动脱敏"""
        # 用 [REDACTED] 替换敏感内容
        return output

集成到主循环

class SafeAgentLoop:
    """嵌入了安全护栏子循环的 Agent Loop"""

    def __init__(self):
        self.guard = SafetyGuardLoop()

    async def run(self, task: str):
        state = {"task": task, "iteration": 0, "total_cost": 0}

        for state["iteration"] in range(50):
            # Agent 推理 + 规划
            action = await self._plan_next_action(state)

            # 行动前安全检查
            pre_verdict = await self.guard.pre_action_check(action, state)

            if pre_verdict == SafetyVerdict.BLOCK:
                # 阻断:记录事件,尝试重新规划
                state["blocked"] = True
                continue

            if pre_verdict == SafetyVerdict.REQUIRE_REPLAN:
                # 要求重规划:将拒绝原因注入上下文
                state["messages"].append({
                    "role": "user",
                    "content": f"Action {action['name']} rejected. "
                    "Plan a different approach.",
                })
                continue

            # 执行行动
            output = await self._execute_action(action)

            # 行动后审计
            post_verdict = await self.guard.post_action_audit(action, output)

            if post_verdict == SafetyVerdict.BLOCK:
                # 输出阻断:告警 + 停止
                break

            # 更新状态
            state["messages"].append({
                "role": "tool",
                "content": output,
            })

行业落地清单

行业必选护栏可选护栏
金融操作权限矩阵、交易金额上限、合规审计日志PII 检测、反洗钱规则
医疗HIPAA 合规、PHI 数据脱敏、审计追踪处方剂量校验、药物相互作用
SaaSAPI 密钥扫描、用户数据隔离、速率限制内容安全审核、GDPR 合规
基础设施破坏性操作确认、变更窗口检查容量检查、回滚预案验证

总结:2026 年 Loop Engineering 技术选型矩阵

维度推荐方案为什么
控制流复杂度轻量图 + 代码节点拓扑可见、逻辑可调试、策略可切换
任务复杂度双层循环 + 动态重规划复杂任务成功率提升 25%,无效调用减少 50%
延迟敏感流式循环 + 异步事件总线端到端延迟从 9s 降到 3s
质量要求Generator-Critic 或 多 Agent 辩论质量提升显著,Token 成本可控
可靠性要求Temporal.io 耐久执行服务器崩溃后自动恢复,不丢状态
持续改进DSPy 自优化循环无需人工改 prompt,自动提升 15-35%
工程门槛声明式配置策略即配置,可 Git、可 diff、可实验
运维可控可观测 Loop + 动态断点运行时注入审批,不下线调整参数
安全合规安全护栏子循环每个 Action 前后双层防线,行业强制要求

Loop Engineering 在 2026 年已经不再是一个概念讨论,而是有清晰工程方案的实践领域。选择哪条技术路线,取决于你的任务类型、团队能力和合规要求。九大做法可以组合使用——例如用 Temporal 做耐久执行的底,在上面搭建双层循环,用流式事件驱动执行,最后用安全护栏包裹每一个行动。

这不是让工作变简单,而是让工作变深刻。设计 Loop 的判断力是你的解药,逃避思考的惯性是你的陷阱。同一个动作,相反的结果。