四点半·技术博客

Chatbot 和 AI Agent 的真正区别:控制流、上下文黑板和副作用

阿信 · 北京四点半软件工作室 #AI Agent #LLM #架构 #Chatbot

市面上关于这个话题的文章,九成会给你一张对比表:Chatbot 是问答,Agent 会用工具;Chatbot 无记忆,Agent 有规划。这种分类没错,但它停留在产品表象。真到你要做一个系统的时候,这些标签帮不上忙——你还是得回答一个更根本的问题:这玩意儿的控制流,到底跑在谁手里?

一个 while 循环的差别

先看 chatbot 的运行模型。从技术上看,它极其简单:

while True:
    user_input = read_user_message()
    response = llm.chat(history + user_input)
    write_back(response)

用户说一句,模型生成一句。整个循环的下一步由用户决定。模型是个无状态的函数,输入是对话历史,输出是下一段文本。它自己没有”打算”。

再看 agent 的最小骨架:

observation = initial_task
while not done:
    thought = llm.reason(observation, memory, tools)
    if thought.action == "final_answer":
        break
    result = execute(thought.action, thought.args)
    observation = result
    memory.append(thought, result)

差别在哪?循环的退出条件和下一步动作,是模型自己决定的。用户只给了初始任务,之后模型自己观察、自己决定做什么、自己判断什么时候算完。用户从”每一步都在发指令”变成了”开头发一个任务、结尾拿结果”。

这不是”chatbot 加了几个工具”。这是把程序的控制流从代码手里交出去,交给了一个概率模型。后面所有差异——记忆、规划、错误处理——都是从这一个架构选择里长出来的。

上下文窗口不是记忆,是黑板

很多人把上下文窗口理解成 chatbot 的”短期记忆”。这个比喻对 chatbot 勉强成立,但对 agent 是错的。

在 chatbot 里,上下文就是历史消息列表,你来我往,干净。在 agent 里,上下文是一块工作黑板,上面同时堆着:

  • 用户最初的任务描述
  • 模型自己写的中间思考(scratchpad)
  • 工具返回的原始结果,可能是几千行 JSON
  • 之前失败的尝试和错误堆栈
  • 子任务的拆分清单
  • 它刚决定要调用的下一个工具参数

关键在于:这些东西不是为了”回忆”,而是为了让模型下一步决策时有足够的现场。模型每做一个决定,都是在通读这块黑板之后做的。黑板满了,决策质量就下降——这就是为什么长任务 agent 会”迷路”。不是模型变笨了,是它在几千行无关内容里找不到重点了。

这直接派生出一堆 chatbot 里根本不存在的工程动作:压缩旧观察、按相关性检索历史、把大工具结果摘要后再塞回去、把结构化中间状态外化到外部文件。做 chatbot 你不会干这些事,因为没人会在对话历史里塞 5000 行 API 返回。

工具调用:从”说出来”到”结构化动作”

Chatbot 其实也能”用工具”。你问它北京天气,它可能会说”我需要查一下天气 API”,然后编一个结果。这个”说”是文本层面的,模型并不真的执行。

Agent 的工具调用是另一回事。以 OpenAI function calling 为起点,现在的标准做法是:

  1. 系统提示里声明可用工具的 JSON schema
  2. 模型在需要的时候,输出一个结构化对象,而不是自然语言:
    {"name": "get_weather", "arguments": {"city": "Beijing"}}
  3. 运行时代码解析这个对象,真正去调 API
  4. 把 API 返回结果作为一条 tool 角色的消息塞回上下文
  5. 模型看到结果,继续下一步决策

这里的关键不是”模型会用工具”,而是模型的输出被拆成了两种:给人看的自然语言,和给机器执行的结构化动作。运行时必须能区分这两者,执行完动作再把结果喂回去。这个”结构化输出 → 执行 → 结果回灌”的协议,是 agent 能闭环的前提。

没有这个协议,你得到的是一个”嘴上说会调工具”的 chatbot。有了这个协议,你才得到一个真的能在环境里行动的系统。

规划:为什么需要把任务外化

你可能会问:既然模型能一步步推理,为什么还要专门的 planner?

因为 LLM 的注意力是平面的。给它一个 20 步的任务让它一口气规划完,它到第 8 步就开始忘第 2 步的约束。这不是能力问题,是 Transformer 注意力机制的特性——它对序列位置的”记忆”是 soft 的,没有一个真正的程序计数器。

规划模块的本质,是把任务状态外化到上下文里:

任务:订一张明天去上海的机票
计划:
  [x] 1. 查明天北京→上海航班        (已查到 3 个班次)
  [ ] 2. 选时间合适的班次            ← 当前
  [ ] 3. 查用户常旅信息
  [ ] 4. 下单支付
  [ ] 5. 发确认邮件

