学习笔记—AI领域的应用和相关概念

简介

这篇文章主要是我在学习 AI 应用开发过程中的一些整理与笔记,内容以入门理解为主,不追求面面俱到,重点是把几个常见概念串起来:LLM、Token、Context、Prompt、ReAct、Agent、MCP 以及工具调用流程。
如果你也刚开始接触这些内容,希望这篇笔记能提供一点帮助。

AI大模型中的一些常见术语

LLM(Large Language Model) 大语言模型

LLM是大语言模型的英文名的简称,目前市面上的大多数文本类模型均采用Transformer架构进行训练。
大语言模型的生成过程,本质上是基于已有上下文不断预测下一个 token,并将这些 token 依次拼接成完整输出,直到生成结束标记或达到设定长度。

Token 词元

该名称一般形容大模型认为的词,是模型内部使用的文本切分单位。由于文本大模型的架构和设计语言是英文,因此Token对于英文的理解和划分会更好,但这不意味着它就完全符合我们所认为的英语单词。

例如:

  • 枫叶天凝,在GPT中所展示的Token分别是,枫、叶、天、凝加上结束符=5Token

  • careful是一个完整的英语单词,但是在大模型眼中它是care+ful+结束符=3Token;

  • 而 “doing” 在某些模型中又可能作为一个完整 token 出现。
    因此,token 更像是模型内部使用的“文本切分单位”,而不是严格意义上的单词数。
    如果想直观看到切分结果,可以使用公开工具如 gpt-tokenizer 查看

  • 值得注意的是Tokenizer的划分并不统一,不同的模型有不同的Tokenizer

Context 上下文

大模型能够利用当前输入中的上下文信息来理解问题并生成回答,但它并不会像人一样天然地“长期记住”之前所有对话。

在聊天场景中,我们之所以感觉模型“记得前面说过什么”,通常是因为应用程序把之前的对话记录一并发送给了模型。

这部分被一起输入给模型的信息,就可以称为上下文(Context)。

Prompt 提示词 Prompt Engineering 提示词工程

Prompt:大模型接受的具体问题,或者指令。

Prompt Engineering提示词工程:如何更清楚、更有效地向模型表达需求,从而让模型更稳定地输出我们想要的结果。随着模型能力增强,提示词工程的重点也逐渐从“教模型理解问题”,扩展到“如何更稳定地约束模型行为、组织任务流程,以及提升输出质量

因此根据这个思路,将AI接收到的指令分成多部分。
一般分为两种System Prompt和User Prompt当然,在实际应用中,开发者也可以根据任务需要设计更多消息角色或控制层一起发给大模型

  • System Prompt(系统提示词):
    一般由开发者预先设定,用于告诉模型它应该扮演什么角色、遵循什么规则、输出什么风格。这部分内容通常对用户不可见。比如说设置为:你是一个优秀的文案主编,请你以XXX格式将我需要的资料发给我。

  • User Prompt(用户提示词):
    这部分属于用户的具体的问题,例如:“请根据以下资料写一份新闻总结”

ReAct思考模式

ReAct思考模式,是一种理论知识,它的讨论为大模型可以根据ReAct思考模式能够自主完成用户要求的一些事情,而无需人为干预。
它是一种思路强调让模型在解决问题时按照“思考(Reason)→ 行动(Act)→ 观察(Observe)”的方式循环推进任务

  • 思考: 负责调度让大模型思考自身究竟需要做什么,或者说当前需要做什么。
  • 行动: 开始去按照自己的想法去做
  • 观察: 根据结果去确认,是否完成,是否要再次去做

这种模式常见于 Agent 系统中,用来让模型分步骤完成更复杂的任务。

Agent

如果做一个简单理解,可以把 Agent 看成:
大模型 + 工具调用能力 + 任务执行策略

它的目标不是只回答一句话,而是能够围绕用户任务持续执行多轮操作,直到问题被解决或达到停止条件。
在很多实现中,Agent 会结合 ReAct 思路,通过调用外部 Tool 来获取信息、执行命令或修改文件。
是否能够稳定规划步骤、正确选择工具、减少错误调用,往往也是衡量一个 Agent 是否好用的重要标准。

SSE(Server-Sent Events)服务器发送事件

在传统的 Web 请求中,通常是客户端发起一次请求,服务端返回一次结果,随后连接关闭,也就是一种“一来一回”的通信方式。
但在大模型对话场景中,模型的回答往往不是一次性生成完成的,而是随着推理过程逐步输出,因此更适合使用流式返回的方式。

