LLM成长笔记(八):Agent 智能体开发

Agent 智能体开发学习博客(通俗原理 + 详细注释 · AI应用强化版)

Agent 是让大模型从"只会聊天"进化为"能自主执行任务"的关键技术。这篇博客从实际问题出发 ,用生活化类比 建立直觉,通过术语详解 深入概念本质,再用原理剖析图解演示可运行代码带你一步步理解。重点覆盖 Agent 面试中的高频考点。


一、初级篇:让模型"动起来"

1. 🔥 ReAct 模式:思考-行动-观察循环

问题

传统的 LLM 只能回答静态问题,如何让它能够自主地使用工具、分步完成任务,比如"帮我查一下北京天气,然后写一段周末出行建议"?

生活化类比
ReAct 就像你教一个实习生处理复杂任务:你告诉他"先思考需要什么信息,然后去执行(查资料、打电话),观察结果,再根据结果思考下一步,直到完成"。实习生不断循环这三步,最终交付成果。

术语详解

  • ReAct = Reasoning(推理)+ Acting(行动)。它将大模型的思考工具调用 交织在一起,形成一个循环:
    1. 思考(Thought):分析当前状态,决定下一步做什么。
    2. 行动(Action):执行一个工具调用(如搜索、计算、API 调用)。
    3. 观察(Observation) :接收工具返回的结果,作为下一次思考的输入。
      这个循环一直重复,直到模型决定"任务完成"并输出最终答案。

原理

ReAct 模式的核心是把工具调用和推理过程统一成一个文本序列。每次循环,模型都会在上下文中追加一段"思考:...行动:...观察:...",然后继续生成。这样,模型能利用之前的观察结果,动态调整后续行为,而不是一次性地预测所有步骤。相比固定流程的 Chain,ReAct 更灵活、更抗干扰。

图解演示:ReAct 循环流程

复制代码
用户问题
   │
   ▼
┌─────────────┐
│ 思考 (Thought) │ ← 分析现状,制定计划
└──────┬──────┘
       │ 决定调用工具
       ▼
┌─────────────┐
│ 行动 (Action) │ ← 执行具体工具(搜索、计算...)
└──────┬──────┘
       │ 工具返回结果
       ▼
┌─────────────┐
│ 观察 (Observation)│ ← 接收结果,更新上下文
└──────┬──────┘
       │ 任务未完成,回到思考
       ▼
     (重复)
       │ 任务完成
       ▼
   输出最终答案

演示用例:手写一个极简 ReAct Agent(文本解析版)

为了直观理解,我们用一个手动循环来模拟 ReAct,不使用任何 Agent 框架。模型通过解析输出中的特殊标记来决定是否调用工具。

python 复制代码
# 模拟 ReAct 循环(演示核心思想)
from openai import OpenAI
import json

client = OpenAI()

# 模拟外部工具:一个简单的词典查询
def lookup_word(word: str) -> str:
    """模拟查询词典,返回词义。参数 word: 要查询的词语。"""
    dictionary = {
        "苹果": "一种水果,通常是红色或绿色。",
        "香蕉": "一种长条形黄色水果。",
        "天气": "大气状况,如晴、雨、雪等。"
    }
    return dictionary.get(word, "未找到该词")  # 找不到时返回提示

# 工具列表(用文本形式告诉模型可调用的工具)
tools_desc = """
你可以调用以下工具:
- lookup_word(word):查询一个词的含义,返回字符串。
当你需要查词时,输出格式为:
Action: lookup_word
Action Input: 要查的词
当你能直接回答时,输出格式为:
Action: Finish
Final Answer: 最终答案
"""

# ReAct 循环
def react_agent(user_query: str, max_steps: int = 5):
    """
    纯文本解析版 ReAct Agent。
    参数 user_query: 用户的原始问题。
    参数 max_steps: 最大循环步数,防止无限循环(默认5步)。
    """
    # 初始消息:系统指令 + 工具描述 + 用户问题
    messages = [
        {"role": "system", "content": f"你是一个智能助理。{tools_desc}\n请严格遵循格式输出。"},
        {"role": "user", "content": user_query}
    ]
    
    for step in range(max_steps):
        # 调用 LLM 生成思考+行动
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",        # 指定使用的对话模型
            messages=messages,            # 传入当前完整对话历史
            temperature=0.0               # 温度为0,确保输出稳定可解析
        )
        reply = response.choices[0].message.content  # 提取模型回复的文本
        print(f"--- Step {step+1} ---")
        print(f"模型输出:\n{reply}")
        
        # 将模型的输出作为新消息加入对话(模拟上下文扩展)
        messages.append({"role": "assistant", "content": reply})
        
        # 简单解析:查找 Action 和 Action Input
        if "Action: Finish" in reply:
            # 提取最终答案
            final_answer = reply.split("Final Answer:")[-1].strip()  # 按标记分割并去空格
            return final_answer
        elif "Action: lookup_word" in reply:
            # 提取要查的词
            word = reply.split("Action Input:")[-1].strip()  # 获取标记后的内容
            # 执行工具------这是"观察"步骤
            observation = lookup_word(word)
            print(f"工具返回: {observation}")
            # 将观察结果作为新消息(模拟工具返回)
            messages.append({"role": "user", "content": f"Observation: {observation}"})
        else:
            # 格式不符合预期,强行结束并返回错误提示
            return "Agent 输出格式错误,无法继续。"
    
    return "达到最大步数,任务未完成。"  # 循环耗尽仍无结果

