19-为什么AI工程这么喜欢"创造名词":从Prompt到Skill的造词运动

53 阅读18分钟

引言:AI工程的"造词游戏"

如果你刚接触AI工程,肯定会被一堆高大上的术语搞晕:Prompt、SystemPrompt、Memory/Context、Function Calling、Tool、MCP、Agent、Skill、A2A......

这些概念看起来很深奥,感觉每个都是突破性创新。但说白了

  • Prompt = 你给模型的输入
  • SystemPrompt = 你给模型的固定背景
  • Memory = 历史对话记录
  • Function Calling = 让模型返回个JSON告诉你该调用什么函数
  • Tool = 给函数包了层壳
  • MCP = 给Tool的接口定了个标准
  • Agent = 让模型循环调用Tool直到完成任务
  • Skill = 把一堆Prompt和Tool打包
  • A2A = 让多个Agent互相调用

核心观点:这些术语本质上都是在做一件事——管理"上下文"(Context)。整个AI工程就是"上下文工程",这些花里胡哨的名词,很大程度上是没活硬整出来的。

但话说回来,理解这些术语的演进逻辑,确实能帮你快速掌握AI应用开发的全貌。本文用最直白的语言,带你看透这场"造词运动"。


第一幕:接词游戏变成了聊天机器人

预训练模型的本质:接词游戏

大语言模型(LLM)在预训练阶段做的事情非常简单:给定前面的词,预测下一个词

# 预训练模型的训练目标
输入:"今天天气"
模型预测:"很好" (概率: 0.3)
         "不错" (概率: 0.25)
         "真棒" (概率: 0.15)
         ...

# 就是个接词游戏
输入:"从前有座山,山里有座"
模型:"庙"

这就是个统计接龙机器,没啥智能可言。你给它灌海量文本,它学会了"哪些词经常一起出现"。

ChatGPT的魔法:指令微调让模型有了"人感"

OpenAI做了个关键改进:把接词游戏改造成一问一答的形式。这就是指令微调(Instruction Tuning)。

# 预训练模型(原始状态)
输入:"翻译成英文:你好"
输出:"世界" (因为"你好世界"经常一起出现)

# 指令微调后(ChatGPT)
输入:"翻译成英文:你好"
输出:"Hello"

# 怎么做到的?微调数据长这样
训练数据 = [
    {"input": "翻译成英文:你好", "output": "Hello"},
    {"input": "翻译成英文:再见", "output": "Goodbye"},
    {"input": "写一首诗", "output": "春风拂面..."},
    ...  # 几万到几十万条"问题-答案"对
]

通过这种微调,模型学会了:

  1. 理解人类的指令意图
  2. 用"助手"的口吻回答
  3. 拒绝不当请求

这就有了"人感" —— 你感觉在跟一个助手对话,而不是一个接词机器。


第二幕:给输入起了个高大上的名字

Prompt:把"输入"包装成专业术语

既然模型能一问一答了,那我们给用户的输入起个名字吧。

就叫Prompt(提示词)。

# 其实就是字符串
prompt = "帮我写一首关于春天的诗"

response = model.generate(prompt)

为什么不直接叫"输入"?

因为LLM的"输入"和传统编程不同:

  • 传统编程:print("Hello") —— 精确指令
  • LLM:"帮我写代码打印Hello" —— 自然语言描述

所以搞个新词"Prompt"区分一下,顺便引出了"Prompt Engineering"(提示工程)这个听起来很专业的领域。

Prompt Engineering的本质:怎么写输入,让模型给你想要的输出。

# 差劲的Prompt
"写代码"  # 模型一脸懵逼

# 好的Prompt
"""
用Python写一个函数:
- 输入:整数列表
- 输出:所有偶数的平方和
- 要求:用列表推导式
- 示例:[1,2,3,4] → 20
"""
# 模型:这下懂了!

小结:Prompt = 你的输入文本。起这个名字是为了和传统编程的"指令"区分,顺便搞出一个新领域。


第三幕:重复的东西提炼出来

SystemPrompt:避免每次都说废话

用ChatGPT用久了,你会发现一个问题:有些话每次都要重复说

# 每次对话都要交代背景
对话1: "你是专业翻译,请翻译:Hello"
对话2: "你是专业翻译,请翻译:World"
对话3: "你是专业翻译,请翻译:AI"

