别再手动改 Prompt 了!用 AI Loop 让 AI 自己跟自己较劲,直到满意为止

0 阅读10分钟

🤖 别再手动改 Prompt 了!用 AI Loop 让 AI 自己跟自己较劲,直到满意为止

一条推文,700 万人围观。Claude Code 作者公开说:"我不写 prompt,我用 Loop。"
这篇文章,帮你从"改稿工具人"变成"AI 闭环设计师"。


📌 导语:你才是那个"循环"

打开 ChatGPT,输入 Prompt,回车。AI 吐出一段文字。你扫一眼,不满意,删掉重写 Prompt,再回车。AI 又吐出一段。你还是不满意,再改……

这场景太熟悉了,以至于我们从来没意识到一个问题:明明被循环的是 AI,真正在循环里转不出去的,是你。

每一次"不满意→改 Prompt→再发",都是一次迭代。但执行迭代的是你,不是 AI。你成了那个 while 循环里的引擎,而 AI 只是循环体里一行被动执行的代码。

这很荒谬。计算机最擅长的事就是循环——CPU 每秒重复几十亿次"取指令→执行→取下一指令",大模型也是靠万亿次循环训练出来的。但到了我们手上,这个最擅长循环的工具,却被用成了"一次性的生成器"。

AI Loop 要翻转的,就是这个关系。

你不再站在循环里当那个喊"再来一次"的人。你退到循环外面,设计好三件事:目标是什么、什么算合格、什么时候该停。然后让 AI 自己在循环里转,直到交出让你满意的东西。

你从"操作者"变成了"设计者"。从"改稿工具人"变成了"闭环架构师"。


一、先让大脑"叮"一下的事实 🧠

计算机最底层的本事,就是循环。你按开机键,主板就开始跑 while(true),直到你关机。这个死循环里,CPU 一刻不停地"取指令 → 执行 → 取下一指令",每秒重复几十亿次。

我们平时聊天用的 DeepSeek、Claude、GPT,它们是怎么变聪明的?也是循环喂出来的:拿海量数据给模型看 → 算一下错了多少 → 调整参数 → 再来一轮。万亿次循环后,AI 才学会了对话和推理。

Loop 包含三件事(三个要素):

要素问题缺了会怎样
1️⃣ 从哪里开始初始状态是什么?跑不起来
2️⃣ 重复做什么每轮干什么活?没有意义
3️⃣ 什么时候停满足什么条件退出?死循环,跑崩为止

但有意思的是:我们用 AI 的方式,却几乎没人用循环。 我们把最擅长干重复活的 AI,用成了"一次性工具"。这就像请了个世界级大厨,却只让他给你煮泡面——还每次都得你亲自告诉他"水开了没、面软了没"。


二、99% 的人正在用的"旧模式":人在环内 👤

回想一下你平时怎么用 AI:

打开 ChatGPT / Claude
  → 写一段 Prompt → 看结果 → 不满意?→ 手动改 Prompt → 再发一次 → ……直到你觉得"算了就这样吧"

我们把工作丢给了 AI,但自己是那个推动循环的引擎——没有你的判断和修改,AI 一步就停了。这就是 人在环内(Human in the Loop)

你做的事AI 做的事
✍️ 写 Prompt生成一个结果
👀 判断好不好等你下指令
🔧 修改 Prompt生成下一个结果
🔄 再判断再等...

这个模式的问题在哪?你把最宝贵的精力花在了重复劳动上。 更亏的是,AI 其实完全有能力自己判断、自己修正——我们却把它当成了"指一步动一步"的提线木偶。


三、翻转思路:人在环外 🚀

把上面那张表翻转过来:别让人当循环引擎,让 AI 自己闭环。

生成 → 检查 → 没通过?再生成 → 再检查 → …… → 通过,退出

你提前做好三件事,然后就可以走开:

你做的事对应代码大白话
📋 告诉 AI 你要什么task.desc目标(写一篇小红书文案)
📏 告诉 AI 怎么算合格task.rules规则(标题带数字、正文<300字、有行动号召)
🛡️ 设定安全边界limit最多试几次、最多花多少钱
以前(人在环内)现在(人在环外)
执行者AI 干一步就停AI 循环干到底
判断者你手动看AI 自己检查
你做什么反复改 Prompt一次性定目标、定规则
循环引擎AI

