Skip to content

第5章 给Agent一个好脑子

第4章讲了Agent是什么——一个能感知、规划、行动、反思的聪明程序。那这个程序最核心的"聪明"从哪来?这一章,我们聊聊Agent的"大脑"——大语言模型(LLM)。不用担心数学公式,我们只讲"它怎么想事情"和"你怎么跟它说话让它变聪明"。


5.1 LLM是怎么"想事情"的?

5.1.1 别被"大语言模型"这名字吓到

很多人一听"大语言模型"就觉得:肯定要学数学、神经网络、梯度下降……

不需要。

你开车不需要懂发动机原理,你只需要知道踩油门会加速、打方向盘会转弯。用LLM也一样——你不用懂Transformer架构,你只需要知道:

LLM本质上就是一个"接话高手"。你给它一段文字,它预测下一个最可能出现的词,一个词一个词接下去,直到把整段话说完。

是不是比你想的简单?它就是一直问自己"根据前文,下一个词大概率是什么",然后挑最可能的那个写上去。

5.1.2 那它为什么能"变聪明"?

"接话"听起来很简单,但当LLM读过了几乎整个互联网的文字之后,它"接"出来的话就变得非常有道理了。

比如你输入"帮我写一个Python函数,输入一个列表,返回它的中位数",LLM能"接"出正确的代码——因为它在训练时见过太多类似的问答了。

关键是:LLM的"知识"截止到它的训练数据日期。它不知道今天的新闻,不知道你公司的内部文件。这就是为什么Agent需要RAG(第7章)和工具调用(第6章)——补上"实时信息"这块短板。


5.2 教LLM学会新任务

5.2.1 零样本:直接问,不给例题

最简单的用法——直接问。

你:"把下面这句话翻译成英文:今天天气真好。"
LLM:"The weather is really nice today."

你没有给它任何例子,它照样能做。这叫做零样本(Zero-shot)。LLM在训练时已经见过足够的翻译数据,所以不需要你教。

但零样本的极限也很明显——遇到复杂任务就懵了。

你:"分析这段代码的时间复杂度和空间复杂度,并给出优化建议。"
LLM:(给了一个看起来对但其实有漏洞的答案)

5.2.2 少样本:给几个例子,让它学着来

当你发现零样本搞不定的时候,给它看几个例子。这就是少样本(Few-shot)

零样本——直接问

你:把下面的用户反馈分类为"好评"、"中评"、"差评"。
反馈:"物流太慢了,东西还行吧。"
LLM:差评(猜错了——其实偏中评)

少样本——先给例子再看

你:请参考以下例子对反馈分类:
例子1:"太棒了,下次还来" → 好评
例子2:"一般般,没啥特别" → 中评
例子3:"质量太差,不会再买" → 差评

现在分类:"物流太慢了,东西还行吧。"
LLM:中评(对了!因为有例子参考)

5.2.3 少样本的实战技巧

技巧1:例子要典型。 如果你要让LLM识别"紧急程度",给的例子要覆盖"紧急"、"普通"、"不急"三种情况,别全给"紧急"的。

技巧2:例子要简短。 别把一个完整项目文档当例子——LLM的注意力会分散。3-5个简短例子效果最好。

技巧3:例子放最后。 把最重要的例子放在最后——LLM对离它最近的上下文最敏感。


5.3 思维链与推理增强

5.3.1 "一步一步想"的魔力

LLM最强的地方不是"给答案",而是"给推理过程"。当你要求它"一步一步想",它的准确率会大幅提升。

不加思维链——直接问

你:一个水果摊有苹果25个,卖出了8个,又进了15个,现在有多少个?
LLM:32个(错了!应该是32吗?25-8+15=32,这个其实是对的,但我们换个复杂的)

换个复杂点的:

你:小明有3个苹果,小红有2倍于小明的苹果,小刚有小红一半的苹果。三人一共有多少个?
LLM:10个(错了!应该是3+6+3=12)

加思维链——让它写过程

你:一步步算:小明有3个。小红是小明的2倍=6个。小刚是小红的一半=3个。总共=3+6+3=
LLM:12个(对了!因为它一步一步来,没有跳步)

这就是思维链(Chain of Thought,CoT)——你不需要教它新知识,只需要让它"别急着回答,先写草稿"。

5.3.2 怎么让LLM用思维链

最简单的办法:在Prompt里加一句。

请一步一步思考,先写出推理过程,再给出最终答案。

这句话适用于几乎所有的推理任务——数学、逻辑、编程调试、决策分析。

进阶用法:直接给它看一个有思维链的例子。

例子:
问:一个班有20个男生,女生是男生的1.5倍,全班多少人?
推理:女生=20×1.5=30人。全班=20+30=50人。
答案:50人。

现在回答:
问:一个公司有80个员工,40%是工程师,其中一半是后端,后端有多少人?

5.3.3 思维链的边界