# "你是专业翻译"说了3遍,烦不烦?

OpenAI想了个办法:把重复的背景信息单独拎出来,只说一次

这就是SystemPrompt(系统提示词)。

# OpenAI API的设计
messages = [
    {
        "role": "system",  # SystemPrompt(角色设定)
        "content": "你是专业翻译助手"
    },
    {"role": "user", "content": "Hello"},      # 输出:"你好"
    {"role": "user", "content": "World"},      # 输出:"世界"
    {"role": "user", "content": "AI"},         # 输出:"人工智能"
]

本质:SystemPrompt = 所有对话共享的固定背景,避免重复。

为什么搞个新名词?

因为SystemPrompt和Prompt的生命周期不同

  • SystemPrompt:整个会话期间不变
  • Prompt:每次对话不同
概念作用生命周期说人话
SystemPrompt角色设定整个会话固定背景
Prompt (UserPrompt)具体问题单次对话当前输入

小结:SystemPrompt = 提炼出来的固定背景。起这个名字是为了区分"固定的"和"临时的"输入。


第四幕:让模型"记住"历史对话

Memory/Context:历史消息记录

LLM本身是无状态的 —— 每次调用都是全新的,它不记得你之前说过啥。

# 第一次调用
messages = [{"role": "user", "content": "我叫小明"}]
response = model.generate(messages)
# AI: "你好小明!"

# 第二次调用(全新的)
messages = [{"role": "user", "content": "我叫什么?"}]
response = model.generate(messages)
# AI: "抱歉,我不知道你的名字" —— 它忘了!

怎么办?把历史对话都传给它

# 带历史的调用
messages = [
    {"role": "user", "content": "我叫小明"},
    {"role": "assistant", "content": "你好小明!"},
    {"role": "user", "content": "我叫什么?"}  # ← 现在它能看到完整历史
]
response = model.generate(messages)
# AI: "你叫小明"

这个"历史消息列表"有两个名字:

  • Memory(记忆)—— 更直白
  • Context(上下文)—— 更学术

本质:就是把所有历史对话拼成一个大字符串传给模型。

# 实际传给模型的
完整输入 = """
System: 你是Python助手

User: 什么是列表?
Assistant: 列表是Python的数据结构...

User: 如何添加元素?
Assistant: 使用append()方法...

User: 那如何删除元素?  ← 当前问题
"""

# 模型看到整个"Context",才能理解"那"指的是列表

核心问题:Context长度限制

模型的上下文窗口是有限的(比如GPT-4是128K tokens ≈ 10万字)。

聊天聊久了,历史消息会超长,怎么办?

# 方案1:截断(简单粗暴)
messages = messages[-20:]  # 只保留最近20轮

# 方案2:总结(复杂但好)
old_summary = summarize(messages[:-10])  # 把旧对话总结成摘要
messages = [old_summary] + messages[-10:]

# 方案3:RAG(外挂知识库)
# 把历史存到数据库,需要时检索相关片段

小结:Memory/Context = 历史对话记录。起这些名字是为了让"历史消息列表"听起来更专业。


第五幕:让模型能"动手"

Function Calling:让模型返回JSON,告诉你该调用什么函数

早期的ChatGPT只能"说话",不能"做事"。

用户:"现在几点?"
AI:"抱歉,我无法获取实时时间"

用户:"北京天气如何?"
AI:"抱歉,我的知识截止到2024年..."

OpenAI想了个办法:让模型不直接执行,而是告诉你该调用什么函数

这就是Function Calling(函数调用)。

工作流程

# 1. 告诉模型有哪些函数可用
functions = [
    {
        "name": "get_weather",
        "description": "获取城市天气",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"}
            }
        }
    }
]

# 2. 用户提问
user_message = "北京天气如何?"

# 3. 模型返回函数调用请求(JSON格式)
response = model.generate(user_message, functions=functions)
# 返回:
# {
#     "function_call": {
#         "name": "get_weather",
#         "arguments": '{"city": "北京"}'
#     }
# }

# 4. 你的代码真正执行函数
result = get_weather(city="北京")  # {"temp": 15, "condition": "晴"}

