智能体开发

一、什么是智能体

在经典智能体框架中,智能体(Agent)一般指能够在环境中感知信息、基于策略做出决策并采取行动,以最大化回报或满足目标约束的系统。

在大模型应用开发中,智能体通常指一种以大语言模型为推理与决策核心,结合记忆、工具调用与环境交互能力,能够进行规划决策并执行动作以达成目标的软件系统。

OpenAI 前安全系统团队负责人翁丽莲(Lilian Weng)于 2023 年 6 月在个人博客中系统化总结了当时流行的 LLM Agent 典型架构。

实际开发中,四个要素并不需要同时出现。可以用一句话总结:

  • 必须的:行动(Action)
  • 几乎总是存在的:工具(Tool)
  • 有条件存在的:计划(Planning)
  • 最容易被省略的:记忆(Memory)

说明:以上是工程经验法则,不是严格定义。是否引入规划、记忆等能力,应结合任务复杂度、成本、延迟和可控性权衡。

二、何时需要智能体

智能体是大模型工程实现中复杂度较高的方案,涉及工具调用、记忆、规划、反思与多组件协作。

当任务需要多步决策、动态工具调用或环境交互,且提示词工程、RAG、微调等方式无法满足预期时,可以评估智能体开发。但应同时权衡成本、延迟、可控性与安全风险,不要把智能体作为默认方案。

2.1 开发方式选型总表

现象 首选手段 备选 说明
缺少工具、需要拆分任务、需要多步决策 Agent 工作流编排 注意成本、延迟、可控性
指令遵循效果不好 优化提示词、提供示例 微调 先尝试提示词和示例
缺少参考资料 RAG 多轮检索、重排序 关注检索质量与引用检查
模型缺少领域语言理解 继续预训练 再微调 注意数据覆盖和灾难性遗忘
规则约束强、格式要求高 程序化校验、约束解码、模板 Agent 兜底重写 Agent 不是首选
数据分布与线上不一致 收集数据重新微调 调整数据策略 先分析分布差异

这些方式不是互斥的,应按"简单优先、成本递增"的顺序选型。

2.2 提示词工程

如果提示词工程效果不好,首先应尝试优化提示词;如果优化提示词效果仍不好,再分析原因选择其他开发方式。提示词效果不好可能是因为:

  1. 缺少工具、需要拆分任务等,这类场景天然适合 Agent 开发。
  2. 指令遵循效果不好,先尝试提供示例;如果效果还不好,可以考虑微调。
  3. 缺少参考资料,尝试 RAG。
  4. 模型缺少对领域语言的系统理解,尝试继续预训练。

2.3 RAG

RAG 效果不好可能是因为:

  1. 检索质量不高、文档冲突或者模型未正确引用资料。此时可以结合 Agent 引入多轮检索、冲突比对以及强制引用检查等,提升结果的可控性和质量。
  2. 上述情况也可以尝试调整 RAG 的文档切分、重排序等策略以提升质量。
  3. 模型读不懂语料,即模型拿到了正确的参考资料,但推理不对,缺少领域知识,尝试继续预训练。

2.4 微调

微调效果不好可能是因为:

  1. 规则约束太强,例如强制要求模型输出特定格式,微调无法保证极高准确率。此时可以考虑通过 Agent 引入格式校验、多次重写等机制,提升生成质量;但格式校验应优先使用程序化校验、约束解码或模板。
  2. 数据分布和真实分布不一致,例如客服场景中的微调数据经过筛选,线上输入五花八门和训练集不同。可以考虑收集合适的数据重新微调。
  3. 如果发现模型读不懂语料,应尝试继续预训练,然后再微调。

2.5 继续预训练

只有在系统性缺失领域语言或数据分布时,才需要继续预训练(Continual Pre-Training,CPT)。如果继续预训练效果不好,可能是因为:

  1. 数据分布没有覆盖使用场景,或者发生了灾难性遗忘,即失去了继续预训练前的能力。此时应该在继续预训练数据集中混合通用语料,并收集数据重新训练。
  2. 继续预训练效果不好时,通常应该分析原因重新训练,Agent 很难从根本上解决问题。但仍然可以结合 Agent 开发,补充校验和多次重写等机制提升生成质量。这是无奈之举,质量提升有限,治标不治本。

三、工具调用的实现方式

3.1 Function Calling

3.1.1 定义

Function Calling(函数调用),也常称为 Tools Call(工具调用),为模型提供了一种强大而灵活的方式,使其能够与外部系统交互并访问训练数据之外的数据,拓展了模型的能力边界。

3.1.2 流程

以 DeepSeek 官方 API 为例演示 Function Calling。

步骤一:定义工具并向模型发送消息

使用 Linux 命令行工具 curl 测试:

bash 复制代码
curl https://api.deepseek.com/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${API_KEY}" \
  -d '{
  "model": "deepseek-chat",
  "messages": [
    {
      "role": "system",
      "content": "你是个智能天气查询助手,根据用户的提问自主调用工具"
    },
    {
      "role": "user",
      "content": "北京市天气如何?"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "根据用户输入的城市信息,获取该城市的天气",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string",
              "description": "城市名称,只保留最细粒度的地区名称"
            }
          },
          "required": ["city"]
        }
      }
    }
  ]
}'

步骤二:模型返回调用信息

模型返回的调用信息如下:

json 复制代码
{
  "id": "7cccd00d-f0a5-4b2e-872c-a54bdb767796",
  "object": "chat.completion",
  "created": 1767176438,
  "model": "deepseek-chat",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "我来帮您查询北京市的天气情况。",
        "tool_calls": [
          {
            "index": 0,
            "id": "call_00_Kpq3g6mPl9BYlZIe1NSNm3Cs",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"city\": \"北京\"}"
            }
          }
        ]
      },
      "logprobs": null,
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 336,
    "completion_tokens": 52,
    "total_tokens": 388,
    "prompt_tokens_details": {
      "cached_tokens": 0
    },
    "prompt_cache_hit_tokens": 0,
    "prompt_cache_miss_tokens": 336
  },
  "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache"
}

说明:arguments 是 JSON 字符串,不是 JSON 对象;finish_reason 为 tool_calls 表示模型请求调用工具。

步骤三:根据调用信息执行函数

根据模型返回的调用信息(函数名称、参数)调用相应的函数,这一步应该在代码中完成,用 curl 无法模拟,省略。

假设调用后返回的信息如下:

json 复制代码
{
  "temp": "2℃",
  "text": "晴",
  "wind": "西北风3级"
}

步骤四:将调用结果和历史消息打包后发送给模型

完整命令如下:

bash 复制代码
curl https://api.deepseek.com/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${API_KEY}" \
  -d '{
  "model": "deepseek-chat",
  "messages": [
    {
      "role": "system",
      "content": "你是个智能天气查询助手,根据用户的提问自主调用工具"
    },
    {
      "role": "user",
      "content": "北京市天气如何?"
    },
    {
      "role": "assistant",
      "content": "我来帮您查询北京市的天气情况。",
      "tool_calls": [
        {
          "index": 0,
          "id": "call_00_Kpq3g6mPl9BYlZIe1NSNm3Cs",
          "type": "function",
          "function": {
            "name": "get_weather",
            "arguments": "{\"city\": \"北京\"}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "call_00_Kpq3g6mPl9BYlZIe1NSNm3Cs",
      "content": "{\"temp\":\"2℃\",\"text\":\"晴\",\"wind\":\"西北风3级\"}"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "根据用户输入的城市信息,获取该城市的天气",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string",
              "description": "城市名称,只保留最细粒度的地区名称"
            }
          },
          "required": ["city"]
        }
      }
    }
  ]
}'

步骤五:模型根据函数调用结果整理信息并回答

模型响应如下:

json 复制代码
{
  "id": "4f5f2133-6519-497e-aecc-2bd25a37c747",
  "object": "chat.completion",
  "created": 1767177264,
  "model": "deepseek-chat",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "根据查询结果,北京市当前的天气情况如下:\n\n- 温度:2℃\n- 天气状况:晴\n- 风力:西北风3级\n\n今天北京天气晴朗,温度在2℃左右,风力不大,是个不错的天气。建议您外出时适当保暖,虽然天气晴朗但温度还是偏低的。"
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 422,
    "completion_tokens": 70,
    "total_tokens": 492,
    "prompt_tokens_details": {
      "cached_tokens": 384
    },
    "prompt_cache_hit_tokens": 384,
    "prompt_cache_miss_tokens": 38
  },
  "system_fingerprint": "fp_eaab8d114b_prod0820_fp8_kvcache"
}

3.1.3 Function Calling 的不足

  1. 工具实现与复用成本高,协作困难

    开发者需要自己实现工具,并编写可被调用的描述信息。工具通常与业务、环境绑定,复用难、共享难、生态扩展慢。

  2. 规范碎片化,跨模型适配负担重

    不同厂商定义的 Function Calling 规范不同,开发者可能需要为同一个工具编写多份描述信息,维护成本和一致性风险较高。

  3. 可靠性不足

    工具可能没有经过足够调试。如果描述信息不完善,模型可能在某些场景下不能正确调用工具,工具可靠性不足。

3.2 最小 Python 示例

以下示例使用 OpenAI Python SDK 调用兼容接口,演示完整的工具调用闭环。运行前请安装 openai,并设置环境变量 DEEPSEEK_API_KEY。

python 复制代码
import json
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "根据用户输入的城市信息,获取该城市的天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,只保留最细粒度的地区名称",
                    }
                },
                "required": ["city"],
            },
        },
    }
]


def get_weather(city: str) -> dict:
    # 这里替换为真实天气 API 调用
    return {
        "temp": "2℃",
        "text": "晴",
        "wind": "西北风3级",
    }


messages = [
    {
        "role": "system",
        "content": "你是个智能天气查询助手,根据用户的提问自主调用工具",
    },
    {
        "role": "user",
        "content": "北京市天气如何?",
    },
]

response = client.chat.completions.create(
    model="deepseek-chat",
    messages=messages,
    tools=tools,
)

message = response.choices[0].message
messages.append(message.model_dump(exclude_none=True))

if message.tool_calls:
    for tool_call in message.tool_calls:
        if tool_call.function.name == "get_weather":
            args = json.loads(tool_call.function.arguments)
            result = get_weather(args["city"])
            messages.append(
                {
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": json.dumps(result, ensure_ascii=False),
                }
            )

    final_response = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages,
        tools=tools,
    )
    print(final_response.choices[0].message.content)
else:
    print(message.content)

四、MCP

4.1 定义

MCP(Model Context Protocol,模型上下文协议)是一套标准化的通讯协议,旨在规范 AI 模型和外部工具、数据源的连接方式,由 Anthropic(Claude 母公司)于 2024 年 11 月提出。

MCP 可以类比为 AI 时代的 USB-C 通用接口:开发者只需按标准开发一次 MCP Server,任何支持该协议的 AI 应用都能即插即用。

通过 MCP 协议,AI 应用和 MCP Server 可以建立多对多的双向数据流。

4.2 角色与流程

MCP 可以理解为对 Function Calling 的进一步封装和拓展。需要准确区分角色:

  • Host:AI 应用本身,例如 IDE、聊天客户端或 Agent 应用。
  • Client:Host 内部负责与 MCP Server 建立连接、发送请求的协议客户端。
  • Server:按 MCP 协议暴露工具、资源、提示词的服务端。
  • 模型:根据上下文决定是否调用工具、调用哪个工具以及传什么参数。
  • Server:负责执行工具并返回结果。

MCP 并不是"由 MCP Server 决定是否调用工具"。模型仍然负责决策,Host/Client 负责按协议发起调用,Server 负责执行。

典型流程如下:

  1. Host(AI 应用)启动并连接 MCP Server。
  2. MCP Client 与 Server 初始化并协商能力。
  3. Server 暴露 Tools(工具)、Resources(资源)、Prompts(提示词)等能力。
  4. 模型根据上下文决定是否调用工具。
  5. Host/Client 向 MCP Server 发送工具调用请求。
  6. MCP Server 执行工具并返回结果。
  7. Host 将结果交给模型,生成最终回答。

4.3 MCP 的三大模块

  1. Tools(工具)

    最常用的模块。MCP Server 暴露可调用工具,供 AI 应用发现和调用。

  2. Resources(资源)

    MCP Server 提供的被动数据源,例如文件、数据库等,可以被 AI 应用主动获取,作为模型上下文传递。

  3. Prompts(提示词)

    MCP Server 提供的预置提示词模板,由 AI 应用主动获取后发给大模型。AI 应用也可以和用户交互,补全模板中的关键信息。

实际开发中最常用的是 Tools 模块。