思维链不是万能的。它能让LLM更准确地推理,但如果LLM本身的知识就是错的,思维链只会让它"更自信地犯错"。

所以当你用LLM做Agent的"大脑"时,不能全信它的推理——需要有工具验证(第6章)、需要给它查资料(第7章)。


5.4 好的Prompt是写出来的,不是想出来的

5.4.1 Prompt就是Agent的"任务说明书"

很多人把Prompt当成"随便写一句指令"。其实Prompt是你在跟一个超级聪明但完全没有常识的实习生沟通——你说的每一句话,都会影响它的输出质量。

差Prompt

你:帮我写个函数。(太模糊)
LLM:什么语言?什么功能?什么参数?

好Prompt

你:用Python写一个函数,输入一个整数列表,返回其中位数。
要求:不要用numpy。如果列表为空返回None。
LLM:(给出正确的代码)

5.4.2 Prompt设计的四条铁律

写 Prompt 就像给新员工下任务——说清楚比说得多重要。从"帮我看看代码"到"你是Python专家,请审查以下代码的安全问题",效果天差地别:

铁律一:角色要明确。 告诉LLM"你是谁"。

你是一个Python代码审查专家。请审查以下代码的问题。

vs

帮我看看这段代码有什么问题。

前者让LLM进入"专家模式",输出质量明显更高。

铁律二:任务要具体。 "帮我写"不如"帮我写一个函数,输入X返回Y"。

铁律三:格式要限定。 如果你需要JSON,直接说"请以JSON格式返回"。

铁律四:给反例。 告诉LLM"不要做什么"往往和"要做什么"一样重要。

请写一个友好的回复。不要道歉,不要问"我还能帮你什么",不要超过50个字。

5.4.3 Agent中的Prompt实战

在Agent系统中,Prompt通常会被写成一个模板。因为同样的任务逻辑,用户每次输入的内容不同。

python
# Agent的意图识别Prompt模板
intent_prompt = """
你是一个客服意图识别助手。
根据用户输入,判断意图是以下哪种:查询订单、申请退款、投诉、其他。

用户输入:{user_message}

请只返回意图名称,不要加任何解释。
"""

这个模板在不同用户输入下反复使用——{user_message} 每次被替换成用户实际说的话。


5.5 用 LangChain 管理 Prompt 与 Chain

前面我们手写了 Prompt 模板,但真正开发 Agent 时,Prompt 不是一次性用完就扔的——你需要动态替换变量组合多个 Prompt把 LLM 的输出解析成结构化数据。这时候,代码框架就该登场了。

LangChain 为 Prompt 和 Chain 的管理提供了一套完整的工具链。我们不重复讲概念,直接看 LangChain 特有的能力。

5.5.1 Few-Shot 也能写成代码

5.2 节讲了 Few-shot 的概念——给 LLM 几个例子让它照着学。但在代码里,你不可能每次都手动拼一个带例子的 Prompt 字符串。LangChain 提供了 FewShotPromptTemplate,把"例子管理"和"模板拼接"解耦开:

python
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

# 管理你的例子库(可以随时增删,不用改主模板)
examples = [
    {"word": "开心", "antonym": "伤心"},
    {"word": "高大", "antonym": "矮小"},
]

# 定义每个例子怎么展示
example_prompt = PromptTemplate.from_template("词语: {word}\n反义词: {antonym}")

# 主模板:prefix 放前面,examples 自动插入,suffix 放后面
prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix="请给出下列词语的反义词:",
    suffix="词语: {input}\n反义词:",
    input_variables=["input"]
)

# 实际使用时,只需传用户输入
result = prompt.invoke({"input": "快速"})
print(result.to_string())
# 请给出下列词语的反义词:
# 词语: 开心
# 反义词: 伤心
# 词语: 高大
# 反义词: 矮小
# 
# 词语: 快速
# 反义词:

这比你每次手动拼字符串优雅得多。例子库可以存在数据库里、可以从配置文件加载——模板本身保持不变。

5.5.2 Chat Prompt:区分"谁在说话"

前面我们用简单的字符串模板就够了,但在多轮对话和 Agent 场景中,你需要区分系统指令用户输入AI 回复。LangChain 的 ChatPromptTemplate 用角色标签来组织这些消息:

python
from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一位专业的{subject}老师。"),  # 系统指令
    ("human", "请解释:{concept}"),                # 用户输入
])

messages = prompt.invoke({
    "subject": "物理学",
    "concept": "量子纠缠"
})

LLM 拿到的不再是一串文字,而是有结构的信息——它知道第一条是"你的角色设定",第二条才是"用户要你回答的问题"。这就是 SystemMessage / HumanMessage 的分工。

5.5.3 输出解析器:别让 LLM 说废话

Prompt 设计中最头疼的问题之一:LLM 的输出格式不可控

你让它"以 JSON 返回",它可能会在前面加一句"好的,以下是 JSON 格式的结果:",然后才是 JSON。你的解析器就崩了。