# 测试
result = react_agent("苹果是什么?")
print("\n最终答案:", result)

输出结果(示例,实际可能略有差异)

复制代码
--- Step 1 ---
模型输出:
Thought: 用户想知道"苹果"的含义,我需要查词典。
Action: lookup_word
Action Input: 苹果
工具返回: 一种水果,通常是红色或绿色。
--- Step 2 ---
模型输出:
Thought: 我已获得词义,可以回答用户。
Action: Finish
Final Answer: 苹果是一种水果,通常是红色或绿色。

最终答案: 苹果是一种水果,通常是红色或绿色。

⚠️ 生产环境建议 :上面用手写文本解析来演示原理,但实际项目中推荐使用 OpenAI 原生的 tool_calls 机制。模型会直接返回结构化的 tool_calls 数组(包含工具名和 JSON 参数),避免文本解析的不稳定性。在 LangChain 中,可以直接使用 create_openai_tools_agentAgentExecutor 封装好的 ReAct Agent。
面试要点 :ReAct 循环的核心是思考-行动-观察的交互模式,面试官常问"ReAct 和普通的 Chain 有什么区别?"------Chain 是固定流程,ReAct 是动态决策。
AI 应用场景:ReAct 是构建能自主使用工具、搜索、计算、操作数据库的 Agent 的基础模式,也是 OpenAI Assistant API、LangChain Agent 等框架的底层逻辑。


2. ⭐ 记忆管理:缓冲记忆、摘要记忆、向量长期记忆

问题

对话越来越长,Agent 需要记住之前的交互内容。但直接把所有历史塞进上下文会导致 token 爆炸。如何高效管理记忆?

生活化类比

  • 缓冲记忆 就像你的草稿纸------只记最近几句话,用完就擦。
  • 摘要记忆 就像你的课堂笔记------每隔一段时间把前面的内容总结成要点。
  • 向量长期记忆 就像你的外部硬盘------把重要信息存到向量数据库,需要时再检索出来。

术语详解

  • 缓冲记忆(Buffer Memory):直接保存最近 K 轮对话原文。简单但占空间,适合短对话。
  • 摘要记忆(Summary Memory):用 LLM 将长对话逐步总结为一段摘要,只保留摘要。省 token 但可能丢失细节。
  • 摘要缓冲记忆(SummaryBufferMemory):结合缓冲和摘要------保留最近 K 轮原文,超过 K 轮的部分自动压缩为摘要。这是生产中最常用的折中方案,兼顾了细节保留和 token 控制。
  • 向量长期记忆(Vector Long-term Memory) :将对话片段转为向量存入数据库,需要时用语义检索。通常在每次交互后异步写入 (避免阻塞主流程),检索时将召回的历史片段拼接到上下文开头并标注来源,供 LLM 参考。

原理

记忆管理的本质是在上下文窗口有限的情况下,尽可能保留对后续推理最有用的信息。缓冲记忆利用"最近性"(最近的信息最重要);摘要记忆利用"概括性"(压缩信息);向量记忆利用"相关性"(只提取与当前问题相关的历史)。三种方式常组合使用。

演示用例:使用 LangChain 实现三种记忆

python 复制代码
# pip install langchain langchain-openai
from langchain.memory import (
    ConversationBufferMemory,
    ConversationSummaryMemory,
    ConversationSummaryBufferMemory  # 混合方案:缓冲 + 摘要
)
from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain

# 创建 LLM 实例,temperature=0.3 让输出较稳定
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)

