AI Agent 必考知识点:记忆机制、任务规划、工具调度完全拆解

HTMLPAGE 团队
26 分钟阅读

从“Agent 为什么会失控”出发,系统拆解记忆(窗口/摘要/外部知识)、规划(拆解/校验/纠错)、工具调度(超时/重试/幂等/并发)三大模块,并给出可落地的最小 Demo 路径与评估指标。

#AI Agent #记忆机制 #任务规划 #工具调用 #可靠性

如果你把 AI Agent 理解为“LLM + 一堆工具”,你会在两个地方翻车:

  1. 系统不可控:它会跑偏、会胡说、会乱调用工具。
  2. 系统不可靠:外部 API 超时、限流、错误返回时,你的 Agent 会直接崩溃或给出更糟的结果。

面试官考 Agent,核心就是在考:你是否理解“把不确定模型变成可控系统”的方法。

这篇文章不讨论“某个框架怎么用”,而是用工程语言把 Agent 的三大模块讲透:

  • 记忆:你到底把什么信息交给模型?怎么压缩?怎么检索?怎么隔离?
  • 规划:你怎么把大任务拆成可执行的步骤?怎么检测跑偏?怎么纠错?
  • 工具调度:你怎么在真实世界的不稳定条件下执行?怎么保证幂等?怎么做并发控制?

读完你至少应该能做到:

  • 在白板上画出一个 Agent 的执行环(loop)和状态机
  • 解释每个环节的失败模式,以及“工程化兜底”
  • 设计一个最小 Demo,并写出一组可验证指标

一、先建立共识:Agent 的最小架构是什么?

你可以把 Agent 的执行看成一个循环(loop):

  1. 感知(Perception):读取用户输入 + 当前状态 + 相关记忆
  2. 决策(Decision):生成计划/下一步行动(plan/action)
  3. 执行(Action):调用工具或输出文本,并写入状态/记忆

最常见的“失控”发生在:决策与执行之间缺少约束,或状态与记忆不可验证。

为了更工程化地讨论,我们把一次执行称为一次 run

run(user_input) => {
  context = build_context(user_input, session_state, memory)
  plan_or_action = decide(context)
  result = act(plan_or_action)
  validated = validate(result)
  record(trace, metrics)
  return output
}

关键点:每一步都应该产生可记录、可回放、可比较的证据,否则你无法迭代。


二、记忆机制:不是“存聊天记录”,而是“管理状态与证据”

2.1 你会遇到的三个硬约束

  1. 上下文窗口有限:你不可能把全部历史、全部文档都塞进去。
  2. 信息密度不均:一段对话里 90% 是噪声,10% 是约束条件。
  3. 多用户隔离:任何串话都是灾难级 bug。

所以“记忆”不是一个组件名,而是一组策略。

2.2 记忆的三层分工(面试必考)

建议用“三层记忆”去答题:

层级你存什么解决什么问题常见坑
短期上下文(Short-term)最近 N 轮原文保持语境太长导致成本高/窗口爆炸
工作记忆(Working memory)摘要 + 结构化状态(目标/约束/已完成步骤)控制任务执行摘要丢关键约束,导致跑偏
外挂记忆(External)RAG 证据片段、业务数据、工具结果补事实,降幻觉检索噪声、引用不透明

面试官最爱追问的是:为什么需要“结构化状态”?

答案是:纯自然语言摘要无法当作“系统状态”,它不可校验、不可 diff、不可约束。

一个典型的结构化状态示例:

{
  "goal": "生成出货单 PDF",
  "constraints": {
    "language": "zh-CN",
    "currency": "USD",
    "deadline": "2026-03-02"
  },
  "progress": [
    {"step": "收集订单信息", "done": true},
    {"step": "校验地址", "done": false}
  ],
  "permissions": ["read:orders", "write:pdf"],
  "last_tool_errors": []
}

结构化状态的价值:

  • 可校验:缺字段就补,字段不合法就拒绝。
  • 可约束:下一步计划必须引用这些约束。
  • 可回放:你能解释“为什么走到这一步”。