Output Parser 就是来解决这个问题的——它让 LLM 直接吐出结构化数据。

StrOutputParser:最简单的,把输出当纯文本,自动去掉多余空白和标记。

JsonOutputParser:配合 Pydantic 模型,让 LLM 输出合法的 JSON(dict):

python
from langchain_core.output_parsers import JsonOutputParser
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field

# 用 Pydantic 定义你期望的输出结构
class Joke(BaseModel):
    setup: str = Field(description="笑话的铺垫")
    punchline: str = Field(description="笑话的笑点")

# 创建解析器,告诉它"我要这种结构"
parser = JsonOutputParser(pydantic_object=Joke)

# 连起来:prompt → LLM → parser
chain = prompt | llm | parser
result = chain.invoke({"topic": "程序员"})
print(result)  # {"setup": "为什么程序员...", "punchline": "因为..."}
# result 已经是正经的 dict,可以直接 .get("punchline")

Parser 拿到的是 Python dict,不再是"像 JSON 的字符串"。你的代码再也不用写 json.loads() + try/except 了。

注意:对于复杂结构,Parser 依赖 LLM 正确遵循格式指令。关键业务场景建议加一层校验。

5.5.4 LCEL:一个竖线串起一切

前面你已经看到了 | 管道符。这是 LangChain 最核心的设计——LCEL(LangChain Expression Language)。

python
chain = prompt | llm | parser
result = chain.invoke({"topic": "程序员"})

这一行的含义是:把用户输入填进模板 → 发给 LLM → 把 LLM 的返回交给解析器。每个组件都可以单独替换、单独测试。

两个 Chain 还能串起来——上一个的输出成为下一个的输入:

python
# Chain1: 翻译成英文
translate_chain = (
    ChatPromptTemplate.from_template("翻译成英文:{text}")
    | llm | StrOutputParser()
)

# Chain2: 一句话总结
summarize_chain = (
    ChatPromptTemplate.from_template("一句话总结:{text}")
    | llm | StrOutputParser()
)

# 串联:先翻译,再总结
final_chain = {"translation": translate_chain} | summarize_chain
result = final_chain.invoke({"text": "今天天气真好"})

更灵活的是路由链——根据输入内容,动态选择走哪条处理路径:

python
# 定义两条分支
translate_chain = (ChatPromptTemplate.from_template("翻译:{text}") | llm | StrOutputParser())
qa_chain = (ChatPromptTemplate.from_template("问答:{text}") | llm | StrOutputParser())

# 路由器:让 LLM 判断该走哪条
router = ChatPromptTemplate.from_template(
    "判断以下输入是翻译请求还是问答请求,只回复 'translate' 或 'qa':\n输入:{text}"
) | llm | StrOutputParser()

def route(text):
    choice = router.invoke({"text": text}).strip().lower()
    if "translate" in choice:
        return translate_chain.invoke({"text": text})
    return qa_chain.invoke({"text": text})

这种组件化的设计思路贯穿 LangChain 的始终——每个零件都是可替换的,串联方式却始终一样。就像乐高,积木不同,但连接方式是统一的。

一句话总结这一节:LangChain 帮你把 Prompt 从"一次性拼字符串"升级到"可复用、可组合、可解析的组件系统"。你不再跟字符串搏斗,而是在拼积木。


5.6 本章小结

这一章我们搞清楚了Agent"大脑"的运作方式:

  1. LLM就是接话高手:一个词一个词地预测下一个词。但因为见得多,"接"出来的话显得很聪明。
  2. 少样本学习:给LLM几个例子,它就能做复杂任务。例子要典型、简短、重要放最后。
  3. 思维链:让LLM"一步一步想",准确率暴涨。原理很简单——别让它跳步。
  4. Prompt设计:角色明确、任务具体、格式限定、给反例。Agent的Prompt就是它的"任务说明书"。
  5. LangChain Prompt 管理:FewShotPromptTemplate 管理示例库、ChatPromptTemplate 组合消息模板、OutputParser 解析结构化输出、LCEL 管道串联——代码框架让 Prompt 工程从"手写字符串"升级为"工程化管理"。

记住一句话:LLM是你见过最聪明也最没常识的实习生——你交代得越清楚,它做得越好。


✅ 知识点检查

学完这一章,试试回答这几个问题:

  • [ ] LLM"接话"的过程是怎样的?为什么它看起来像在"思考"?
  • [ ] 零样本和少样本的区别是什么?什么情况下用少样本?
  • [ ] 思维链为什么能提高LLM的推理准确率?怎么在Prompt里触发它?
  • [ ] Prompt设计的四条铁律分别是什么?
  • [ ] LangChain 的 FewShotPromptTemplate 和 LCEL 管道分别解决什么问题?

📚 延伸阅读


🎯 下一章预告

第6章,Agent光有脑子还不够——

"光说不练假把式。让Agent学会调用工具,它才能真正帮你干活。"