# ---- 1. 缓冲记忆:保留所有对话原文 ----
# return_messages=True 表示以消息对象列表形式存储,方便直接使用
buffer_memory = ConversationBufferMemory(return_messages=True)
chain_buffer = ConversationChain(llm=llm, memory=buffer_memory)
# predict 方法接收用户输入字符串,返回 AI 回复
chain_buffer.predict(input="你好,我是小明。")
chain_buffer.predict(input="我喜欢吃苹果。")
print("缓冲记忆中的消息数量:", len(buffer_memory.chat_memory.messages))

# ---- 2. 摘要记忆:逐步压缩历史为摘要 ----
# 同样 return_messages=True 保持接口一致
summary_memory = ConversationSummaryMemory(llm=llm, return_messages=True)
chain_summary = ConversationChain(llm=llm, memory=summary_memory)
chain_summary.predict(input="你好,我是小明。")
chain_summary.predict(input="我今天去了公园,看到了很多花。")
print("摘要记忆中的摘要内容:", summary_memory.buffer)  # buffer 存储摘要文本

# ---- 3. 摘要缓冲记忆(推荐生产使用) ----
# max_token_limit 控制总 token 上限,超过后旧消息被压缩为摘要
hybrid_memory = ConversationSummaryBufferMemory(
    llm=llm,                      # 用于生成摘要的 LLM
    max_token_limit=200,          # 当历史 token 超过此值时,将旧消息压缩
    return_messages=True          # 仍以消息形式返回,方便 Chain 使用
)
chain_hybrid = ConversationChain(llm=llm, memory=hybrid_memory)
chain_hybrid.predict(input="你好,我是小明。")
chain_hybrid.predict(input="我最近在学习 Python。")
print("混合记忆中的消息数量:", len(hybrid_memory.chat_memory.messages))

# ---- 4. 向量长期记忆(概念演示) ----
# 实际实现:将对话片段存入向量库(如 ChromaDB)
# 查询时用当前问题检索相关历史,拼接到上下文
# 写入:通常异步执行,不阻塞主流程
# 检索:召回的历史片段拼接到上下文开头,标注 [历史记忆] 来源

输出结果

复制代码
缓冲记忆中的消息数量: 4  (用户2条+AI2条)
摘要记忆中的摘要内容: The human introduces themselves as Xiao Ming...
混合记忆中的消息数量: 4  (在 token 限制内保留原文)

AI 应用场景:缓冲记忆适合短期对话;摘要记忆适合长对话但可丢失细节;向量长期记忆是构建能"记住用户偏好"的 Agent 的关键技术;SummaryBufferMemory 是大多数生产环境的推荐选择。


二、中级篇:让 Agent 更聪明、更可靠

1. 🔥 多 Agent 协作:角色分工、状态传递

问题

复杂任务(如"调研市场并生成报告")需要多个专业角色协作:一个搜索员、一个分析师、一个作家。如何让多个 Agent 分工合作?

生活化类比
多 Agent 协作就像公司部门会议:每个 Agent 是一个专家(市场部、技术部、财务部),他们各自完成分内工作,然后通过"会议纪要"(状态传递)将结果汇总给下一个部门。

术语详解

  • 多 Agent 系统:由多个具有不同角色和目标的 Agent 组成,通过通信协作完成任务。
  • AutoGen (微软)和 CrewAI 是两个主流多 Agent 框架。CrewAI 的核心概念是 Agent(角色)+ Task(任务)+ Crew(协作组)。
  • 状态传递 :上一个 Agent 的产出作为下一个 Agent 的输入,通过 context 参数实现。
  • 委派机制(allow_delegation) :开启后,Agent 可以将自己无法完成的任务委派给协作组中的其他 Agent(如研究员把计算任务委派给计算器 Agent)。顺序执行模式 中每个 Agent 依次执行各自任务;分层模式中有一个管理者 Agent 负责分配和协调。
  • 意见冲突处理:如果多个 Agent 给出矛盾的结果,通常由最后一个汇总 Agent 做判断,或引入投票机制。

演示用例:用 CrewAI 构建一个调研+报告的多 Agent 系统

python 复制代码
# pip install crewai langchain-openai
from crewai import Agent, Task, Crew, Process

# 定义 Agent:研究员
researcher = Agent(
    role="研究员",                              # Agent 的角色名称,用于显示和日志
    goal="找到关于{topic}的最新信息",            # 角色目标,决定行为方向
    backstory="你是一个经验丰富的研究员,擅长挖掘深度内容。",  # 角色背景故事,辅助 LLM 理解角色
    verbose=True,                              # 是否打印详细执行日志
    allow_delegation=False,                    # 是否允许将任务委派给其他 Agent(此处关闭)
    llm="gpt-3.5-turbo"                       # 指定该 Agent 使用的语言模型
)

