> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# AI代理的本质：循环调用LLM
- URL: https://www.hubwiz.com/blog/the-essense-of-ai-agents-llms-in-a-loop/
- Published: 2025-12-28T13:30:32.000Z
- Updated: 2025-12-28T13:30:32.000Z
- Description: “AI代理”听起来很复杂，但通常核心比你想象的要简单：AI代理通常在一个循环中返回调用大型语言模型（LLM）。
- Author: admin
- Tags: AGENT, #Import 2026-10-14 14:41

“AI代理”听起来很复杂，但通常核心比你想象的要简单。**AI代理通常在一个循环中运行大型语言模型（LLM）。**

这段伪代码用JavaScript捕捉了本质：

```
// 环境保存状态/上下文
const env = { state: initialState }; // 简单对象状态
// 代理可用的工具
const tools = new Tools(env);
// LLM的指令
const system_prompt = `你的主要任务、目标、限制...`;

// 主代理循环
while (true) {
  // （需要一个真正的退出条件！）
  // 1. LLM大脑：根据提示+状态决定动作
  const action = llm.run(`${system_prompt} ${env.state}`);

  // 2. 系统双手：运行请求的工具的实际代码
  env.state = tools.run(action); // 用结果更新状态
}

```

*这个简单的循环是代理的核心。*

