AI Agent 控制流实战指南:别再堆 Prompt 了,写代码吧

发布日期: 2026-05-08 • 阅读: 8 分钟 • 标签: AI Agent, 控制流, LLM 编排, 状态机, prompt 工程

2026 年 5 月,一篇名为 "Agents Need Control Flow, Not More Prompts" 的文章在 Hacker News 上获得 400+ 讨论。

核心论点直击当下 AI Agent 开发的痛点:你用 MANDATORY、DO NOT SKIP、CRITICAL 这些大写单词堆出来的 prompt,本质上只是在碰运气

本文把这篇文章的核心思想翻译成可实操的工程方案——怎么从"写更好的 prompt"转型到"写更好的控制流代码"。

Prompt 的极限在哪里

想象一种编程语言:它的语句是"建议",函数可能返回"成功"但实际上啥都没做。你能在这种语言上构建可靠的复杂系统吗?不能。

但这就是我们用 prompt 写 AI Agent 的现状:

这不是 LLM 的错。这是工具选型的错。软件工程之所以可靠,靠的是递归可组合性:函数调用函数,模块调用模块,每一步的行为都是确定性的。Prompt chain 缺少这个核心性质。

当你发现自己开始在 prompt 里写 IMPORTANT!!! 甚至 ⚠️⚠️⚠️ 的时候,就该停下来想想了——你已经碰到了 prompt 工程的天花板。

核心思路:把 LLM 当组件,而不是当系统

可靠 Agent 的架构应该遵循一个原则:逻辑走代码,创造力走 LLM

具体来说,你需要把 Agent 的流程拆成三层:

┌─────────────────────────────────┐
│   Layer 3: Orchestration Layer  │  ← 状态机 / DAG / while 循环(代码控制)
│   ┌───────────────────────────┐ │
│   │  Layer 2: Validation Layer │  ← 断言检查 / schema 校验 / 边界检测
│   └───────────────────────────┘ │
│   ┌───────────────────────────┐ │
│   │  Layer 1: LLM Layer        │  ← 纯 prompt,只做生成/推理
│   └───────────────────────────┘ │
└─────────────────────────────────┘

每一层各司其职。LLM 只负责"产生内容"和"做推理",控制流、校验、重试全部交给代码。

模式一:状态机驱动 Agent

这是最基础也最实用的模式。把 Agent 的工作流建模为有限状态机:

States: [PLAN, EXECUTE, VERIFY, REVISE, DONE]
Transitions:
  PLAN → EXECUTE     (规划完成)
  EXECUTE → VERIFY   (执行完成)
  VERIFY → DONE      (验证通过)
  VERIFY → REVISE    (验证失败)
  REVISE → EXECUTE   (修正完成)
  VERIFY → PLAN      (需要重新规划)
  REVISE → FAILED    (修正次数超限)

用 TypeScript 实现大概长这样:

type AgentState = 'plan' | 'execute' | 'verify' | 'revise' | 'done' | 'failed';
class AgentWorkflow {
  private state: AgentState = 'plan';
  private retryCount = 0;
  private readonly maxRetries = 3;
  async run(task: string): Promise<Result> {
    while (this.state !== 'done' && this.state !== 'failed') {
      switch (this.state) {
        case 'plan':
          this.plan = await llm.plan(task);
          this.state = 'execute';
          break;
        case 'execute':
          this.output = await llm.execute(this.plan);
          this.state = 'verify';
          break;
        case 'verify':
          const valid = await this.validate(this.output);
          if (valid) {
            this.state = 'done';
          } else if (this.retryCount < this.maxRetries) {
            this.retryCount++;
            this.state = this.decideReviseOrReplan();
          } else {
            this.state = 'failed';
          }
          break;
        case 'revise':
          this.output = await llm.revise(this.output, this.feedback);
          this.state = 'verify';
          break;
      }
    }
    return this.state === 'done' ? success(this.output) : failure();
  }
}

关键点:状态转换是确定性的。LLM 不决定下一步做什么,它只负责在给定状态下生成内容。流程的决定权在代码手里。

模式二:校验检查点(Validation Checkpoints)

这是最容易立刻上手的改进。在每次 LLM 调用之后插入一个代码级的校验:

async function safeLLMCall<T>(
  prompt: string,
  schema: z.ZodSchema<T>,
  options?: { maxRetries?: number }
): Promise<T> {
  const maxRetries = options?.maxRetries ?? 2;
  for (let i = 0; i <= maxRetries; i++) {
    const raw = await llm.generate(prompt);
    try {
      // 尝试解析 JSON
      const parsed = JSON.parse(raw);
      // Zod schema 校验
      return schema.parse(parsed);
    } catch (e) {
      if (i === maxRetries) throw new ValidationError(e);
      // 把校验失败信息传给 LLM 让它修正
      prompt += `\n\nPrevious attempt failed: ${e.message}\nPlease fix and retry.`;
    }
  }
}

这个模式的精髓在于:校验是代码干的,不是 LLM 干的。Zod / Pydantic / JSON Schema 是确定性的——通过就是通过,不通过就是不通过。你不应该让 LLM 自己判断自己的输出对不对。

常见的校验类型:

模式三:结构化错误恢复

Agent 一定会出错。区别在于你怎么设计恢复策略。

三种常见的恢复策略:

1. 重试 + 反馈注入(Return with Feedback)

校验失败时,把具体的失败信息作为 feedback 重新发给 LLM。因为 LLM 能看到"我之前做错了什么",修正成功率远高于从头再生成一次。

# 效果对比
# 无反馈重试: 正确率 65% → 68% (几乎没用)
# 带反馈重试: 正确率 65% → 91% (显著提升)

2. 退化回退(Graceful Degradation)

复杂任务失败后,降级到更简单但更可靠的方案:

try:
    result = await agent.complex_reasoning(task)
except MaxRetriesExceeded:
    # 降级到规则匹配
    result = await rule_based_fallback(task)
# 甚至:
# Agent 失败 → 模板匹配 → 人工兜底

3. 人类介入(Human-in-the-Loop)

对于高风险场景,在 Agent 报错或置信度过低时,把控制权交给人。这是最老土但最稳妥的方案。

工具生态:现成的控制流框架

2026 年已经有不少工具帮你实现上述模式,不需要从头造轮子:

选择建议:

常见反模式

以下做法看起来对,实际上会让你的 Agent 越来越不可靠:

可观测性:没有日志就没有可靠性

如果生产环境的 Agent 出了问题你却不知道是哪一步失败的,那它算不上可靠。

至少需要记录这些信息:

推荐使用 OpenTelemetry 标准追踪,或者至少在每个状态转换时打结构化日志。

# 最少日志格式
{"timestamp":"...","event":"state_transition","from":"plan","to":"execute",
 "duration_ms":2300,"tokens":1450,"task_id":"..."}
{"timestamp":"...","event":"validation_failed","step":"verify",
 "reason":"schema_mismatch","retry_count":1,"task_id":"..."}

总结

如果你从这篇文章只记住一件事,那就是:prompt 是给 LLM 看的,控制流是给代码写的

每次你想往 system prompt 里加一个 IMPORTANT 时,停下来想一下——这个逻辑能不能用 if 语句、for 循环、状态机或者 try/catch 来实现?如果可以,那就写在代码里。

LLM 擅长的是创造性推理和内容生成。让 LLM 做 LLM 擅长的事,让代码做代码擅长的事。这样你的 Agent 既聪明又可靠。


参考阅读:Agents need control flow, not more prompts (bsuh.bearblog.dev)

← 返回博客列表