# 定义 Agent:撰稿人
writer = Agent(
    role="撰稿人",
    goal="根据研究员的发现,撰写一份简洁的报告",
    backstory="你是一个专业撰稿人,擅长将复杂信息转化为流畅文章。",
    verbose=True,                              # 同样打印日志
    allow_delegation=False,                    # 不委派
    llm="gpt-3.5-turbo"
)

# 定义任务:研究
task_research = Task(
    description="搜索并总结关于{topic}的三个关键点。",         # 任务描述,告诉 Agent 要做什么
    expected_output="一个包含三个要点的列表,每个要点一段说明。",  # 期望输出格式,用于指导 Agent
    agent=researcher                                          # 指定由哪个 Agent 执行此任务
)

# 定义任务:撰写报告,依赖研究任务的输出
task_write = Task(
    description="根据研究员提供的要点,撰写一份简短报告(不超过200字)。",
    expected_output="一份结构清晰的报告。",
    agent=writer,                              # 由撰稿人执行
    context=[task_research]                    # 将研究任务的输出作为本任务的上下文(状态传递)
)

# 组建 Crew(协作组)
crew = Crew(
    agents=[researcher, writer],               # 参与协作的 Agent 列表
    tasks=[task_research, task_write],         # 需要执行的任务列表
    process=Process.sequential,                # 执行模式:sequential 顺序执行,先完成第一个再第二个
    verbose=True                               # 打印详细的执行过程
)

# 启动协作,传入输入变量(会替换 {topic})
result = crew.kickoff(inputs={"topic": "2024年人工智能发展趋势"})
print("最终报告:\n", result)

输出结果

复制代码
[研究员] 搜索相关信息...
[研究员] 找到三个关键点:
1. 多模态模型成为主流...
2. 开源模型性能逼近闭源...
3. AI 代理(Agent)开始商业化应用...
[撰稿人] 根据要点撰写报告...
最终报告: 2024年人工智能发展呈现三大趋势...

AI 应用场景:多 Agent 适合需要多角色协作的复杂流程,如自动生成市场研究报告、代码审查 + 修复、多轮辩论等。顺序模式适合流水线任务,分层模式适合需要动态调度的复杂项目。


2. 🔥 Plan-and-Execute 与反思机制

问题

ReAct 模式中 Agent 一边想一边做,容易"走一步看一步"而忽略全局规划,导致任务完成率低。如何让 Agent 先制定计划再执行,并且能从失败中反思?

生活化类比

  • Plan-and-Execute 就像建筑蓝图:先画好整个房子的设计图(Plan),再按图施工(Execute),而不是砌一块砖想一块。
  • 反思机制 就像考试后复盘:做完题后检查一遍,发现错误就修正,总结教训下次不再犯。

术语详解

  • Plan-and-Execute:将 Agent 的决策分为两阶段------先由规划器(Planner)生成完整步骤列表,再由执行器(Executor)逐步完成。
  • ReWOO (Reason Without Observation):Plan-and-Execute 的变体。在规划阶段就将工具调用的占位符嵌入计划(如"第2步:将[搜索结果]整理为表格"),执行时按顺序填入结果,避免每步都等待 LLM 重新思考,大幅减少调用次数。
  • 反思(Reflection):Agent 完成一个步骤或整个任务后,自我评估结果的质量。触发条件包括:工具返回空结果、结果不符合预期格式(如要求 JSON 但返回纯文本)、LLM 自我评估分数低于预设阈值。

原理

ReAct 的问题在于决策碎片化:每执行一个工具就要重新调用 LLM 思考下一步,耗时且容易偏离目标。Plan-and-Execute 将"思考全局计划"和"执行具体步骤"解耦:Planner 一次性生成完整计划(如 1.搜索资料 2.分析数据 3.写报告),Executor 按计划顺序执行,中间不需要 LLM 再次规划。反思机制在执行后引入校验循环:如果某步结果不理想,Executor 可以请求 Planner 仅修改失败步骤而非全盘重来。

演示用例:模拟 Plan-and-Execute + 反思的完整流程

python 复制代码
# 模拟 Plan-and-Execute + 反思机制的完整流程
import json