![](https://www.hubwiz.com/blog/content/images/2025/12/image-378.png)

*简化的周期：思考 -> 行动 -> 更新状态 -> 重复。*

快速分解：

- `**llm.run(...)**`: “大脑”。使用指令（`system_prompt`） + 当前情况（`env.state`）来决定下一步的`action`。
- `**tools.run(action)**`: “双手”。如果`action`请求了一个工具，这会执行该工具的**实际代码**并更新`env.state`。

循环重复，将新状态反馈给LLM。

## 1、为什么需要工具？

因为LLM只会说话。

LLM只输出文本。**它不能直接做事情** – 没有浏览、没有文件编辑、没有运行命令。它需要“双手”。

**工具**就是那些双手。它们是系统在被要求时为LLM执行的函数，使它能够与世界互动。

## 2、定义和使用一个工具

LLM如何知道工具是什么以及何时使用它们？

### 2.1 工具定义

每个工具需要传递给LLM的清晰定义，详细说明：

- **名称:** 唯一ID（例如，`runLinter`）。
- **描述:** 工具是什么以及做什么（例如，“在JS代码/文件上运行ESLint...”）。
- **输入参数:** 需要的*确切*输入（每个的名称、类型、描述）。

这个定义告诉LLM工具的能力以及如何为其结构化请求。

```
// 传递给LLM API的示例工具定义
{
  type: "function",
  function: {
    name: "runLinter", // 唯一名称
    description: "在JS代码/文件上运行ESLint，返回JSON错误或成功消息。", // 描述
    parameters: { /* 输入参数模式 */ }
  }
}

```

![](https://www.hubwiz.com/blog/content/images/2025/12/image-379.png)

*用名称、描述和参数为LLM定义一个工具*

### 2.2 何时使用工具：系统提示

主要的`system_prompt`为代理提供了核心指令和策略。

至关重要的是，它告诉LLM**何时**和**如何**使用它的工具。它列出了可用的工具并设定了使用规则。

> *示例:* “你有`runLinter`工具。*始终*首先运行它... 如果发现错误，修复代码，然后再次运行`runLinter`以验证，然后再完成。”

这确保了LLM在循环内有效地使用工具。

## 3、动手实践：构建一个简单的lint代理

让我们看看它如何运作。我们将逐步介绍一个简单的、可工作的Node.js代理的关键部分，该代理使用**一个单一工具**来修复JavaScript linting错误。

你可以在[这里](https://github.com/cloudx-labs/simple-linter-agent?ref=hubwiz.com)找到*完整、可运行的代码*（包括完整的系统提示）。

以下是关键部分：

### 3.1 系统提示（`src/config.js` \- 缩略版）

此片段展示了代理编程的结构和关键指令。

```
// src/config.js - 系统提示（缩略版）
const config = {
  model: "gpt-4o",
  systemPrompt: `
你是一个帮助修复linting错误的专家JavaScript助手...

可用工具：
- runLinter({ codeContent?: string, filePath?: string }): 执行ESLint...

遵循的流程：
1. 接收代码/路径。
2. **始终**首先使用 'runLinter'...
3. 分析错误...
4. 如果没有错误，返回代码...
5. 如果有错误：
    a. 修改代码...
    b. **重要：** 再次调用 'runLinter' 以验证...
    c. 如果验证通过，返回更正后的代码...
    d. 如果仍然有错误，在尝试后返回最佳努力...

最终响应：
你的最终响应必须只包含完整的更正后的代码...严格包裹在 <final_code>...</final_code> 中...

// （存储库代码中的工具调用和最终响应示例的完整细节）
`,
};

export default config;

```

### 3.2 工具（`src/tools/linter.js` \- 函数片段）

这显示了系统执行的*实际* `runLinter` 函数的核心逻辑，省略了一些样板代码以提高清晰度。

```
// src/tools/linter.js - 核心工具函数（简化版）
import { ESLint } from "eslint";
import fs from "fs"; // 仍需要用于上下文

const runLinter = async ({ codeContent, filePath }) => {
  // --- 确定代码来源和处理临时文件逻辑 ---
  // ... 获取 codeToLint 和管理 useFilePath 的代码 ...
  // ... 包括 fs.readFileSync/writeFileSync 逻辑 ...

  try {
    // --- 核心 ESLint 执行 ---
    const eslint = new ESLint({ fix: false, useEslintrc: true });
    const results = await eslint.lintFiles([
      /* 确定的文件路径 */
    ]);

    // --- 处理结果 ---
    const errors = results.flatMap(/* ... 将结果映射到错误对象 ... */);

    // --- 清理和返回 ---
    // ... 如果使用了临时文件则删除 ...
    return errors.length === 0
      ? { result: "未找到任何 linting 错误！" }
      : { errors: JSON.stringify(errors) };
  } catch (err) {
    // --- 错误处理和清理 ---
    // ... 删除临时文件 ...
    console.error("运行 ESLint 时出错:", err);
    return { error: `ESLint 执行错误: ${err.message}` };
  }
};

// --- 导出的定义供代理/LLM 使用 ---
export default {
  name: "runLinter",
  description: "在 JS 代码/文件上运行 ESLint...",
  parameters: {
    /* ... 参数模式 ... */
  },
  function: runLinter, // 链接到上面的实际函数
};

```

### 3.3 循环（src/agent/agentInvoker.js - 核心逻辑片段）

此片段突出了代理的执行流程：调用LLM，处理工具调用，并更新状态。

```
// 在 src/agent/agentInvoker.js 中（简化的核心循环）
import linterTool from "../tools/linter.js";
import config from "../config.js";
import memory from "./memory.js";
// ... 其他函数: callLLM(messages), handleToolCall(toolCall) ...

const tools = [/* ... 使用 linterTool 的工具定义结构 ... */];

const invokeAgent = async (conversationId, inputs) => {
  // ... 设置初始用户消息在 memory 中 ...

  const MAX_ITERATIONS = 3;
  let finalCode = null;

  // === 代理循环 ===
  for (let i = 0; i < MAX_ITERATIONS; i++) {
    const messages = memory.getMessages(conversationId); // 获取状态

    // --- 1. LLM 大脑：决定动作 ---
    const llmResponse = await callLLM(messages);
    const assistantMessage = llmResponse.choices[0].message;
    memory.addMessage(conversationId, assistantMessage); // 存储想法

    if (assistantMessage.tool_calls) {
      // --- 2. 系统双手：执行工具 ---
      for (const toolCall of assistantMessage.tool_calls) {
        if (toolCall.function.name === linterTool.name) {
           const toolResult = await handleToolCall(toolCall); // 运行 runLinter
           const toolResultContent = /* ... 格式化结果 ... */ ;
           // --- 3. 更新状态 ---
           memory.addMessage(conversationId, { role: "tool", /*...*/ content: toolResultContent });
        }
      }
    } else if (assistantMessage.content) {
      // --- LLM 提供了最终答案 ---
      const match = /* ... 检查 <final_code> ... */ ;
      if (match) { finalCode = match[0]; break; } // 目标达成！
    } else { /* ... 处理错误 ... */ break; }
  } // === 结束循环 ===

  // ... 处理循环结束 ...
  return finalCode;
};

```

这种结构展示了**LLM（规划） + 循环（重复） + 工具（行动）** 的模式。

## 4、连接到更复杂的代理（如 Cursor）

我们的简单 linter 代理只使用了一个工具，但它展示了基本模式。现实世界的代理如 **Cursor** 操作在完全相同的原则上，只是扩展了。

想象一下，你让 Cursor 来“重构 `ComponentA.jsx` 以使用新的 `useDataFetching` hook 并在 `ComponentA.test.js` 中更新其测试。” Cursor 的 `LLM` 大脑，由其自己的复杂系统提示引导，可能在其循环中协调如下序列：

1. **循环 1:** `LLM` 思考：“需要 `ComponentA.jsx`。” -> **动作:** 调用 `readFile(path="...")`。系统运行它。
2. **循环 2:** `LLM` 思考：“需要 `ComponentA.test.js`。” -> **动作:** 调用 `readFile(path="...")`。系统运行它。
3. **循环 3:** `LLM` 思考：“计划 JSX 更改。” -> **动作:** 调用 `editFile(path="...", changes=[...])`。系统运行它。
4. **循环 4:** `LLM` 思考：“计划测试更改。” -> **动作:** 调用 `editFile(path="...", changes=[...])`。系统运行它。
5. **循环 5:** `LLM` 思考：“验证更改。” -> **动作:** 调用 `runTests(path="...")`。系统运行它。
6. **循环 N:** （继续...）

这是相同的**思考 -> 行动（工具）-> 更新状态 -> 重复** 周期，只是有更多的工具（`readFile`, `editFile`, `runTests` 等）和更复杂的策略。核心的**LLM + 循环 + 工具** 架构保持不变。

## 5、实用的结论

忘记关于“AI代理”的复杂炒作。核心通常是这种简单的**LLM + 循环 + 工具**模式：

1. **LLM 思考**（使用系统提示 + 工具定义 + 当前状态）
2. **系统行动**（运行请求的工具的实际代码）
3. **重复**

这是一种简单而强大的方式，使 LLM 完成现实世界中的任务。

---

原文链接：[Forget the Hype: Agents are Loops](https://dev.to/cloudx/forget-the-hype-agents-are-loops-1n3i?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处