# 5. 把结果传回模型
messages.append({
    "role": "function",
    "name": "get_weather",
    "content": json.dumps(result)
})

# 6. 模型生成最终回答
final_response = model.generate(messages)
# "北京现在15°C,天气晴朗。"

核心理解

  • 模型不执行函数(安全考虑)
  • 模型只决定调用什么函数、传什么参数
  • 真正执行是你的代码做的
用户提问 → 模型决策 → 返回JSON → 代码执行 → 结果回传 → 模型整合 → 最终回答

为什么叫Function Calling不叫Function Execution?

因为模型只"Call"(请求调用),不"Execute"(执行)。执行权在开发者手里,防止模型乱来。

小结:Function Calling = 让模型返回个JSON,告诉你该调用什么函数、传什么参数。实际执行是你的代码做的。


第六幕:给函数包层壳,起个新名字

Tool:Function的标准化包装

有了Function Calling,你可以定义很多函数给模型用。但问题来了:函数越来越多,怎么管理?

# 散装函数
def get_weather(city): ...
def search_web(query): ...
def read_file(path): ...
def write_file(path, content): ...
def send_email(to, subject, body): ...
# ... 100个函数

# 每个函数的描述、参数都散落各处,混乱!

于是有人想:不如给函数包层壳,统一格式

这个"包了壳的函数"就叫Tool(工具)。

# 原始函数
def get_weather(city: str) -> dict:
    return call_weather_api(city)

# 包装成Tool
class WeatherTool:
    name = "get_weather"
    description = "获取城市实时天气"

    parameters = {
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "城市名"}
        }
    }

    def run(self, city: str) -> dict:
        return call_weather_api(city)

# 现在函数有了统一的"壳"
tools = [WeatherTool(), SearchTool(), FileReadTool(), ...]

Tool vs Function的区别

维度FunctionTool
本质一个函数函数 + 元数据(描述、参数schema)
组织方式散装统一格式
类比裸函数包装好的模块

为什么搞个新名词?

因为LangChain等框架需要统一接口,方便管理。叫"Tool"听起来也更专业一点。

本质:Tool = 给Function包了层标准化的壳。

小结:Tool就是Function的包装。起这个名字是为了标准化、听起来更专业。


第七幕:没活硬整的巅峰

MCP:给Tool的接口定个标准

有了Tool,每个AI平台都可以定义自己的Tool格式。于是问题来了:每家格式不一样!

# OpenAI的Tool格式
{
    "type": "function",
    "function": {
        "name": "get_weather",
        "parameters": {...}
    }
}

# Anthropic的Tool格式
{
    "name": "get_weather",
    "input_schema": {...}
}

# Google的Tool格式
{
    "function_declarations": [{
        "name": "get_weather",
        "parameters": {...}
    }]
}

# 同一个工具要写3遍!

Anthropic说:不如我定个标准,大家都遵守?

这就是MCP(Model Context Protocol,模型上下文协议)

MCP想做什么?

  1. 统一Tool的定义格式
  2. 统一AI访问Tool的协议
  3. 让Tool可以跨平台使用
# 用MCP定义Tool(一次)
@mcp.tool()
def get_weather(city: str) -> dict:
    """获取天气"""
    return call_api(city)

# 自动转换成各家格式
openai_format = mcp.to_openai(get_weather)
anthropic_format = mcp.to_anthropic(get_weather)
google_format = mcp.to_google(get_weather)

类比:MCP就像USB标准 —— 统一接口,随便插。

MCP的三个核心概念

# 1. Resources(资源):AI可以读取的数据
{
    "resource": {
        "uri": "file:///path/to/doc.txt",
        "text": "内容..."
    }
}

# 2. Tools(工具):AI可以调用的函数
{
    "tool": {
        "name": "search",
        "inputSchema": {...}
    }
}

# 3. Prompts(提示模板):预定义的Prompt
{
    "prompt": {
        "name": "code_review",
        "arguments": [...]
    }
}

评价:MCP的想法很好,但目前还在推广阶段。大多数人用的还是各家自己的格式。

为什么说是"没活硬整"?

因为:

  1. 各家已经有自己的Tool格式,迁移成本高
  2. MCP主要是Anthropic在推,OpenAI、Google未必买账
  3. 本质上就是个"接口标准",非得起个Protocol的名字