def plan_and_execute_with_reflection(user_query: str, max_reflections: int = 2):
    """
    完整的 Plan-and-Execute + 反思 Agent。
    参数 user_query: 用户请求的原始字符串。
    参数 max_reflections: 每个步骤最多重试次数,防止无限重试。
    """
    # ---- 阶段1:规划 ----
    planner_prompt = f"""
    你是一个任务规划器。请将以下用户请求分解为步骤列表,以 JSON 数组返回。
    每个步骤包含 step(序号)、action(动作描述)、expected(预期结果)。

    用户请求:{user_query}

    返回格式示例:
    [
      {{"step": 1, "action": "搜索北京的天气", "expected": "获得温度和天气状况"}},
      {{"step": 2, "action": "根据天气写出行建议", "expected": "一段出行建议文本"}}
    ]
    """
    plan_response = client.chat.completions.create(
        model="gpt-3.5-turbo",            # 使用规划模型
        messages=[{"role": "user", "content": planner_prompt}],
        temperature=0.0                   # 温度为0,保证规划稳定
    )
    plan_text = plan_response.choices[0].message.content  # 获取规划文本
    plan_steps = json.loads(plan_text)    # 解析 JSON 为步骤列表
    print("生成的计划:")
    for s in plan_steps:
        print(f"  步骤{s['step']}: {s['action']} → 预期: {s['expected']}")
    
    # ---- 阶段2:执行 + 反思 ----
    results = []                          # 存储每一步的结果
    for i, step in enumerate(plan_steps):
        print(f"\n执行步骤 {step['step']}: {step['action']}")
        
        # 模拟执行工具(实际会调用搜索、计算等工具)
        # 这里用 LLM 模拟工具返回的结果
        exec_prompt = f"模拟执行以下动作并返回结果:{step['action']}"
        exec_response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": exec_prompt}],
            temperature=0.0
        )
        step_result = exec_response.choices[0].message.content
        print(f"  执行结果: {step_result[:100]}...")  # 仅显示前100字符
        
        # ---- 反思:检查结果是否符合预期 ----
        reflection_prompt = f"""
        预期结果: {step['expected']}
        实际结果: {step_result}
        
        请判断实际结果是否满足预期。如果满足,回复 "OK"。
        如果不满足,回复 "RETRY: <原因>" 并给出修正建议。
        """
        reflection_response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[{"role": "user", "content": reflection_prompt}],
            temperature=0.0
        )
        reflection = reflection_response.choices[0].message.content
        print(f"  反思结果: {reflection}")
        
        # 如果反思建议重试且还有重试次数
        retry_count = 0
        while "RETRY" in reflection and retry_count < max_reflections:
            print(f"  → 重试步骤 {step['step']}...")
            # 用反思的建议重新执行
            retry_response = client.chat.completions.create(
                model="gpt-3.5-turbo",
                messages=[{"role": "user", "content": f"重新执行: {step['action']}。注意: {reflection}"}],
                temperature=0.0
            )
            step_result = retry_response.choices[0].message.content
            retry_count += 1
            # 再次反思(简化:重试后默认通过,实际可再次评估)
            reflection = "OK"
        
        results.append({"step": step['step'], "result": step_result})
    
    # ---- 阶段3:汇总所有步骤结果生成最终答案 ----
    summary_prompt = f"根据以下步骤结果,回答原始问题:{user_query}\n\n" + \
                     "\n".join([f"步骤{r['step']}: {r['result']}" for r in results])
    final_response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": summary_prompt}],
        temperature=0.0
    )
    return final_response.choices[0].message.content

# 测试
result = plan_and_execute_with_reflection("北京今天天气怎么样?适合户外运动吗?")
print("\n最终答案:", result)

提升任务完成率的关键

  • 规划时要明确每个步骤的输入和输出,用 JSON 格式结构化。
  • 执行中出错时,不要全盘重来,只修正失败步骤。
  • 反思触发条件:空结果、格式不匹配、LLM 自我评估低于阈值。
  • ReWOO 适用于步骤间依赖明确的任务,通过在计划中预埋占位符避免频繁等待。

面试考点:面试官常问"ReAct 有什么缺点?如何改进?"------答:ReAct 缺少全局规划,Plan-and-Execute 或 ReWOO 可以弥补;反思机制能显著提高成功率。


3. 🔥 MCP 协议:概念、资源/工具/提示的标准化暴露

问题

不同的 AI 应用各自定义工具接口,开发者需要为每个模型或框架适配一遍。有没有一种标准协议,让所有模型都能以统一方式调用工具和资源?

生活化类比
MCP 就像 USB 接口:以前每种设备有自己独特的接口(打印机用并口,鼠标用 PS/2),现在统一成 USB,任何设备都能即插即用。MCP 就是 AI 应用和工具之间的"USB 标准"。

