一、LLM 的边界:为什么它不能直接完成复杂任务?
一个场景
用户说:“帮我生成一份 Java 简历。”
如果直接丢给 LLM,它会生成一份“看起来不错”的简历——但很可能是凭空捏造的。LLM 没有用户的真实经历,不知道目标岗位的具体要求,也无法调用外部工具去搜索、匹配、排版。
真正需要的执行流程
用户输入 → 理解意图 → 收集 JD(需要用户上传或描述) → 解析 JD(提取技术要求) → 收集简历(需要用户提供) → 解析简历(提取技能、经历) → 匹配排序(精确对比 JD 和简历) → 重写优化 → 生成 PDFLLM 能完成其中的“理解意图”“解析 JD”“重写优化”——这些属于语言理解与生成。
但它做不了:
- 获取外部信息(搜索、读文件)
- 精确计算(打分排序)
- 执行动作(调用 API、生成 PDF)
- 多步循环(根据中间结果调整策略)
LLM vs Agent 本质区别
| LLM | Agent |
|---|---|
| 只能“回答” | 可以“行动” |
| 被动的,单次生成 | 主动的,循环执行 |
| 不知道的只能编 | 不知道的可以去查 |
| 输出文本 | 调用工具 + 输出文本 |
Agent = LLM + 规划能力 + 工具调用 + 记忆 + 反思
LLM 是一个“思考器官”,Agent 是一个“完整的执行者”。
二、 Tool(工具)?
Tool 的准确定义
Tool 是 LLM 不具备的能力的外部扩展。它让 Agent 能够“做事情”,而不只是“说事情”。
为什么 LLM 需要 Tool?
- 精确计算:LLM 擅长语言概率,不擅长数学规则
- 实时信息:LLM 的知识有截止日期
- 执行操作:LLM 不能调 API、读写文件、操作数据库
Tool ≠ 函数
“Tool 就是函数”——这个理解不完整。
判断标准:Tool 是一个可以被调用来完成具体任务的能力单元,有明确的输入输出。
三、Function Calling:为什么比 ReAct 稳定?
以前的做法(ReAct)
让 LLM 在输出文本中嵌入特殊标记:
Action: Calculator Action Input: 123 * 456然后用代码解析文本,提取出工具名和参数。
问题:
- 格式不稳定:多一个空格或换行就可能解析失败
- 依赖正则匹配,复杂场景容易出错
Function Calling 的做法
LLM直接输出结构化 JSON:
{"name":"calculator","parameters":{"expression":"123 * 456"}}为什么更稳定?
- 结构化输出:JSON 格式确定,不会因文本差异解析失败
- Schema 约束:可以预定义参数类型、必填项,LLM 被训练去遵守这些约束
- 训练有素:现代 LLM 在训练阶段已学过 Function Calling
四、Tool Calling 全过程——Agent 的核心循环
所有 Agent 的通用骨架
用户输入 → ① LLM 分析:需要调用什么工具? → ② 决定调用 Tool → ③ 执行 Tool → 得到 Observation(观察结果) → ④ Observation 返回给 LLM → ⑤ LLM 判断:任务完成了吗?还需要再调工具吗? → ⑥ 如需要,回到②;如完成,生成最终回答Observation 是什么?为什么一定要回传 LLM?
Observation = Tool 执行后返回的原始结果。比如:
- 计算器 Tool →
"56088" - 检索 Tool →
["简历段落1", "简历段落2"]
为什么不能直接返回给用户?
用户问“我的简历匹配吗?” → Agent 调检索 Tool 拿到一堆文本片段 →如果直接返回,用户看到的是碎片数据,不是答案。
正确做法:Observation 回传给 LLM → LLM 阅读并理解 → 生成自然语言回答:
“您的简历在 Java 方面匹配度很高,但在微服务经验上有所欠缺……”
Observation 必须回传 LLM 的三个原因:
- LLM 是唯一的“理解者”和“表达者”
- LLM 需要判断任务是否完成
- LLM 需要整合多步 Observation 生成完整答案