小结:MCP = 给Tool的接口定了个标准。想法不错,但更多是Anthropic的市场策略。


第八幕:循环调用Tool,就成了Agent

Agent:让模型自己决定调用多少次Tool

有了Function Calling,模型可以调用一个函数。但如果任务需要多步操作呢?

# 用户:"帮我分析这份财报"

# 需要多步:
# 1. 读取PDF文件
# 2. 提取数据
# 3. 计算关键指标
# 4. 生成图表
# 5. 撰写总结

传统方式:你要写代码控制每一步。

Agent方式:让模型自己决定调用多少次、调用什么。

# Agent的核心:循环调用
def agent_loop(task, tools, max_iterations=10):
    messages = [{"role": "user", "content": task}]

    for i in range(max_iterations):
        # 1. 模型决策:下一步做什么?
        response = model.generate(messages, tools=tools)

        # 2. 如果模型说"完成了",退出循环
        if response.finish_reason == "stop":
            return response.content

        # 3. 如果模型要调用函数,执行之
        if response.function_call:
            func_name = response.function_call.name
            func_args = response.function_call.arguments

            result = execute_function(func_name, func_args)

            # 4. 把结果传回模型
            messages.append({
                "role": "function",
                "name": func_name,
                "content": result
            })

        # 循环继续...

    return "达到最大迭代次数"

# 使用
agent_loop(
    task="帮我分析这份财报",
    tools=[read_pdf, extract_data, calculate, plot, write_summary]
)

Agent的核心

  1. 模型自己决定下一步做什么(思考)
  2. 调用Tool(行动)
  3. 看结果(观察)
  4. 重复1-3,直到完成

这就是ReAct循环(Reasoning + Acting):

循环:
  Thought(思考):分析当前状态,决定下一步
  Action(行动):调用Tool
  Observation(观察):查看结果
  → 回到Thought

为什么叫Agent?

因为它有"自主性" —— 你只给目标,它自己想办法完成。

对比传统AIAgent
交互模式一问一答给目标,自己搞定
工具调用调用一次循环调用多次
决策能力自己决定下一步

本质:Agent = 给模型加个循环,让它自己反复调用Tool直到完成任务。

小结:Agent就是个while循环,让模型不断"思考-行动-观察"直到任务完成。起这个名字是为了让它听起来很智能。


第九幕:把Prompt和Tool打包就是Skill

Skill:预定义的Prompt + Tool组合

Agent能自主调用Tool了,但你会发现:有些任务的流程是固定的

比如"代码审查":

  1. 读取代码文件
  2. 运行静态分析
  3. 运行测试
  4. 生成报告

与其每次让Agent自己摸索,不如把这个流程固定下来

这就是Skill(技能)。

# Skill = 预定义的Prompt模板 + Tool列表 + 执行流程

class CodeReviewSkill:
    """代码审查技能"""

    # 固定的Prompt模板
    system_prompt = """
    你是代码审查专家。
    你需要:
    1. 检查代码规范
    2. 发现潜在bug
    3. 给出改进建议
    """

    # 需要用到的Tool
    tools = [
        ReadFileTool(),
        RunLinterTool(),
        RunTestsTool(),
        GenerateReportTool()
    ]

    # 执行流程
    def execute(self, file_path):
        # 步骤1:读取代码
        code = self.tools[0].run(file_path)

        # 步骤2:运行linter
        lint_result = self.tools[1].run(code)

        # 步骤3:运行测试
        test_result = self.tools[2].run(file_path)

        # 步骤4:生成报告
        report = self.tools[3].run({
            "code": code,
            "lint": lint_result,
            "test": test_result
        })

        return report

# 使用Skill
skill = CodeReviewSkill()
skill.execute("main.py")

Skill vs Agent的区别

维度AgentSkill
灵活性自主决策固定流程
适用场景开放任务重复任务
类比助理(自己想办法)SOP(标准操作流程)

为什么搞个Skill?

因为:

  1. 有些任务流程固定,不需要Agent每次重新规划
  2. 固定流程更可控、更高效
  3. 可以把常用流程打包复用

本质:Skill = Prompt模板 + Tool列表 + 固定流程。就是把Agent的"自主决策"换成了"预定义流程"。