4.4 MCP 相较于 Function Calling 的优势

  1. 降低协作与复用成本

    MCP 协议允许开发者把工具暴露为 MCP Server,可以被多个 AI 应用复用,减少重复开发和共享困难。

  2. 降低跨模型适配负担

    Function Calling 规范存在厂商差异,而 MCP 通过标准化协议降低同一工具在多个 AI 应用之间适配的成本。

  3. 支持工具发现与生态扩展

    MCP 支持按协议发现 Server 提供的能力,有利于形成工具生态。

  4. 除工具外还支持资源和提示词

    MCP 不仅覆盖工具调用,还覆盖 Resources 和 Prompts,能力边界比单一 Function Calling 更宽。

4.5 Function Calling 与 MCP 对比

维度 Function Calling MCP
工具定义位置 通常在 AI 应用内定义 由独立 MCP Server 暴露
复用性 较低,易与业务绑定 较高,可被多个 AI 应用复用
跨模型适配 需按厂商规范分别适配 协议标准化,适配负担较低
工具发现 多为静态配置 支持协议化能力发现
资源与提示词 通常不覆盖 支持 Resources、Prompts
运维复杂度 相对简单 需要维护 Server、连接和权限
适用场景 单应用快速集成 多应用、多工具、生态化集成

4.6 MCP 使用注意事项

MCP 并不自动保证工具可靠性和安全性。实际使用中仍需关注:

  • 工具参数校验和返回结果校验;
  • 最小权限原则;
  • 认证、授权与租户隔离;
  • 沙箱执行、超时、重试和幂等;
  • 审计日志与调用链追踪;
  • 提示注入、越权访问和数据泄露风险;
  • MCP Server 的版本管理和可用性运维。

五、安全、评估与可观测性

5.1 安全

  • 工具调用前进行参数校验,不信任模型生成的参数。
  • 遵循最小权限原则,避免工具拥有过大权限。
  • 高风险工具放入沙箱执行,并设置超时、重试和幂等机制。
  • 记录工具调用审计日志,包含调用方、参数摘要、结果摘要和耗时。
  • 防范提示注入,不把不可信内容直接当作高优先级指令。
  • API Key 使用环境变量或密钥管理服务,不要写入代码仓库。

5.2 评估指标

建议至少关注:

  • 任务成功率;
  • 工具调用准确率;
  • 参数正确率;
  • 平均调用轮次;
  • 单次任务成本;
  • 端到端延迟;
  • 人工接管率;
  • 失败原因分布。

5.3 可观测性

建议记录:

  • 模型输入输出摘要;
  • 工具调用名称、参数和结果;
  • 每轮耗时和 token 消耗;
  • 错误码和重试次数;
  • 用户反馈与人工修正记录。

六、常见问题

Q1:什么时候应该用智能体?

当任务需要多步决策、动态工具调用或环境交互,且提示词工程、RAG、微调等方式无法满足预期时,再评估智能体。不要默认使用智能体。

Q2:Function Calling 和 MCP 是什么关系?

Function Calling 是模型调用工具的能力机制;MCP 是规范 AI 应用与外部工具、数据源连接方式的标准化协议。MCP 可以理解为对工具调用能力的进一步封装和生态化拓展,而不是简单替代。

Q3:工具调用结果一定要回传给模型吗?

通常需要。模型需要根据工具结果组织最终回答。也可以由业务代码直接消费工具结果,但是否回传取决于产品流程。

Q4:格式要求很高时应该用 Agent 吗?

优先使用程序化校验、约束解码、模板等确定性手段。Agent 可以作为兜底重写机制,但不是首选。

相关推荐
皮皮林5519 分钟前
为什么数据库连接池不采用 IO 多路复用?
后端
IT_陈寒4 小时前
Redis雪崩把我坑惨了,这是5年经验的教训
前端·人工智能·后端
量化分析码农4 小时前
【Python量化多因子实战 06】从因子到持仓:多因子选股实盘工程化落地
后端
贾公子4 小时前
检测链路的容量规划——用 Little's Law 算清 QPS、并发与积压
后端·python
kyson_4 小时前
Java 集合框架:从继承体系到数据结构,再到开发选型
后端
想不到一个好的ID5 小时前
什么是RAG
后端
金銀銅鐵5 小时前
JDK 21 中 List.of 的实现细节:从 List12 到 ListN
java·后端·ai编程
苏三说技术5 小时前
Codex中必装的11个Skills
后端
付威20236 小时前
target 目录越来越大,Rust 项目该怎么清理
后端