把这个清单放在上下文里,模型每走一步都能”看见”自己在哪、下一步该干嘛。这跟人写 TODO list 是一个道理——不是你记不住,是写下来比记在脑子里可靠。

到了长任务,这个外化会演化成 DAG(任务间有依赖)、层次化规划(大任务拆成子任务)、甚至反思模块(做完一步回头看有没有走错路,Reflexion 那一类)。所有这些机制都是在跟 Transformer 的注意力限制做对抗。

错误模型:最被低估的差异

Chatbot 答错了,代价是一段错误文本。用户读了发现不对,追问一句,成本几乎为零。

Agent 做错了,代价可能是:

  • 往线上数据库写了脏数据
  • 给客户发了错误邮件
  • 调了付费 API 烧了钱
  • 删了不该删的文件
  • 在 GitHub 上提了一个没人愿意 merge 的 PR

也就是说,agent 的错误是有副作用的、不可逆的、可能跨系统传播的。

这一个差异决定了 agent 工程里一大堆 chatbot 里根本不存在的东西:

  • Human-in-the-loop:写操作之前停下来等人确认,而不是一路跑到底
  • 幂等设计:工具调用要能安全重试,失败了不会重复扣费
  • 沙箱隔离:执行代码、跑 shell 这种动作必须隔离,不能直接碰生产环境
  • 审计日志:每一步 observation / thought / action 全留痕,出了事能回溯
  • 预算控制:设 token 上限、步数上限、花钱上限,防止模型在死循环里烧钱
  • 恢复策略:工具失败了是重试、换工具、还是直接报告用户?这个决策本身也要模型来做

做 chatbot 你几乎不用想这些。做 agent,这些是核心设计决策。

为什么 2023 年中才出现像样的 agent

Agent 这个概念不新,ReAct 论文 2022 年就有了。但真正能跑的产品是 2023 年下半年之后才出现的。几个关键拐点:

Function calling 标准化。 2023 年 6 月之前,让模型稳定输出结构化 JSON 是个工程噩梦——你得在 prompt 里磨、在解析层加 repair 重试、处理各种格式跑偏。OpenAI 把 tool calling 做成原生能力之后,模型输出的结构化对象基本可靠,“执行 → 回灌”的循环才稳定跑得起来。

指令遵循能力上了一个台阶。 早期模型你给工具描述加个坑它就钻进去,现在的模型能老实按 schema 填参数,不会自由发挥。

上下文窗口变长。 2023 年初还是 4k / 8k,现在主流是 128k 起步。黑板塞得下多步中间结果,长任务才不至于几步就爆上下文。

但瓶颈也很明显。长程任务的错误会累积——单步 95% 成功率,20 步下来就是 36%。上下文窗口哪怕 1M,有效信息密度也会随着无关内容增多而下降。这些是当前 agent 产品在真实生产环境里最头疼的问题,也是为什么大部分”agent”产品最后都退化成了 chatbot 加几个硬编码按钮。

一个不太好听的现实

如果你见过足够多号称是 agent 的产品,会发现:真正让它们能用的,往往不是 agent 循环,而是人类工程师偷偷写死的逻辑。

一个客服 agent,看起来能查订单、退款、发优惠券,实际上 90% 的流程是代码里写死的状态机,LLM 只负责把用户的自然语言翻译成结构化参数。剩下那 10% 真开放给模型自由发挥的,往往是事故高发区。

这不是骗你,这是工程现实。纯靠 LLM 自由规划的端到端 agent,今天还做不到可靠地跑生产任务。工业界的做法是把 agent 当一个兜底策略:硬编码流程覆盖 90% 的常见情况,剩下 10% 模型自由发挥,并且在高风险动作上卡 human-in-the-loop。

什么时候该用哪个

判断标准其实很朴素:

  • 任务是”给用户一段文字回答”,用 chatbot。不要为了显得先进硬上 agent。
  • 任务需要跨多个系统、多步执行、有副作用,才需要 agent 架构。
  • 任务是单步、低风险、参数明确(翻译、总结、改写),哪怕要调工具,也是 chatbot + 一个 function call 就够,不要套 while 循环。
  • 如果你发现自己在写 planner、reflection、memory 压缩,先问一句:这个任务真的需要模型自己规划吗?还是我可以用一个状态机把流程固定下来?后者通常更便宜、更可靠、更好调试。

一句话总结

Chatbot 是模型在回答人,agent 是模型在驱动一个环境里的循环。前者的产品形态是对话框,后者的产品形态更接近一个由 LLM 当策略函数的自动化程序——输入是任务,输出是一连串动作,错误模型是有副作用的,工程上要解决的是可靠性、成本和可控性,而不是对话质量。

理解了这一层,你看任何一个”agent”产品,都能快速判断它是真 agent 还是 chatbot 套了个壳。

评论区未配置。在 blog/.env 填入PUBLIC_GISCUS_* 后重新构建即可启用。