小结:Skill就是把Prompt和Tool打包成固定流程。起这个名字是为了区分"自主的Agent"和"固定流程的Skill"。


第十幕:让多个Agent互相调用

A2A:Agent间通信协议

有了Agent,Google想:能不能让多个Agent协作?

比如软件开发:

  • 需求分析Agent:理解需求
  • 架构设计Agent:设计架构
  • 开发Agent:写代码
  • 测试Agent:测试
  • 部署Agent:部署

问题:Agent之间怎么通信?

# Agent1想把任务交给Agent2,但格式不一样

# Agent1的接口
agent1.execute(task="分析需求", data=requirements)

# Agent2的接口
agent2.run(action="design", input=requirements)

# Agent3的接口
agent3.process({"type": "coding", "spec": requirements})

# 没法统一!

Google说:不如我定个标准?

这就是A2A(Agent-to-Agent Protocol,Agent间通信协议)

A2A的核心

# 统一的消息格式
class A2AMessage:
    sender: str      # 发送者Agent ID
    receiver: str    # 接收者Agent ID
    task: str        # 任务描述
    context: dict    # 上下文
    priority: int    # 优先级

# 所有Agent都用这个格式通信
response = a2a.send(
    to=agent2,
    message=A2AMessage(
        sender="agent1",
        receiver="agent2",
        task="设计架构",
        context={"requirements": ...}
    )
)

典型协作模式

# 1. 流水线模式
需求分析Agent → 设计Agent → 开发Agent → 测试Agent

# 2. 管理者-工人模式
管理Agent
  ├─→ 工人Agent1
  ├─→ 工人Agent2
  └─→ 工人Agent3

# 3. 对等协作模式
Agent1 ←→ Agent2 ←→ Agent3  # 互相讨论

评价:和MCP一样,A2A的想法很好,但:

  1. 目前还在推广阶段
  2. 主要是Google在推,其他家未必买账
  3. 本质上就是"接口标准",搞个Protocol的名字

为什么说是"没活硬整"?

因为多Agent协作可以用很多方式实现,不一定需要A2A。比如:

  • 直接函数调用
  • 共享消息队列
  • HTTP API

A2A更多是个标准化尝试,而不是必需品。

小结:A2A = 定义了Agent之间的通信格式。想法不错,但更多是Google的市场策略。


总结:这些都叫"上下文工程"

回看这些术语,你会发现:本质上都在管理"上下文"(Context)

┌───────────────────────────────────────┐
│         上下文工程 (Context Engineering)  │
├───────────────────────────────────────┤
│                                        │
│  Prompt         → 当前输入             │
│  SystemPrompt   → 固定背景             │
│  Memory/Context → 历史对话             │
│  Function Call  → 函数调用信息          │
│  Tool           → 可用工具列表          │
│  Agent          → 多轮对话上下文         │
│  Skill          → 预定义上下文模板       │
│  MCP/A2A        → 跨系统的上下文共享     │
│                                        │
└───────────────────────────────────────┘

为什么说是"没活硬整"?

  1. Prompt —— 就是输入,非得起个新名字
  2. SystemPrompt —— 就是固定背景,非得单独拎出来起名
  3. Memory/Context —— 就是历史记录,搞两个名字
  4. Function Calling —— 就是让模型返回JSON,搞得很高级
  5. Tool —— 给Function包层壳,起个新名字
  6. MCP —— Tool的接口标准,搞个Protocol
  7. Agent —— 加个while循环,就成智能体了
  8. Skill —— Prompt和Tool打包,又是新名字
  9. A2A —— Agent通信标准,又是个Protocol

但话说回来:这些术语确实有存在价值。

它们帮助我们:

  1. 清晰地表达概念 —— "Agent"比"带循环的模型调用"简洁
  2. 构建抽象层级 —— 从Prompt → Tool → Agent,逐层抽象
  3. 形成技术标准 —— MCP、A2A虽然还在推广,但方向是对的

三条演进主线

主线1:上下文的范围越来越大

Prompt        → 单条输入
SystemPrompt  → 固定背景
Memory        → 历史对话
Agent         → 多轮交互
A2A           → 跨Agent共享

主线2:从静态到动态

Prompt        → 静态文本
Function Call → 动态调用
Tool          → 标准化调用
Agent         → 循环调用

主线3:从单一到复合

Function      → 单个函数
Tool          → 封装的工具
Skill         → 打包的流程
Multi-Agent   → 多Agent协作

技术栈全景

┌─────────────────── 应用层 ────────────────────┐
│  Multi-Agent System (多Agent协作)            │
│    └─ A2A Protocol (通信标准)                 │
└──────────────────────────────────────────────┘
                      ↓
┌─────────────────── 能力层 ────────────────────┐
│  Agent (循环调用)                             │
│    └─ Skill (固定流程)                        │
└──────────────────────────────────────────────┘
                      ↓
┌─────────────────── 工具层 ────────────────────┐
│  Tool (标准化工具)                            │
│    └─ MCP Protocol (工具标准)                 │
│    └─ Function Calling (函数调用)             │
└──────────────────────────────────────────────┘
                      ↓
┌─────────────────── 交互层 ────────────────────┐
│  Context / Memory (历史记录)                  │
│  SystemPrompt (固定背景)                      │
│  Prompt (当前输入)                            │
└──────────────────────────────────────────────┘
                      ↓
┌─────────────────── 模型层 ────────────────────┐
│  Large Language Model (接词游戏)              │
│    └─ Instruction Tuning (指令微调)           │
└──────────────────────────────────────────────┘

实用建议:你需要用到哪些?

根据你的应用场景,选择合适的抽象层级:

场景1:简单问答 → 只需Prompt

# 翻译、总结、问答等简单任务
prompt = "翻译成英文:你好"
response = model.generate(prompt)

场景2:固定角色 → 加上SystemPrompt

# 需要特定风格的助手
messages = [
    {"role": "system", "content": "你是专业翻译"},
    {"role": "user", "content": "你好"}
]

场景3:多轮对话 → 管理Memory

# 聊天机器人、客服等需要记住上下文的场景
messages = []  # 历史记录

# 每轮对话
messages.append({"role": "user", "content": user_input})
response = model.generate(messages)
messages.append({"role": "assistant", "content": response})

场景4:需要实时信息 → 使用Function Calling

# 查天气、搜索、读文件等需要外部数据的场景
functions = [get_weather, search_web, read_file]
response = model.generate(user_input, functions=functions)

if response.function_call:
    result = execute_function(response.function_call)
    final_response = model.generate([..., result])

场景5:复杂任务 → 用Agent

# 需要多步操作的任务:数据分析、代码生成、报告撰写等
agent = Agent(tools=[read, analyze, plot, write])
agent.run("分析这份数据并生成报告")

场景6:重复流程 → 封装成Skill

# 代码审查、文档生成等固定流程
skill = CodeReviewSkill()
skill.execute("main.py")

场景7:多领域协作 → Multi-Agent

# 软件开发、复杂项目等需要专业分工的场景
team = [AnalystAgent, DesignerAgent, DeveloperAgent, TesterAgent]
result = team.collaborate("开发一个博客系统")

原则

  • 简单任务用简单工具 —— 不要为了用Agent而用Agent
  • 复杂任务逐步升级 —— 从Prompt开始,需要时再加功能
  • 不要被术语吓倒 —— 理解本质,选择合适的抽象

结语:穿透术语看本质

AI工程的"造词运动"看似复杂,本质上就是在解决一个问题:如何更好地管理上下文(Context)

从这个角度看:

  • Prompt/SystemPrompt/Memory = 上下文的组成部分
  • Function Calling/Tool = 扩展上下文(加入外部信息)
  • Agent = 动态管理上下文(循环更新)
  • Skill = 预定义上下文模板
  • MCP/A2A = 跨系统共享上下文

这些术语有些确实有必要(比如Prompt、Agent),有些确实有点"没活硬整"(比如MCP)。

理解这些术语的演进逻辑,能帮你:

  1. 快速掌握AI应用开发的全貌
  2. 选择合适的抽象层级
  3. 不被新名词吓倒(它们可能就是老概念换了个名字)

最重要的:技术是为了解决问题,不是为了炫耀名词。

当你遇到新术语时,问自己三个问题:

  1. 它解决了什么问题?
  2. 和已有概念有什么区别?
  3. 我的项目需要它吗?

不要被术语绑架,做个清醒的工程师。