四、用一个生活例子秒懂 🌰

想象你是一个新店长,要训练客服回复话术。

旧模式:你亲自盯每个客服,每回复一条消息,你看看合不合格,不合格就让他重写。一天下来你嗓子哑了,手边还攒了 200 条未读。

新模式:你写一份《客服回复手册》,里面写清楚"语气要亲切、不能超 100 字、结尾必须有解决方案"。然后把手册发给客服,告诉他:"每条消息按手册自查,不合格就自己改,改到符合手册为止才提交。" 你只需要抽查最终结果。你从"监工"变成了"规则制定者"。

AI Loop 做的就是这件事——只不过那个"客服"是 AI,那个"手册"是 task.rules


五、不到 100 行的完整示例(可直接运行)📁

5.1 文件结构

loop-demo/
├── .env          ← 放你的密钥(不提交 git)
├── package.json
└── main.mjs      ← 全部代码,不到 100
// package.json
{
  "dependencies": {
    "openai": "^6.44.0",
    "dotenv": "^17.4.2"
  }
}

5.2 环境变量

# .env
DEEPSEEK_API_KEY=你的key
DEEPSEEK_BASE_URL=https://api.deepseek.com

六、逐行拆解:Loop 三要素在代码里长什么样 👨‍💻

整个程序的核心是一个 runLoop() 函数。先看整体调用关系:

                     runLoop()  ← 入口
                        │
          ┌─────────────┼─────────────┐
          │             │             │
        task         limit        needStop()   ← 要素一、三:目标+刹车
    (从哪开始)    (什么时候停)
          │
    ┌─────┴─────┐
    │           │
  gen()      check()     ← 要素二:重复做什么(生成+校验)
 (生成)      (打分)
    │           │
    └─────┬─────┘
          │
      while 循环

要素一:从哪里开始?定义任务目标和规则 📝

import { OpenAI } from 'openai'
import dotenv from 'dotenv'
dotenv.config();

const client = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: process.env.DEEPSEEK_BASE_URL,
})

// 👇 这就是要素一:告诉 AI 要什么 + 怎么算合格
const task = {
    desc: "小红书美妆文案",
    rules: [
        "标题要带上数字",
        "正文<300字",
        "结尾有行动号召"
    ]
}

desc 是方向,rules 是尺子。两者分开的好处是:后面 gen()check() 都从这个对象读数据,改任务只需要改这一个地方。rules 用数组存,后面用 join("、") 自动拼成自然语言塞进 Prompt。


要素二:重复做什么?gen() + check() 双函数 ⚙️

// 干活:调用 AI 生成文案
async function gen() {
    const res = await client.chat.completions.create({
        model: "deepseek-chat",  // 可换成你的模型
        messages: [{
            role: "user",
            content: `假如你是一位资深小红书美妆博主,
                      写一篇${task.desc},严格遵守${task.rules.join("、")}。

                      【输出格式要求】
                      第一行必须是:标题:xxx
                      正文从第二行开始
                      结尾单独一行,用emoji引导行动号召

                      只输出文案,不要多余的解释`
        }]
    })
    const text = res.choices[0].message.content;
    console.log(`生成token数:${res.usage.total_tokens}\n${text}`);
    return { text, token: res.usage.total_tokens };
}

⚠️ 注意 1:输出格式乱变
每次运行 AI 输出格式可能不一样——有时 【标题】xxx,有时 标题:xxx,有时干脆没"标题"二字。上面的 【输出格式要求】 就是为了锁死格式,防止 check 解析失败。如果发现格式还是乱,可以进一步收紧 prompt,比如加一句"违反格式直接判定为不合格"。

// 裁判:调用 AI 检查文案是否达标
async function check(text) {
    const res = await client.chat.completions.create({
        model: "deepseek-chat",
        messages: [{
            role: "user",
            content: `校验这篇文案:${text}
                      对照规则:${task.rules.join("、")}
                      仅输出JSON,格式:{"pass":true,"fail":[]}
                      不要输出其他任何内容`
        }]
    })
    return JSON.parse(res.choices[0].message.content.trim());
}

⚠️ 注意 2:gen 里的规则只是"提示",不是"强制"
gen() 的 prompt 里写了 严格遵守${task.rules.join("、")},但这只是"告诉 AI 规则",AI 可能嘴上答应实际上产出违规内容——所以才需要 check() 再验一遍。gen 里的规则是提示,check 才是守门员。 别以为 gen 写了规则就可以省掉 check。

⚠️ 注意 3:JSON.parse 可能因多余字符报错
AI 返回的 JSON 前后可能带空格或换行,上面用 .trim() 处理了。但如果 AI 在 JSON 外面包了 markdown 代码块,.trim() 也救不了。更健壮的做法见下方「拓展优化建议」。

要素二 = gen + check 双函数,每轮循环跑一次。gen 产出,check 打分——AI 自己当选手又当裁判

check 返回的 JSON 长这样:

{"pass": false, "fail": ["标题没有数字", "正文超过300字"]}
// 或
{"pass": true, "fail": []}

要素三:什么时候停?三道安全刹车线 🛑

// 👇 刹车配置 —— 三道防线
const limit = { maxRound: 5, maxToken: 2000, sameStep: 2 };

// 计数器 —— 追踪当前状态
let round = 0, totalToken = 0, lastText = "", sameCount = 0;

// 👇 刹车判断 —— 任一触发就停
function needStop() {
    return round >= limit.maxRound           // 防线1:轮数耗尽
        || totalToken >= limit.maxToken      // 防线2:预算花光
        || sameCount >= limit.sameStep;      // 防线3:AI 卡死(连续输出相同内容)
}

async function runLoop() {
    console.log(`🚀 AI Loop 开始`);
    while (!needStop()) {
        round++;
        console.log(`\n📌 第${round}轮`);

        const { text, token } = await gen();        // 1. 干活
        totalToken += token;
        sameCount = text === lastText ? sameCount + 1 : 0;
        lastText = text;

        const { pass, fail } = await check(text);   // 2. 检查
        if (pass) {
            console.log(`✅ 全部规则通过,循环结束 🎉`);
            console.log(`📄 最终文案:\n${text}`);
            return;   // 达标!愉快退出
        }
        console.log(`❌ 不满足规则:${fail.join("、")}`);  // 不通过,进入下一轮
    }
    console.log(`\n⛔ 触发刹车强制停止,最后一次内容:${lastText}`);
}

runLoop();  // ← 程序从这里启动

要素三 = 三道刹车线:

刹车线防什么大白话
maxRound: 5死循环"试了 5 次还不对?放弃"
maxToken: 2000超预算"花太多钱了,停"
sameStep: 2AI 卡死"连续 2 次输出一样?它在糊弄"

sameStep 的计数逻辑:一旦 textlastText 不同就归零,必须是连续相同才刹车——偶尔一次重复不算。

轮次lastText本次text判断sameCount
1"""文案A"不同0
2"文案A""文案A"相同1
3"文案A""文案A"相同2 → 刹车

七、实际跑起来长什么样?🎬

最理想——一次通过:

🚀 AI Loop 开始
📌 第1轮
生成token数:320
标题:3步搞定夏日不脱妆!油皮亲妈底妆大法🔥
正文:夏天到了……
结尾:👉 评论区告诉我你的肤质,我来帮你选!

✅ 全部规则通过,循环结束 🎉
最终文案:……

需要多轮修正:

📌 第1轮
标题:超好用底妆大法(缺少数字)
❌ 不满足规则:标题没有数字、正文超过300字

📌 第2轮
标题:3步搞定夏日底妆...
✅ 全部规则通过,循环结束 🎉

第一轮被打回,第二轮自动修正——全程不用人插手


八、拓展优化建议 🔧

上面这个 Demo 能跑通,但离"生产可用"还差几步。以下是三个常见的优化方向:

优化 1:check() 返回的 JSON 被包在 Markdown 代码块里

AI 有时候不听话,虽然 prompt 说了"仅输出JSON,不要输出其他任何内容",它偶尔还是会返回:

当然可以,以下是校验结果:
```json
{"pass": true, "fail": []}

`.trim()` 也救不了,`JSON.parse` 直接炸。

**解决:** 用正则把 JSON 从返回内容里抠出来:

```js
async function check(text) {
    const res = await client.chat.completions.create({
        model: "deepseek-chat",
        messages: [{ role: "user", content: `校验这篇文案:${text}...` }]
    })
    const raw = res.choices[0].message.content;
    const match = raw.match(/\{[\s\S]*\}/);   // 匹配第一个 { 到最后一个 }
    if (!match) throw new Error("check 返回内容中没有找到 JSON");
    return JSON.parse(match[0].trim());
}

优化 2:API 调用没有重试,网络波动直接崩

gen()check()await client.chat.completions.create(...) 都没包 try-catch,万一 DeepSeek 瞬时限流或网络抖一下,整个程序就挂了。

解决: 加个简单的重试:

async function gen() {
    for (let i = 0; i < 3; i++) {
        try {
            const res = await client.chat.completions.create({...});
            return { text: res.choices[0].message.content, token: res.usage.total_tokens };
        } catch (e) {
            if (i === 2) throw e;  // 3次都失败就真挂了
            console.log(`gen 第${i + 1}次失败,1秒后重试...`);
            await new Promise(r => setTimeout(r, 1000));
        }
    }
}

check() 同理加上重试逻辑。

优化 3:check() 太贵了——每轮都要调用一次 AI

目前的实现里,每生成一次就要调用一次 check,相当于每一轮花两份 token 的钱(gen 一份 + check 一份)。对简单任务来说,这个成本可能翻倍。

解决思路 A:规则优先用程序校验

能用代码判断的规则就别交给 AI。比如:

  • "正文<300字" → text.length < 300
  • "标题要带上数字" → /\d/.test(title)

只有程序判不了的主观规则(如"大爆款"、"语气亲切")才交给 AI 去 check。

async function check(text) {
    const fails = [];
    // 程序校验
    if (text.length > 300) fails.push("正文超过300字");
    // ... 其他可编程规则
    
    // 只有程序判不了的才调 AI
    if (需要AI判断的规则.length > 0) {
        const aiResult = await aiCheck(text);
        fails.push(...aiResult.fail);
    }
    return { pass: fails.length === 0, fail: fails };
}

解决思路 B:check() 精简 prompt

把 check 的 prompt 压到最短,用更少的 token。比如用英文或缩写:

Validate this text against rules. Output JSON: {"pass":bool,"fail":[]}. Rules: ...

解决思路 C:放宽 check 频率

不是每一轮都 check,而是每 2 轮或每 3 轮 check 一次。缺点是可能浪费几轮在错误方向上——适合 gen 本身就比较稳定、只需要最终把关的场景。


九、这个模式还能用在哪儿?🔧

任何 "生成 → 校验 → 重来" 的任务都能套用:

场景gen(生成)check(校验)
💻 写代码AI 写功能代码跑单元测试
🌐 翻译翻译英文回译检查 + 语法检查
📊 写周报生成草稿检查字数、格式、指标
➗ 数学题给出解答验算答案
💬 客服回复生成话术检查语气和合规

三步定制你自己的 AI Loop:

// 只改这两个对象 + gen 里的 prompt,其他代码不动
const task = { desc: "你的目标", rules: ["规则1", "规则2", "规则3"] };
const limit = { maxRound: 5, maxToken: 2000, sameStep: 2 };
  1. ✏️ 改 task —— 写清楚目标 + 合格规则
  2. 💰 改 limit —— 给预算,别让 AI 跑飞
  3. 🎭 改 gen() 里的 Prompt —— 换角色和场景

其他代码基本不用动。


十、核心思想:你从"操作者"变成"设计者" 🧠

维度旧模式(人在环内)新模式(人在环外)
你的工作反复改 Prompt、盯结果一次性定目标、定规则
AI 行为干一步就停循环干到底
你消耗的精力token(预算可控)

以前你写 Prompt 是在"告诉 AI 怎么做",现在你设计的是一个自动达标系统——告诉 AI 要什么、什么算好,然后让它自己跟自己较劲。

关键不是 AI 有多强,而是你设计的这个闭环有多聪明。


🎯 行动起来

今天就可以试试:

  1. 打开终端,复制上面的代码,跑一次感受流程
  2. 把你日常一个重复性 AI 任务(写周报、翻译、代码审查)改造成 Loop
  3. 在评论区分享:你打算用 AI Loop 改造哪个场景?