于是加入了SSE机制,现在大多数模型的聊天界面均采用SSE模式,客户端向服务端发起一个 HTTP 请求后,服务端不会立即在返回结果后关闭连接,而是会保持这条连接处于打开状态。接下来,当模型不断生成新的内容时,服务端就可以通过这条连接持续将增量结果发送给客户端。这样前端就能一边接收、一边展示,于是用户看到的就是聊天界面里常见的“逐字输出”或“打字机效果”。

MCP(Model Context Protocol) 模型上下文协议

MCP 不仅是一个协议名称,也可以理解为一套围绕模型与外部工具交互的设计方式。

在实际使用中,人们谈到 MCP 时,既可能是在说协议本身,也可能是在说基于这套协议构建出的协作系统,它通过ReAct理念以及一些设计帮助模型完成一些模型本来不能做的事情,如调用外部工具,命令执行等。

它的意义在于:模型本身虽然擅长文本生成,但并不能直接访问文件、调用本地程序、查询实时数据;而通过 MCP,模型可以在外部系统的配合下获得这些能力。

MCP协议中的角色:

  • MCP Host:可以理解为模型与 MCP Server 之间的桥梁。它负责管理会话、把工具信息提供给模型、接收模型的调用请求,并把请求转发给对应的 MCP Server。

  • MCP Server:可以理解为是一个服务和进程,它主要负责实际执行 Tool 对应的逻辑,并将结果返回给 MCP Host。如获取某些信息并返回给MCPHost,又或者说是完成某些工作,将工作状态返回给MCPHost。

  • Tool:具体函数或者是包装后的接口,可以说MCPServer可以当作Python中的Class,那么Tool就是具体的函数,做了什么事,能做什么事。

一个简单的 Tool 示例

Tool的开发会基于MCP协议提供的一些功能,下面的例子是一个博主写的代码,他讲的课非常通俗易懂
这是它的链接:https://space.bilibili.com/1815948385/upload/video

代码示例

下面这段代码演示了一个基于 MCP 的天气查询服务。
它的主要作用不是“爬网页”,而是调用天气 API,并把结果整理成适合大模型理解和展示的文本格式

在这个示例中:

  • 使用 FastMCP 创建了一个 MCP Server;
  • 通过 @mcp.tool() 注册了两个 Tool;
  • Tool 的功能分别是:
    • 查询某个州的天气预警;
    • 根据经纬度查询天气预报
  • 代码内部使用了 async 异步方式处理网络请求,以避免阻塞,提高处理效率

需要注意的是:
MCP Host 在连接到这个 Server 后,不只是能看到 Tool 的名字,还通常能获得 Tool 的说明、参数定义等信息。大模型正是根据这些信息,判断“什么时候该调用哪个工具”

from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP

# 创建 MCP Server
mcp = FastMCP("weather", log_level="ERROR")

# 美国国家气象局 API
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"


async def fetch_json(url: str) -> dict[str, Any] | None:
    """请求指定 API,并返回 JSON 数据。"""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json",
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception:
            return None


def format_alert(feature: dict) -> str:
    """将天气预警信息格式化为易读文本。"""
    props = feature["properties"]

    return f"""
Event: {props.get("event", "Unknown")}
Area: {props.get("areaDesc", "Unknown")}
Severity: {props.get("severity", "Unknown")}
Description: {props.get("description", "No description")}
Instruction: {props.get("instruction", "No instruction")}
""".strip()


@mcp.tool()
async def get_alerts(state: str) -> str:
    """
    获取美国某个州的天气预警信息。

    参数:
        state: 两位州代码,例如 CA、NY、TX

    返回:
        格式化后的天气预警文本;如果没有数据,则返回提示信息
    """
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await fetch_json(url)

    if not data or "features" not in data:
        return "无法获取天气预警数据。"

    if not data["features"]:
        return f"{state} 州当前没有天气预警。"

    alerts = [format_alert(feature) for feature in data["features"]]
    return "\n\n---\n\n".join(alerts)