2.3 窗口管理:摘要不是答案,摘要需要测试

很多系统的“记忆失败”不是因为没做摘要,而是因为摘要不可靠。

建议你在工程里把摘要当成一个可测试模块:

  • 输入:完整历史
  • 输出:摘要 + 结构化状态
  • 测试:摘要必须保留的关键字段(目标、约束、已完成步骤、风险)

你可以为摘要定义一个“必须保留项”检查:

function assertSummary(summary: { goal?: string; constraints?: Record<string, unknown> }) {
  if (!summary.goal) throw new Error('summary.missing_goal')
  if (!summary.constraints) throw new Error('summary.missing_constraints')
}

这类“显得很工程”的做法,在面试里非常加分:你把 LLM 当成不稳定依赖,而不是神。

延伸阅读:


三、任务规划:Agent 不是“想一想”,而是“可执行计划 + 校验 + 纠错”

3.1 为什么规划是 Agent 的分水岭

没有规划的 Agent 很像“高级聊天机器人”:它能回答,但难以完成复杂任务。

有规划的 Agent 才像“系统”:它能把目标拆成步骤,并在失败时回退。

规划的工程化要求是:

  1. 计划必须结构化(能被程序解析)
  2. 每一步有成功条件(否则永远不知道是否完成)
  3. 允许纠错与重规划(任务拆错是常态)

3.2 最小可用的计划结构

下面的结构足够覆盖大部分面试题:

{
  "steps": [
    {
      "id": "s1",
      "tool": "searchDocs",
      "input": {"query": "shipping address validation rules"},
      "success": "找到规则并返回引用片段"
    },
    {
      "id": "s2",
      "tool": "validateAddress",
      "input": {"orderId": "..."},
      "success": "返回 status=valid 或 errors 列表"
    }
  ],
  "stop": {"maxSteps": 6, "deadlineMs": 45000}
}

你要强调:计划不是写给人看的,是写给系统执行的。

3.3 “拆错了怎么办”:规划的纠错闭环

这是面试官最常用来区分层次的问题。

一个成熟的答法是:把纠错拆成 3 个阶段。

  1. 检测:怎么知道跑偏?
    • 输出不满足 schema/业务规则
    • 工具返回的证据不足
    • 计划步骤数异常增长(无限循环征兆)
  2. 定位:错在哪?
    • 是目标理解错?(意图识别)
    • 是证据不足?(检索/数据缺失)
    • 是工具失败?(参数/权限/超时)
  3. 修复:如何回到可控状态?
    • 重新构建上下文(补证据/补约束)
    • 重规划(plan repair)
    • 降级(解释性答复 + 让用户补信息)

工程上可以非常朴素:

  • 最多重规划 1-2 次,超出就停止并请求人工/用户输入。
  • 每次重规划必须写明原因(记录到 trace),否则无法复盘。

四、工具调度:Function Calling 的本质是“受控执行”

4.1 工具调度为什么比 prompt 更重要

在真实系统里,最糟糕的失败不是“模型答错”,而是:

  • 模型调用了不该调用的工具(越权)
  • 工具被重复调用造成副作用(重复下单、重复写库)
  • 工具失败导致系统崩溃,用户无反馈

所以面试官会盯着问:你如何保证工具调用可控?

4.2 四件工程必需品:超时、重试、幂等、并发控制

1) 超时(Timeout)

  • 每个 tool 调用都要有单独 timeout
  • 整个 run 也要有 deadline(防止无限耗时)

2) 重试(Retry)

重试不是“失败就再来一次”。至少要做到:

  • 只对可重试错误重试(网络抖动、429、临时 5xx)
  • 指数退避(避免雪崩)
  • 上限(比如最多 2 次)

3) 幂等(Idempotency)

凡是有副作用的工具(写库、下单、发邮件),必须有幂等键:

const idempotencyKey = `${runId}:${stepId}`
await tools.createInvoice({ orderId }, { idempotencyKey })

4) 并发控制(Concurrency)

  • 同一用户同一资源,避免并发写冲突
  • 全局并发上限,避免打爆下游
  • 按租户/用户做隔离