术语详解

  • MCP(Model Context Protocol) :由 Anthropic 提出,定义了 AI 模型与外部工具、资源交互的标准协议。它规定了三类暴露端点:
    • 工具(Tools):可执行的函数,如搜索、计算。
    • 资源(Resources):可读取的数据源,如文件、数据库记录。
    • 提示(Prompts):预设的提示模板。
  • MCP Server:实现了 MCP 协议的服务端,负责暴露工具和资源。
  • MCP Client:AI 应用(如 Claude Desktop)通过 MCP 客户端连接到 Server,自动发现并调用这些工具。
  • MCP 与 Function Calling 的关系 :MCP 是工具标准化的协议 (定义工具如何被发现和调用),Function Calling 是模型端的能力(模型如何"表示"调用意图)。两者配合:MCP Server 提供标准化的工具接口,模型通过 Function Calling 决定何时调用哪个 MCP 工具。

原理

MCP 使用 JSON-RPC 2.0 作为传输协议。Server 启动后注册自己支持的工具。Client 连接后通过 tools/list 获取所有可用工具,通过 tools/call 调用具体工具。传输层支持三种方式:

传输方式 适用场景 特点
stdio 本地进程通信 最简单,Server 作为子进程启动
SSE Web 服务远程调用 支持 HTTP,单向推送
Streamable HTTP 生产环境远程调用 双向通信,适合大规模部署

JSON-RPC 报文示例

json 复制代码
// 请求:列出所有可用工具
{
  "jsonrpc": "2.0",          // JSON-RPC 版本
  "id": 1,                   // 请求 ID,用于关联响应
  "method": "tools/list",    // 调用的方法名
  "params": {}               // 参数,列出工具不需要额外参数
}

// 响应:返回工具列表
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "add",                    // 工具名称
        "description": "返回两个整数的和", // 工具描述
        "inputSchema": {                  // 输入参数的 JSON Schema
          "type": "object",
          "properties": {
            "a": {"type": "integer"},
            "b": {"type": "integer"}
          },
          "required": ["a", "b"]
        }
      }
    ]
  }
}

// 请求:调用 add 工具
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "add",             // 要调用的工具名称
    "arguments": {"a": 3, "b": 5}  // 工具参数,符合 inputSchema
  }
}

// 响应:返回计算结果
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{"type": "text", "text": "8"}]  // 工具返回的内容,数组形式
  }
}

演示用例:写一个简单的 MCP Server 并连接客户端

服务端 (server.py):

python 复制代码
# pip install mcp
from mcp.server.fastmcp import FastMCP

# 初始化 MCP 服务器,命名为 "DemoServer"(用于标识)
mcp = FastMCP("DemoServer")

# 注册一个工具:加法计算器
@mcp.tool()  # 装饰器标记该方法为 MCP 工具
def add(a: int, b: int) -> int:
    """返回两个整数的和。参数 a 和 b 是加数。"""
    return a + b

# 注册一个资源:模拟读取配置文件
@mcp.resource("config://app")  # 资源标识符 URI,客户端通过此 URI 读取
def get_config() -> str:
    """返回应用的配置信息。"""
    return '{"theme": "dark", "language": "zh"}'

# 启动服务器,transport="stdio" 表示通过标准输入输出通信(适合本地子进程模式)
if __name__ == "__main__":
    mcp.run(transport="stdio")  # 可改为 "sse" 或 "streamable-http" 用于远程访问

客户端 (client.py):

python 复制代码
# 使用 mcp 库连接 Server 并调用工具
import asyncio
from mcp import Client
from mcp.client.stdio import stdio_client, StdioServerParameters

async def main():
    # 配置 Server 参数:通过 stdio 启动 server.py 作为子进程
    server_params = StdioServerParameters(
        command="python",           # 启动命令
        args=["server.py"]          # 命令参数,即要运行的脚本
    )
    
    # 建立 stdio 连接,返回读写流
    async with stdio_client(server_params) as (read, write):
        # 创建 MCP 客户端,传入读写流
        async with Client(read, write) as client:
            # 列出所有可用工具
            tools = await client.list_tools()
            print("可用工具:", [t.name for t in tools])  # 打印工具名列表
            
            # 调用 add 工具,传入参数字典
            result = await client.call_tool("add", {"a": 3, "b": 5})
            # result.content 是工具返回的内容列表,取第一个元素的 text
            print("3 + 5 =", result.content[0].text)

asyncio.run(main())

输出结果

复制代码
可用工具: ['add']
3 + 5 = 8

AI 应用场景:MCP 让 Agent 的工具生态从"封闭花园"走向"标准化市场",未来开发者可以像安装插件一样为 Agent 添加新能力。面试中常问"MCP 和 Function Calling 是什么关系"------前者是工具标准化协议,后者是模型调用工具的机制,两者互补。


4. ⭐ 会话持久化与检查点恢复

问题

Agent 执行一个长任务(如持续几分钟的数据分析)时,如果服务崩溃或用户断开连接,所有中间状态都丢失了,必须从头开始。如何让 Agent 能"断点续传"?

生活化类比
检查点就像游戏存档:玩到一半时保存进度,即使电脑关机,下次还能从存档处继续,不用重打已经通过的关卡。

术语详解

  • 会话持久化:将 Agent 运行中的状态(消息历史、中间结果、计划进度)保存到外部存储(如数据库、文件)。
  • 检查点(Checkpoint):在关键步骤后保存的状态快照。Agent 可以从最近的检查点恢复执行。
  • LangGraph 等框架内置了检查点机制,可以在每个节点(步骤)后自动保存状态。持久化粒度通常是每个节点执行后保存一次,过于频繁(如每个 token)会影响性能。

原理

持久化的核心是状态的序列化和反序列化 。Agent 的完整状态包括:对话历史、当前任务计划、已完成的步骤、工具调用结果等。将这些保存为 JSON 或数据库记录,恢复时重新加载并继续执行下一步。LangGraph 使用 checkpointer 参数,在编译图时传入,框架自动在每一步执行后调用检查点保存。恢复时用相同的 thread_id 即可从上次断点继续。

演示用例:LangGraph + SqliteSaver 检查点恢复

python 复制代码
# pip install langgraph langgraph-checkpoint-sqlite
from langgraph.checkpoint.sqlite import SqliteSaver  # SQLite 持久化检查点
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator

# 定义 Agent 的状态类型,继承 TypedDict 以获得类型提示
class AgentState(TypedDict):
    messages: Annotated[list, operator.add]  # 消息列表,operator.add 表示追加而非覆盖
    step_count: int                           # 当前步骤计数

# 构建节点函数,每个函数接收当前状态并返回更新后的状态(部分更新)
def step_one(state: AgentState) -> AgentState:
    """模拟第一步:执行某个工具"""
    return {
        "messages": ["步骤1:已搜索资料"],          # 追加消息(operator.add 自动合并)
        "step_count": state.get("step_count", 0) + 1 # 步骤计数 +1
    }

def step_two(state: AgentState) -> AgentState:
    """模拟第二步:分析结果"""
    return {
        "messages": ["步骤2:已分析数据"],
        "step_count": state["step_count"] + 1
    }

def step_three(state: AgentState) -> AgentState:
    """模拟第三步:生成报告"""
    return {
        "messages": ["步骤3:已生成报告"],
        "step_count": state["step_count"] + 1
    }

# 构建状态图
builder = StateGraph(AgentState)               # 传入状态类型
builder.add_node("step_one", step_one)        # 添加节点:名称 + 处理函数
builder.add_node("step_two", step_two)
builder.add_node("step_three", step_three)
builder.set_entry_point("step_one")            # 设置入口节点
builder.add_edge("step_one", "step_two")       # 添加边:步骤顺序
builder.add_edge("step_two", "step_three")
builder.add_edge("step_three", END)            # 最后指向结束

# 使用 SQLite 持久化检查点
with SqliteSaver.from_conn_string("checkpoints.db") as checkpointer:
    # 编译图,传入检查点器
    graph = builder.compile(checkpointer=checkpointer)
    
    # 配置会话 ID------相同的 thread_id 用于恢复
    config = {"configurable": {"thread_id": "user-session-456"}}
    
    # 第一次运行:从空状态开始,执行所有步骤
    print("===== 第一次运行 =====")
    result = graph.invoke(
        {"messages": [], "step_count": 0},  # 初始状态
        config                               # 传入配置(含 thread_id)
    )
    print(f"完成步骤数: {result['step_count']}")
    print(f"消息: {result['messages']}")
    
    # 模拟中断后恢复------用相同的 thread_id 调用 get_state 查看已保存状态
    print("\n===== 模拟中断后恢复 =====")
    state = graph.get_state(config)           # 从数据库加载状态
    print(f"当前步骤数: {state.values.get('step_count', 0)}")
    print(f"历史消息: {state.values.get('messages', [])}")
    # 如果任务未完成,可继续调用 graph.invoke(..., config) 从断点恢复

输出结果

复制代码
===== 第一次运行 =====
完成步骤数: 3
消息: ['步骤1:已搜索资料', '步骤2:已分析数据', '步骤3:已生成报告']

===== 模拟中断后恢复 =====
当前步骤数: 3
历史消息: ['步骤1:已搜索资料', '步骤2:已分析数据', '步骤3:已生成报告']

AI 应用场景 :在需要长时间运行的 Agent 服务(如自动化数据分析、代码重构)中,检查点恢复是保证可靠性的关键。thread_id 是恢复的钥匙------相同 ID 即可从上次断点继续。


AI 应用场景速查表

知识点 核心用途 典型场景
ReAct 模式 让 Agent 动态使用工具 搜索问答、自动化操作
缓冲记忆 记住最近对话 短期上下文感知
摘要记忆 压缩长对话历史 客服长对话
摘要缓冲记忆 缓冲+摘要折中 大多数生产环境首选
向量长期记忆 持久化个性化记忆 用户偏好学习
多 Agent 协作 复杂任务分工 调研报告、代码审查
Plan-and-Execute 提升任务规划能力 多步骤数据分析
ReWOO 减少 LLM 调用次数 步骤间依赖明确的任务
反思机制 自我纠错 提高任务成功率
MCP 协议 工具标准化暴露 跨模型工具复用
会话持久化 断点续传 长任务可靠性

面试模拟题

1. 原理型:什么是 ReAct 模式?它和普通的 Chain 有什么区别?

答案要点:ReAct 将推理和行动交织在一起,通过思考-行动-观察循环动态决策。Chain 是固定的流程,不能根据中间结果调整后续步骤。ReAct 更灵活,但调用次数更多。生产环境推荐使用 Function Calling 实现,避免文本解析的不稳定性。


2. 场景型:你的 Agent 执行一个需要 10 步的任务,但经常在第 6 步出错后全部重来。如何改进?

答案要点:引入检查点机制(如 LangGraph + SqliteSaver),在每步后保存状态,出错后从最近的检查点恢复而非从头开始。同时加入反思机制,让 Agent 分析失败原因并仅修正失败步骤,而非全盘重来。


3. 原理型:Plan-and-Execute 相比 ReAct 有什么优势?ReWOO 又是什么?

答案要点:Plan-and-Execute 先生成全局计划再执行,减少了 LLM 调用次数,任务完成率更高。ReWOO 是其变体,在规划阶段预埋工具结果占位符,避免每步等待 LLM,进一步减少调用。两者都适合步骤清晰、依赖明确的任务。


4. 对比型:缓冲记忆、摘要记忆、摘要缓冲记忆和向量长期记忆各有什么优缺点?如何组合使用?

答案要点:缓冲记忆简单但占空间,适合短期;摘要记忆省 token 但丢失细节;摘要缓冲记忆结合两者,保留最近原文+压缩旧历史,是生产首选;向量记忆保细节且可扩展,但实现复杂。组合方式:短期用缓冲,长期用向量,折中用摘要缓冲。


5. 热点型:什么是 MCP 协议?它和 OpenAI 的 Function Calling 是什么关系?

答案要点:MCP 是模型上下文协议,标准化了 AI 模型与外部工具的交互方式(JSON-RPC),解决了工具接口碎片化问题。Function Calling 是模型端能力,让模型"表示"调用意图;MCP 是工具端标准,让工具能被任何模型发现和调用。两者互补:MCP 提供标准化工具接口,模型通过 Function Calling 决定调用哪个工具。


总结

从 ReAct 的思考-行动循环,到记忆管理、多 Agent 协作,再到 Plan-and-Execute 的全局规划和 MCP 的标准化工具暴露,你已掌握 Agent 开发的核心技术栈。面试中的高频考点------ReAct 的原理与改进、Plan-and-Execute 的规划-执行闭环、MCP 的协议细节------都已覆盖。现在你可以开始构建自己的智能体,并让它可靠地完成复杂任务了。

相关推荐
心中有国也有家7 小时前
ascend-boost-comm:一次写完,到处复用——算子公共平台的 M×N 哲学
人工智能·经验分享·笔记·分布式·算法
東隅已逝,桑榆非晚7 小时前
深度解析数据内存存储与排布规则
c语言·笔记
晓梦林7 小时前
kakeru靶场学习笔记
笔记·学习
aloha_7897 小时前
信息系统项目管理师选择题考前真题错题笔记汇总
java·笔记·学习·tomcat
aloha_7897 小时前
信息系统项目管理师真题做题笔记
java·笔记·学习·软件工程·学习方法
小+不通文墨8 小时前
利用树莓派部署的emqx向mqttx发送信息(python)
经验分享·笔记·学习·树莓派·emqx
Hua-Jay8 小时前
OpenCV联合C++/Qt 学习笔记(二十五)----加载深度神经网络模型及深度神经网络模型的使用
c++·笔记·qt·opencv·学习·计算机视觉·dnn
xuhaoyu_cpp_java8 小时前
Git学习(三)
经验分享·笔记·git·学习