@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """
    根据经纬度获取天气预报。

    参数:
        latitude: 纬度
        longitude: 经度

    返回:
        格式化后的天气预报文本
    """
    # 第一步:根据经纬度获取预报接口地址
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await fetch_json(points_url)

    if not points_data:
        return "无法获取该位置的网格信息。"

    forecast_url = points_data.get("properties", {}).get("forecast")
    if not forecast_url:
        return "无法找到该位置对应的天气预报接口。"

    # 第二步:获取具体天气预报
    forecast_data = await fetch_json(forecast_url)
    if not forecast_data:
        return "无法获取天气预报数据。"

    periods = forecast_data.get("properties", {}).get("periods", [])
    if not periods:
        return "天气预报数据为空。"

    result = []
    for period in periods[:5]:
        result.append(
            f"{period.get('name', 'Unknown')}: "
            f"{period.get('temperature', 'Unknown')}°{period.get('temperatureUnit', '')}, "
            f"{period.get('detailedForecast', 'No forecast available')}"
        )

    return "\n".join(result)


if __name__ == "__main__":
    mcp.run(transport="stdio")

代码讲解

1.FastMCP的作用

mcp = FastMCP("weather", log_level="ERROR")
表示创建了一个名为 weather 的 MCP Server。
后面所有通过 @mcp.tool() 修饰的函数,都会被注册成这个 Server 可以提供的工具。

2.fetch_json()的作用

async def fetch_json(url: str) -> dict[str, Any] | None:
这个函数是一个通用的网络请求函数,负责:

  • 向指定 URL 发起请求;
  • 携带请求头;
  • 读取返回的 JSON 数据;
  • 在请求失败时返回 None。

3.@mcp.tool()的作用

@mcp.tool()
async def get_alerts(state: str) -> str:

这说明 get_alerts 被注册成了一个 MCP Tool。
当 MCP Host 获取 Server 的工具列表时,这个函数的名字、说明、参数信息都可能被暴露出去,供大模型决定是否调用。
可以把它理解成:

  • 函数本身:实际执行逻辑;
  • Tool 描述信息:提供给模型理解和选择使用的“说明书”。

4.get_alerts() 做了什么

这个 Tool 用于查询某个州的天气预警。
例如传入:
state="CA"
它会访问类似下面的接口:
https://api.weather.gov/alerts/active/area/CA
然后把返回的预警信息提取出来,整理成一段更易读的文本。

5.get_forecast() 做了什么

这个 Tool 稍微复杂一点,分两步:
第一步:先根据经纬度查预报地址
例如传入:

latitude=40.7128
longitude=-74.0060

先请求:
https://api.weather.gov/points/40.7128,-74.0060

这个接口不会直接返回天气,而是告诉你“这个位置对应的预报接口地址是什么”。

第二步:再去请求真正的天气预报
拿到 forecast 地址之后,再发第二次请求,获取具体天气内容。
最后代码会把未来几个时间段的天气信息整理成文本返回。

6.为什么用async

这里所有 Tool 都写成了异步函数:
主要原因是这些工具需要访问外部 API,而网络请求通常是整个流程里最耗时的部分。

使用异步方式的好处是:

在等待网络响应时不会一直阻塞;
更适合处理多个请求;
对需要频繁调用外部接口的服务更友好。
更准确地说,async 不是“让代码自动变快”,而是让程序在等待 I/O 操作时更高效

用户问题是如何一步步被解决的

调用流程:

  1. MCP Host 启动,并建立与 MCP Server 的连接。
  2. MCP Host 向 MCP Server 获取当前可用的 Tool 列表及其说明信息。
  3. MCP Server 将已注册的 Tool 名称、用途、参数要求等元数据返回给 MCP Host。
  4. 当用户发起请求后,MCP Host 会将用户问题、历史记录以及可用 Tool 的说明一并发送给大模型。
  5. 大模型根据用户目标和 Tool 描述,判断当前任务是否需要调用外部工具。
  6. 如果模型发现自身无法直接完成任务,例如无法获取实时天气,就会选择合适的 Tool,并按照约定格式生成调用请求。
  7. MCP Host 解析模型输出的工具调用请求,并将对应参数转发给 MCP Server。
  8. MCP Server 执行具体 Tool 逻辑,例如查询天气 API、读写文件或执行系统命令,并将结果返回给 MCP Host。
  9. MCP Host 再将工具返回结果连同历史上下文重新发送给大模型。
  10. 大模型基于新的上下文继续判断任务是否已经完成,或者是否还需要继续调用其他 Tool。
  11. 如果还有后续操作,例如把结果写入文件,模型会再次发起新的工具调用请求。
  12. 当所有步骤完成后,大模型生成最终答复。
  13. MCP Host 将最终答复返回给用户。

流程图说明

image
图片思路参考:其方式类似于B站UP主的马克的技术工作坊的图片文档。

posted @ 2026-04-05 18:22  枫叶天凝  阅读(1)  评论(0)    收藏  举报