4.3 工具参数错误:把“修复”做成回路

你会遇到这种情况:模型生成了无效参数。

成熟做法不是“失败就算了”,而是把错误回传给模型,让它修复:

  1. schema 校验失败 → 生成可读错误(缺字段/类型错/越界)
  2. 把错误作为约束重新喂给模型 → 让它产生新参数
  3. 限制重试次数,防止死循环

这背后的关键思想:不要让模型直接接触世界,让系统作为“执行防火墙”

延伸阅读:


五、一个可落地的最小 Demo 路径(面试官最爱看这个)

你不需要写一个“万能 Agent”,只需要一个能证明你理解三大模块的 Demo。

5.1 Demo 目标

做一个“文档 + 工具”的任务型 Agent,例如:

  • 用户输入:帮我根据订单号生成出货单
  • Agent:检索规则(RAG)→ 调用工具查询订单 → 校验地址 → 生成 PDF

5.2 最小模块划分

  • MemoryStore:存结构化状态 + 摘要
  • Retriever:RAG 检索证据
  • Planner:生成结构化计划
  • Executor:执行步骤(工具调度)
  • Validator:schema/业务规则校验
  • RunLogger:trace + metrics

5.3 伪代码骨架(面试可直接画)

interface ToolCall {
  tool: string
  input: Record<string, unknown>
}

interface PlanStep {
  id: string
  call: ToolCall
  success: string
}

interface Plan {
  steps: PlanStep[]
  stop: { maxSteps: number; deadlineMs: number }
}

async function runAgent(userInput: string, sessionId: string) {
  const runId = crypto.randomUUID()
  const startedAt = Date.now()

  const state = await memory.load(sessionId)
  const evidence = await retriever.search(userInput)
  const context = buildContext({ userInput, state, evidence })

  const plan: Plan = await planner.makePlan(context)
  validatePlan(plan)

  for (const step of plan.steps.slice(0, plan.stop.maxSteps)) {
    if (Date.now() - startedAt > plan.stop.deadlineMs) throw new Error('run.deadline_exceeded')

    const result = await executor.execute(step, { runId })
    const ok = await validator.check(step, result)
    logger.recordStep({ runId, step, result, ok })

    if (!ok) {
      // 纠错:让 planner 解释失败原因并重规划(上限 1-2 次)
      return await recoverOrDegrade({ runId, sessionId, userInput, state, evidence, step, result })
    }
  }

  return logger.finalize(runId)
}

你不需要把每个模块都写到生产级,但你要能解释:

  • state 是什么,怎么校验
  • evidence 怎么来,如何引用
  • plan 为什么要结构化
  • executor 如何处理 timeout/retry/幂等
  • validator 如何定义成功条件

六、评估与指标:让“变好”有证据

6.1 最小离线评估(你可以 1 天做出来)

准备一个 eval_cases.json

  • 10 条简单任务(无工具、无检索)
  • 20 条工具任务(带失败注入)
  • 20 条检索任务(故意混入噪声文档)

每次改 prompt / 改检索 / 改规划后,跑一遍回放,比较:

  • 完成率
  • 平均步数
  • 工具失败导致的终止比例
  • 引用证据比例

6.2 建议你在面试里能报出这 6 个数字

  1. success_rate:任务完成率
  2. p95_latency_ms:P95 延迟
  3. avg_llm_calls:平均模型调用次数
  4. tool_timeout_rate:工具超时比例
  5. citation_rate:引用证据比例
  6. cost_per_run:每次 run 的成本估算

没有真实用户也没关系,有 eval 集就能自证。


七、你现在该怎么准备面试?(最短路径)

如果你时间有限,建议按这个顺序补:

  1. 先把“结构化输出 + schema 校验”练到顺手
  2. 再把“RAG + 引用证据 + 拒答”做成硬约束
  3. 最后把“工具调度的可靠性”补齐(timeout/retry/幂等/并发)

当你能把这三件事讲清楚,你就已经超过大量“只会搭积木”的候选人。

延伸阅读: