软件接入大模型实现 Agent —— 从原理到 C++ 落地完全指南

软件接入大模型实现 Agent ------ 从原理到 C++ 落地完全指南

一、概述:从"对话"到"行动"

1.1 什么是 AI Agent?

传统的大模型应用以"对话"为核心------用户提问,模型回答。而 AI Agent(智能体) 在此基础上更进一步:它不仅能"理解"和"回答",还能"规划"和"执行"。

传统 Chatbot AI Agent
能力边界 生成文本回复 理解意图 → 规划任务 → 调用工具 → 迭代优化
与外部交互 可读写文件、查数据库、执行系统命令
自主性 被动响应 主动规划并执行复杂任务

一个典型的 Agent 场景是:"帮我查一下今天的天气,然后发邮件给团队。"Agent 不仅要理解这句话,还要拆解步骤、调用工具、检查结果、迭代修正。

1.2 为什么用 C++ 构建 Agent?

在 AI 工具链中,Python 因其丰富的库占据主导地位。但用 C++ 构建 Agent 有几个不可替代的优势:

  1. 性能与资源效率:C++ 的静态类型、编译时优化和对硬件资源的直接掌控,可以显著降低延迟、减少内存分配次数,避免 Python GIL 问题。
  2. 系统级集成:许多核心基础设施本身就是 C/C++ 编写的,C++ MCP 服务器可以直接链接这些原生库。
  3. 部署优势:C++ 可编译成独立静态可执行文件,不依赖 Python 解释器或 Node.js 运行时。

1.3 核心架构总览

一个完整的 Agent 系统采用以下四层架构:
#mermaid-svg-y21JjUTE0RHlkBi0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y21JjUTE0RHlkBi0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y21JjUTE0RHlkBi0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y21JjUTE0RHlkBi0 .error-icon{fill:#552222;}#mermaid-svg-y21JjUTE0RHlkBi0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y21JjUTE0RHlkBi0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y21JjUTE0RHlkBi0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y21JjUTE0RHlkBi0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y21JjUTE0RHlkBi0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y21JjUTE0RHlkBi0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y21JjUTE0RHlkBi0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y21JjUTE0RHlkBi0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y21JjUTE0RHlkBi0 .marker.cross{stroke:#333333;}#mermaid-svg-y21JjUTE0RHlkBi0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y21JjUTE0RHlkBi0 p{margin:0;}#mermaid-svg-y21JjUTE0RHlkBi0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-y21JjUTE0RHlkBi0 .cluster-label text{fill:#333;}#mermaid-svg-y21JjUTE0RHlkBi0 .cluster-label span{color:#333;}#mermaid-svg-y21JjUTE0RHlkBi0 .cluster-label span p{background-color:transparent;}#mermaid-svg-y21JjUTE0RHlkBi0 .label text,#mermaid-svg-y21JjUTE0RHlkBi0 span{fill:#333;color:#333;}#mermaid-svg-y21JjUTE0RHlkBi0 .node rect,#mermaid-svg-y21JjUTE0RHlkBi0 .node circle,#mermaid-svg-y21JjUTE0RHlkBi0 .node ellipse,#mermaid-svg-y21JjUTE0RHlkBi0 .node polygon,#mermaid-svg-y21JjUTE0RHlkBi0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y21JjUTE0RHlkBi0 .rough-node .label text,#mermaid-svg-y21JjUTE0RHlkBi0 .node .label text,#mermaid-svg-y21JjUTE0RHlkBi0 .image-shape .label,#mermaid-svg-y21JjUTE0RHlkBi0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-y21JjUTE0RHlkBi0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y21JjUTE0RHlkBi0 .rough-node .label,#mermaid-svg-y21JjUTE0RHlkBi0 .node .label,#mermaid-svg-y21JjUTE0RHlkBi0 .image-shape .label,#mermaid-svg-y21JjUTE0RHlkBi0 .icon-shape .label{text-align:center;}#mermaid-svg-y21JjUTE0RHlkBi0 .node.clickable{cursor:pointer;}#mermaid-svg-y21JjUTE0RHlkBi0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y21JjUTE0RHlkBi0 .arrowheadPath{fill:#333333;}#mermaid-svg-y21JjUTE0RHlkBi0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y21JjUTE0RHlkBi0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y21JjUTE0RHlkBi0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y21JjUTE0RHlkBi0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y21JjUTE0RHlkBi0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y21JjUTE0RHlkBi0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y21JjUTE0RHlkBi0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y21JjUTE0RHlkBi0 .cluster text{fill:#333;}#mermaid-svg-y21JjUTE0RHlkBi0 .cluster span{color:#333;}#mermaid-svg-y21JjUTE0RHlkBi0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-y21JjUTE0RHlkBi0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y21JjUTE0RHlkBi0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-y21JjUTE0RHlkBi0 .icon-shape,#mermaid-svg-y21JjUTE0RHlkBi0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y21JjUTE0RHlkBi0 .icon-shape p,#mermaid-svg-y21JjUTE0RHlkBi0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y21JjUTE0RHlkBi0 .icon-shape .label rect,#mermaid-svg-y21JjUTE0RHlkBi0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y21JjUTE0RHlkBi0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y21JjUTE0RHlkBi0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y21JjUTE0RHlkBi0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户交互层
执行结果
回传
最终回复
工具层 - MCP Servers
文件系统
数据库
命令行
第三方API
自定义工具...
协议适配层 - MCP Client
工具发现
调用路由
结果回传
Agent 核心层 - 大脑
规划器

Planner
执行器

Executor
记忆管理

Memory
上下文蒸馏

Distillation
字段归一化

Normalizer
Web / CLI / 桌面应用

1.4 核心数据流

MCP Server MCP Client DeepSeek API Agent 核心 用户 MCP Server MCP Client DeepSeek API Agent 核心 用户 #mermaid-svg-oP7zK9pL7fe7mKVr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oP7zK9pL7fe7mKVr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oP7zK9pL7fe7mKVr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oP7zK9pL7fe7mKVr .error-icon{fill:#552222;}#mermaid-svg-oP7zK9pL7fe7mKVr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oP7zK9pL7fe7mKVr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oP7zK9pL7fe7mKVr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oP7zK9pL7fe7mKVr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oP7zK9pL7fe7mKVr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oP7zK9pL7fe7mKVr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oP7zK9pL7fe7mKVr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oP7zK9pL7fe7mKVr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oP7zK9pL7fe7mKVr .marker.cross{stroke:#333333;}#mermaid-svg-oP7zK9pL7fe7mKVr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oP7zK9pL7fe7mKVr p{margin:0;}#mermaid-svg-oP7zK9pL7fe7mKVr .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-oP7zK9pL7fe7mKVr text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-oP7zK9pL7fe7mKVr .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-oP7zK9pL7fe7mKVr .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-oP7zK9pL7fe7mKVr .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-oP7zK9pL7fe7mKVr .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-oP7zK9pL7fe7mKVr #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-oP7zK9pL7fe7mKVr .sequenceNumber{fill:white;}#mermaid-svg-oP7zK9pL7fe7mKVr #sequencenumber{fill:#333;}#mermaid-svg-oP7zK9pL7fe7mKVr #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-oP7zK9pL7fe7mKVr .messageText{fill:#333;stroke:none;}#mermaid-svg-oP7zK9pL7fe7mKVr .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-oP7zK9pL7fe7mKVr .labelText,#mermaid-svg-oP7zK9pL7fe7mKVr .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-oP7zK9pL7fe7mKVr .loopText,#mermaid-svg-oP7zK9pL7fe7mKVr .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-oP7zK9pL7fe7mKVr .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-oP7zK9pL7fe7mKVr .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-oP7zK9pL7fe7mKVr .noteText,#mermaid-svg-oP7zK9pL7fe7mKVr .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-oP7zK9pL7fe7mKVr .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-oP7zK9pL7fe7mKVr .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-oP7zK9pL7fe7mKVr .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-oP7zK9pL7fe7mKVr .actorPopupMenu{position:absolute;}#mermaid-svg-oP7zK9pL7fe7mKVr .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-oP7zK9pL7fe7mKVr .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-oP7zK9pL7fe7mKVr .actor-man circle,#mermaid-svg-oP7zK9pL7fe7mKVr line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-oP7zK9pL7fe7mKVr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt 有工具调用 1. 用户输入 2. 加载记忆 + 蒸馏摘要 3. 提示词 + 工具Schema 4. 返回 tool_calls (JSON) 5. 字段归一化 (path→filePath等别名处理) 6. 调用工具 7. JSON-RPC 请求 8. 执行结果 9. 结果回传 10. 回传执行结果 11. 最终自然语言回复 12. 蒸馏并存储记忆 13. 返回最终回复

二、MCP 协议:Agent 的"神经系统"

2.1 什么是 MCP?

MCP(Model Context Protocol) 是由 Anthropic 提出的开放协议,旨在标准化大模型与外部工具、数据源之间的通信方式。它采用客户端-服务器架构,基于 JSON-RPC 2.0 进行通信。

核心概念:

概念 说明
Client(客户端) 集成 LLM 的应用,负责与用户交互、调用模型
Server(服务器) 独立进程,提供具体功能(工具/资源/提示模板)
Tools(工具) 可执行的函数(如 read_file, execute_shell
Resources(资源) 可读取的数据源
Prompts(提示模板) 预定义的提示词片段

2.2 MCP 通信流程

MCP 协议的核心握手与调用流程如下:
MCP Server (Tool Provider) MCP Client (Agent) MCP Server (Tool Provider) MCP Client (Agent) #mermaid-svg-lSw4YsxMHyGUKU6G{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lSw4YsxMHyGUKU6G .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lSw4YsxMHyGUKU6G .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lSw4YsxMHyGUKU6G .error-icon{fill:#552222;}#mermaid-svg-lSw4YsxMHyGUKU6G .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lSw4YsxMHyGUKU6G .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lSw4YsxMHyGUKU6G .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lSw4YsxMHyGUKU6G .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lSw4YsxMHyGUKU6G .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lSw4YsxMHyGUKU6G .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lSw4YsxMHyGUKU6G .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lSw4YsxMHyGUKU6G .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lSw4YsxMHyGUKU6G .marker.cross{stroke:#333333;}#mermaid-svg-lSw4YsxMHyGUKU6G svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lSw4YsxMHyGUKU6G p{margin:0;}#mermaid-svg-lSw4YsxMHyGUKU6G .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-lSw4YsxMHyGUKU6G text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-lSw4YsxMHyGUKU6G .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-lSw4YsxMHyGUKU6G .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-lSw4YsxMHyGUKU6G .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-lSw4YsxMHyGUKU6G .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-lSw4YsxMHyGUKU6G #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-lSw4YsxMHyGUKU6G .sequenceNumber{fill:white;}#mermaid-svg-lSw4YsxMHyGUKU6G #sequencenumber{fill:#333;}#mermaid-svg-lSw4YsxMHyGUKU6G #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-lSw4YsxMHyGUKU6G .messageText{fill:#333;stroke:none;}#mermaid-svg-lSw4YsxMHyGUKU6G .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-lSw4YsxMHyGUKU6G .labelText,#mermaid-svg-lSw4YsxMHyGUKU6G .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-lSw4YsxMHyGUKU6G .loopText,#mermaid-svg-lSw4YsxMHyGUKU6G .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-lSw4YsxMHyGUKU6G .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-lSw4YsxMHyGUKU6G .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-lSw4YsxMHyGUKU6G .noteText,#mermaid-svg-lSw4YsxMHyGUKU6G .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-lSw4YsxMHyGUKU6G .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-lSw4YsxMHyGUKU6G .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-lSw4YsxMHyGUKU6G .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-lSw4YsxMHyGUKU6G .actorPopupMenu{position:absolute;}#mermaid-svg-lSw4YsxMHyGUKU6G .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-lSw4YsxMHyGUKU6G .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-lSw4YsxMHyGUKU6G .actor-man circle,#mermaid-svg-lSw4YsxMHyGUKU6G line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-lSw4YsxMHyGUKU6G :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 初始化握手 2. 工具发现 3. 工具调用 4. 生命周期管理 initialize (协议版本、能力) 协议版本、服务器信息 notifications/initialized tools/list 工具列表 + JSON Schema tools/call (工具名 + 参数) 执行结果 关闭/取消

关键消息格式

初始化请求(Client → Server):

json 复制代码
{
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": {},
    "clientInfo": {"name": "my-agent", "version": "1.0.0"}
  },
  "jsonrpc": "2.0",
  "id": 0
}

工具列表响应(Server → Client):

json 复制代码
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [{
      "name": "read_file",
      "description": "读取指定路径的文件内容",
      "inputSchema": {
        "type": "object",
        "properties": {
          "path": {"type": "string", "description": "文件路径"}
        },
        "required": ["path"]
      }
    }]
  }
}

工具调用请求(Client → Server):

json 复制代码
{
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {"path": "/etc/hosts"}
  },
  "jsonrpc": "2.0",
  "id": 4
}

2.3 MCP 传输协议

MCP 支持多种传输方式:

传输方式 说明 适用场景
stdio 通过标准输入/输出通信,JSON-RPC 消息以换行分隔 本地进程,最常用
Streamable HTTP 基于 HTTP 的流式通信,支持会话状态 远程服务、Web 部署
WebSocket 全双工通信,支持自动重连 需要双向实时通信的场景
SSE(Server-Sent Events) 服务器向客户端推送事件 流式响应推送

重要提醒:MCP Server 只能向 stdout 输出合法的 JSON 消息(每行一个 JSON),任何其他输出(调试日志等)都会破坏与客户端的通信。

三、C++ MCP SDK 生态

目前已有多个生产就绪的 C++ MCP SDK 可供选择:

SDK 特点 标准
cxxmcp 生产就绪 C++17 SDK,支持 stdio/HTTP/WebSocket,协议覆盖 99%(Server)/100%(Client) C++17
gopher-mcp 最全面的 C++ 实现,支持多传输、连接池、多语言绑定(Python/TS/Go/Rust/Java/C#) C++
TinyMCP 轻量级 C++ MCP SDK,由 360 开源 C++
TSAR-MCP IBM 开源,零依赖 C/C++ SDK,MIT 协议 C/C++
mcp-cpp 轻量级 C++ MCP SDK,GitHub 291 Star C++
oatpp-mcp 基于 Oat++ 框架的 MCP 实现 C++

3.1 cxxmcp 快速示例

MCP Server(服务端)

cpp 复制代码
#include <cxxmcp/peer.hpp>
#include <cxxmcp/run.hpp>

int main() {
    return mcp::ServerPeer::builder()
        .name("demo-server")
        .version("1.0.0")
        .stdio()  // 使用 stdio 传输
        .tool<mcp::protocol::Json, mcp::protocol::Json>(
            "echo",
            [](const mcp::protocol::Json& input) {
                return mcp::protocol::Json{{"echo", input}};
            }
        )
        .run();
}

MCP Client(客户端)

cpp 复制代码
#include <cxxmcp/peer.hpp>
#include <cxxmcp/run.hpp>

int main() {
    int status = 0;
    const auto run_status = mcp::ClientPeer::builder()
        .streamable_http("http://127.0.0.1:3000/mcp")
        .run([&status](auto& svc) {
            if (!svc.peer().initialize().has_value() ||
                !svc.peer().notify_initialized().has_value() ||
                !svc.peer().list_all_tools().has_value() ||
                !svc.peer().call_tool("echo", 
                    mcp::protocol::Json{{"value", "hello"}}).has_value()) {
                status = 1;
            }
        });
    return run_status == 0 ? status : run_status;
}

3.2 从零实现简易 MCP Server

如果不想依赖 SDK,也可以从零实现:

cpp 复制代码
#include <iostream>
#include <nlohmann/json.hpp>
#include <string>

using json = nlohmann::json;

// 解析 stdin 中的 JSON-RPC 消息
json readMessage() {
    std::string line;
    std::getline(std::cin, line);
    return json::parse(line);
}

// 发送 JSON-RPC 响应到 stdout
void sendMessage(const json& msg) {
    std::cout << msg.dump() << std::endl;
}

int main() {
    while (true) {
        auto req = readMessage();
        std::string method = req["method"];

        if (method == "initialize") {
            sendMessage({
                {"jsonrpc", "2.0"},
                {"id", req["id"]},
                {"result", {
                    {"protocolVersion", "2024-11-05"},
                    {"capabilities", {{"tools", json::object()}}},
                    {"serverInfo", {{"name", "MyServer"}, {"version", "1.0.0"}}}
                }}
            });
        }
        else if (method == "tools/list") {
            sendMessage({
                {"jsonrpc", "2.0"},
                {"id", req["id"]},
                {"result", {
                    {"tools", {{
                        {"name", "HelloTool"},
                        {"description", "A greeting tool"},
                        {"inputSchema", {
                            {"type", "object"},
                            {"properties", {
                                {"value", {{"type", "string"}}}
                            }},
                            {"required", {"value"}}
                        }}
                    }}}
                }}
            });
        }
        else if (method == "tools/call") {
            std::string name = req["params"]["name"];
            auto args = req["params"]["arguments"];
            if (name == "HelloTool") {
                std::string user = args["value"];
                sendMessage({
                    {"jsonrpc", "2.0"},
                    {"id", req["id"]},
                    {"result", {
                        {"content", {{
                            {"type", "text"},
                            {"text", "Hello, " + user + "!"}
                        }}}
                    }}
                });
            }
        }
    }
    return 0;
}

四、DeepSeek 大模型接入(C++)

4.1 DeepSeek API 基础

DeepSeek API 兼容 OpenAI 接口格式,Base URL 为 https://api.deepseek.com

核心端点POST /v1/chat/completions

请求体结构

json 复制代码
{
  "model": "deepseek-chat",
  "messages": [
    {"role": "system", "content": "你是一个智能助手"},
    {"role": "user", "content": "用户的问题"}
  ],
  "tools": [...]  // 可选,工具定义
}

4.2 C++ HTTP 客户端实现

使用 cpp-httplibnlohmann/json 实现 DeepSeek API 调用:

cpp 复制代码
#include <httplib.h>
#include <nlohmann/json.hpp>
#include <iostream>
#include <vector>
#include <string>

using json = nlohmann::json;

class DeepSeekClient {
private:
    std::string apiKey;
    std::string baseUrl;
    httplib::Client client;

public:
    DeepSeekClient(const std::string& key) 
        : apiKey(key), baseUrl("https://api.deepseek.com"), 
          client(baseUrl) {}

    // 发送聊天请求(非流式)
    json chat(const json& messages, const json& tools = json::array()) {
        json requestBody;
        requestBody["model"] = "deepseek-chat";
        requestBody["messages"] = messages;
        if (!tools.empty()) {
            requestBody["tools"] = tools;
        }
        requestBody["temperature"] = 0.7;

        auto response = client.Post(
            "/v1/chat/completions",
            requestBody.dump(),
            "application/json"
        );

        if (response && response->status == 200) {
            return json::parse(response->body);
        } else {
            throw std::runtime_error("API request failed");
        }
    }
};

4.3 使用现成的 DeepSeek C++ 库

社区已有封装好的 C++ 库:

cpp 复制代码
#include <DeepSeekAPI.h>

using namespace inx::DeepSeek;

API api("your_api_key_here", Model::DeepSeekChat, 
        "You are a helpful assistant.");
api.AddMessage("What's 2 + 2?");
std::string response = api.GetCompletion();

4.4 Function Calling(工具调用)

DeepSeek 支持 OpenAI 风格的 Function Calling。工具定义作为 tools 参数传入:

cpp 复制代码
json tools = json::array({
    {
        {"type", "function"},
        {"function", {
            {"name", "get_weather"},
            {"description", "查询指定城市的天气"},
            {"parameters", {
                {"type", "object"},
                {"properties", {
                    {"city", {{"type", "string"}, {"description", "城市名称"}}}
                }},
                {"required", {"city"}}
            }}
        }}
    }
});

auto response = client.chat(messages, tools);

// 解析 tool_calls
if (response["choices"][0]["message"].contains("tool_calls")) {
    auto toolCalls = response["choices"][0]["message"]["tool_calls"];
    for (auto& tc : toolCalls) {
        std::string name = tc["function"]["name"];
        json args = json::parse(tc["function"]["arguments"].get<std::string>());
        // 执行对应的工具...
    }
}

注意 :DeepSeek-R1 目前不支持 原生 Function Calling,如需工具调用请使用 deepseek-chatdeepseek-v4-pro

五、JSON 响应解析:防御性编程与字段归一化

这是 Agent 开发中最容易被忽视但又最关键的一环。DeepSeek API 返回的 JSON 结构在不同场景下会发生变化,同时大模型或不同 MCP Server 返回的字段名也可能存在"语义漂移"(如第一次返回 path,第二次返回 filePath,本质含义相同)。你的解析器必须具备防御性归一化能力。

5.1 四大"陷阱"场景

场景 字段变化 后果
1. 普通对话 vs 工具调用 工具调用时 message 里会多出 tool_calls,且 content 可能为 null 直接取 content 会抛类型异常
2. 流式响应 vs 非流式 非流式用 message,流式用 delta 取错字段拿不到内容
3. API 报错 没有 choices,只有 error 对象 choices[0] 越界崩溃
4. 字段名语义漂移 同一含义的字段在不同请求中返回不同名称(path vs filePath 业务逻辑取不到数据

5.2 防御性解析器

不要直接索引,必须使用 contains() + value() + 类型检查。

cpp 复制代码
#include <nlohmann/json.hpp>
#include <iostream>
#include <optional>

using json = nlohmann::json;

struct SafeChatResponse {
    bool success = false;
    std::string error_message;
    std::string content;          // 最终展示的文本
    json tool_calls;              // 工具调用列表(可能为空)
    std::string reasoning;        // R1 推理内容(可选)
};

class ResponseParser {
public:
    static SafeChatResponse parse(const json& raw) {
        SafeChatResponse result;

        // ========== 陷阱 1: 检查是否有错误 ==========
        if (raw.contains("error")) {
            result.success = false;
            result.error_message = raw["error"].value("message", "Unknown API error");
            return result;
        }

        // ========== 陷阱 2: 检查 choices 是否存在且非空 ==========
        if (!raw.contains("choices") || raw["choices"].empty()) {
            result.success = false;
            result.error_message = "No choices in response";
            return result;
        }

        auto& choice = raw["choices"][0];

        // ========== 陷阱 3: 区分流式(Delta) 和 非流式(Message) ==========
        json message_obj;
        bool is_streaming = choice.contains("delta");
        
        if (is_streaming) {
            message_obj = choice["delta"];
        } else if (choice.contains("message")) {
            message_obj = choice["message"];
        } else {
            result.success = false;
            result.error_message = "No message or delta field found";
            return result;
        }

        // ========== 陷阱 4: 处理 DeepSeek-R1 的推理内容 ==========
        if (message_obj.contains("reasoning_content")) {
            result.reasoning = message_obj["reasoning_content"].get<std::string>();
        }

        // ========== 陷阱 5: 安全提取 content(处理 null 值) ==========
        // 重点:工具调用时 content 可能是 null,直接用 get<string>() 会抛异常!
        if (message_obj.contains("content") && !message_obj["content"].is_null()) {
            if (message_obj["content"].is_string()) {
                result.content = message_obj["content"].get<std::string>();
            } else {
                result.content = message_obj["content"].dump();
            }
        } else {
            result.content = "";
        }

        // ========== 陷阱 6: 安全提取 tool_calls ==========
        if (message_obj.contains("tool_calls") && !message_obj["tool_calls"].is_null()) {
            result.tool_calls = message_obj["tool_calls"];
        } else {
            result.tool_calls = json::array();
        }

        result.success = true;
        return result;
    }
};

5.3 字段归一化处理器

针对"字段名语义漂移"问题(如 path vs filePath),需要建立一个别名映射表(Alias Map),将所有可能的变体统一映射成标准字段。

cpp 复制代码
class JsonNormalizer {
private:
    // 定义别名映射:将外部混乱的字段名 -> 标准内部字段名
    const std::unordered_map<std::string, std::vector<std::string>> alias_map = {
        {"path", {"path", "filePath", "file_path", "FilePath", "filename", "location"}},
        {"content", {"content", "text", "data", "body", "message"}},
        {"status", {"status", "code", "state", "result_code"}},
        {"city", {"city", "cityName", "city_name", "location"}},
        {"temperature", {"temperature", "temp", "degrees", "celsius"}}
    };

public:
    // 归一化主函数:将 JSON 中的别名都替换为标准键
    json normalize(const json& input) {
        if (!input.is_object()) return input;

        json output = input;

        for (const auto& [standard_key, aliases] : alias_map) {
            for (const auto& alias : aliases) {
                if (output.contains(alias) && !output[alias].is_null()) {
                    // 如果标准键不存在或为空,进行覆盖
                    if (!output.contains(standard_key) || output[standard_key].is_null()) {
                        output[standard_key] = output[alias];
                    }
                    // 可选:删除旧别名键,保持数据干净
                    // output.erase(alias);
                }
            }
        }
        return output;
    }

    // 安全提取工具参数,自动归一化后取值
    std::optional<std::string> extractString(const json& args, const std::string& standard_key) {
        json normalized = normalize(args);
        if (normalized.contains(standard_key) && !normalized[standard_key].is_null()) {
            if (normalized[standard_key].is_string()) {
                return normalized[standard_key].get<std::string>();
            }
        }
        return std::nullopt;
    }
};

使用示例

cpp 复制代码
// 假设 DeepSeek 或 MCP Server 返回了两种不同格式的 JSON
// 第一次:{"path": "/etc/hosts"}
// 第二次:{"filePath": "/var/log/syslog"}

JsonNormalizer normalizer;
json raw_args = /* 从 tool_calls 中解析出的 arguments */;

// 无论 key 是 path 还是 filePath,统统归一化为 "path"
json normalized_args = normalizer.normalize(raw_args);

// 现在可以安全使用 "path"
std::string file_path = normalized_args["path"].get<std::string>();
// 或使用安全提取函数
auto path_opt = normalizer.extractString(raw_args, "path");
if (path_opt.has_value()) {
    std::string path = path_opt.value();
}

5.4 三种治理方案对比

方案 做法 适用场景 优点 缺点
方案一:源头治理 在 Prompt/Schema 中强制限定字段名 自己调 DeepSeek + 自己调本地函数 成本最低,效果最好 无法控制第三方
方案二:字段归一化 建立别名映射表,统一转换 对接各种第三方 MCP Server 兼容性强,对旧数据友好 需维护映射表
方案三:动态 Schema 读取 从 MCP 注册信息中动态读取字段名 大量第三方工具接入 完全解耦,无需预设 实现复杂度较高

5.5 推荐的综合策略

在 Agent 的 executeTool 函数前,插入归一化中间件:

cpp 复制代码
json executeTool(const std::string& name, const json& raw_args) {
    // 1. 归一化:统一字段名
    JsonNormalizer normalizer;
    json args = normalizer.normalize(raw_args);
    
    // 2. 根据工具名路由执行
    if (name == "read_file") {
        // 现在可以安全使用 "path"
        std::string path = args["path"].get<std::string>();
        return readFile(path);
    }
    // ... 其他工具
}

六、Agent 核心循环

6.1 Agent 执行流程

Agent 的核心是一个"思考-行动-观察"的循环:
#mermaid-svg-Tz4Z9QU3soybcN9w{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Tz4Z9QU3soybcN9w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Tz4Z9QU3soybcN9w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Tz4Z9QU3soybcN9w .error-icon{fill:#552222;}#mermaid-svg-Tz4Z9QU3soybcN9w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Tz4Z9QU3soybcN9w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Tz4Z9QU3soybcN9w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Tz4Z9QU3soybcN9w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Tz4Z9QU3soybcN9w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Tz4Z9QU3soybcN9w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Tz4Z9QU3soybcN9w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Tz4Z9QU3soybcN9w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Tz4Z9QU3soybcN9w .marker.cross{stroke:#333333;}#mermaid-svg-Tz4Z9QU3soybcN9w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Tz4Z9QU3soybcN9w p{margin:0;}#mermaid-svg-Tz4Z9QU3soybcN9w .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Tz4Z9QU3soybcN9w .cluster-label text{fill:#333;}#mermaid-svg-Tz4Z9QU3soybcN9w .cluster-label span{color:#333;}#mermaid-svg-Tz4Z9QU3soybcN9w .cluster-label span p{background-color:transparent;}#mermaid-svg-Tz4Z9QU3soybcN9w .label text,#mermaid-svg-Tz4Z9QU3soybcN9w span{fill:#333;color:#333;}#mermaid-svg-Tz4Z9QU3soybcN9w .node rect,#mermaid-svg-Tz4Z9QU3soybcN9w .node circle,#mermaid-svg-Tz4Z9QU3soybcN9w .node ellipse,#mermaid-svg-Tz4Z9QU3soybcN9w .node polygon,#mermaid-svg-Tz4Z9QU3soybcN9w .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Tz4Z9QU3soybcN9w .rough-node .label text,#mermaid-svg-Tz4Z9QU3soybcN9w .node .label text,#mermaid-svg-Tz4Z9QU3soybcN9w .image-shape .label,#mermaid-svg-Tz4Z9QU3soybcN9w .icon-shape .label{text-anchor:middle;}#mermaid-svg-Tz4Z9QU3soybcN9w .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Tz4Z9QU3soybcN9w .rough-node .label,#mermaid-svg-Tz4Z9QU3soybcN9w .node .label,#mermaid-svg-Tz4Z9QU3soybcN9w .image-shape .label,#mermaid-svg-Tz4Z9QU3soybcN9w .icon-shape .label{text-align:center;}#mermaid-svg-Tz4Z9QU3soybcN9w .node.clickable{cursor:pointer;}#mermaid-svg-Tz4Z9QU3soybcN9w .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Tz4Z9QU3soybcN9w .arrowheadPath{fill:#333333;}#mermaid-svg-Tz4Z9QU3soybcN9w .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Tz4Z9QU3soybcN9w .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Tz4Z9QU3soybcN9w .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tz4Z9QU3soybcN9w .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Tz4Z9QU3soybcN9w .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tz4Z9QU3soybcN9w .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Tz4Z9QU3soybcN9w .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Tz4Z9QU3soybcN9w .cluster text{fill:#333;}#mermaid-svg-Tz4Z9QU3soybcN9w .cluster span{color:#333;}#mermaid-svg-Tz4Z9QU3soybcN9w div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Tz4Z9QU3soybcN9w .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Tz4Z9QU3soybcN9w rect.text{fill:none;stroke-width:0;}#mermaid-svg-Tz4Z9QU3soybcN9w .icon-shape,#mermaid-svg-Tz4Z9QU3soybcN9w .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tz4Z9QU3soybcN9w .icon-shape p,#mermaid-svg-Tz4Z9QU3soybcN9w .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Tz4Z9QU3soybcN9w .icon-shape .label rect,#mermaid-svg-Tz4Z9QU3soybcN9w .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tz4Z9QU3soybcN9w .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Tz4Z9QU3soybcN9w .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Tz4Z9QU3soybcN9w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

用户输入
构建提示词 + 工具列表
调用 DeepSeek 推理
防御性解析响应
有 tool_calls?
直接返回文本回复
返回用户
字段归一化处理
路由到 MCP Server
MCP Server 执行工具
收集执行结果
将结果作为 tool 消息回传
再次调用 DeepSeek 润色

6.2 完整的 C++ Agent 实现

cpp 复制代码
#include <httplib.h>
#include <nlohmann/json.hpp>
#include <iostream>
#include <vector>
#include <memory>
#include <cstdlib>
#include <string>
#include <unordered_map>
#include <optional>

using json = nlohmann::json;

// ==================== 1. 防御性解析器 ====================
// (见第五章完整实现)

// ==================== 2. 字段归一化器 ====================
// (见第五章完整实现)

// ==================== 3. DeepSeek 客户端 ====================
class DeepSeekClient {
public:
    DeepSeekClient(const std::string& key) : apiKey(key) {
        client = std::make_unique<httplib::Client>("https://api.deepseek.com");
    }

    json chat(const json& messages, const json& tools = json::array()) {
        json req;
        req["model"] = "deepseek-chat";
        req["messages"] = messages;
        if (!tools.empty()) req["tools"] = tools;
        req["temperature"] = 0.7;

        auto res = client->Post("/v1/chat/completions", req.dump(), "application/json");
        if (res && res->status == 200) {
            return json::parse(res->body);
        }
        throw std::runtime_error("API error");
    }

private:
    std::string apiKey;
    std::unique_ptr<httplib::Client> client;
};

// ==================== 4. MCP 客户端(stdio 通信)====================
class MCPClient {
public:
    MCPClient(const std::string& cmd) : command(cmd) {}

    void start() {
        process = popen(command.c_str(), "r+");
        if (!process) throw std::runtime_error("Failed to start MCP server");
    }

    json sendRequest(const json& req) {
        std::string reqStr = req.dump() + "\n";
        fwrite(reqStr.c_str(), 1, reqStr.size(), process);
        fflush(process);

        char buffer[4096];
        if (fgets(buffer, sizeof(buffer), process)) {
            return json::parse(buffer);
        }
        throw std::runtime_error("No response from MCP server");
    }

    void initialize() {
        json req = {
            {"jsonrpc", "2.0"},
            {"method", "initialize"},
            {"params", {
                {"protocolVersion", "2025-06-18"},
                {"capabilities", json::object()},
                {"clientInfo", {{"name", "cpp-agent"}, {"version", "1.0.0"}}}
            }},
            {"id", 0}
        };
        sendRequest(req);
        
        json notify = {
            {"jsonrpc", "2.0"},
            {"method", "notifications/initialized"}
        };
        sendRequest(notify);
    }

    json listTools() {
        json req = {
            {"jsonrpc", "2.0"},
            {"method", "tools/list"},
            {"params", json::object()},
            {"id", 1}
        };
        return sendRequest(req);
    }

    json callTool(const std::string& name, const json& args) {
        static int id = 2;
        json req = {
            {"jsonrpc", "2.0"},
            {"method", "tools/call"},
            {"params", {
                {"name", name},
                {"arguments", args}
            }},
            {"id", id++}
        };
        return sendRequest(req);
    }

    ~MCPClient() {
        if (process) pclose(process);
    }

private:
    std::string command;
    FILE* process = nullptr;
};

// ==================== 5. Agent 主循环 ====================
class Agent {
public:
    Agent(const std::string& apiKey, const std::string& mcpCmd)
        : llm(apiKey), mcp(mcpCmd), normalizer() {}

    void initialize() {
        mcp.start();
        mcp.initialize();
        
        auto result = mcp.listTools();
        if (result.contains("result") && result["result"].contains("tools")) {
            for (auto& tool : result["result"]["tools"]) {
                json toolDef = {
                    {"type", "function"},
                    {"function", {
                        {"name", tool["name"]},
                        {"description", tool.value("description", "")},
                        {"parameters", tool["inputSchema"]}
                    }}
                };
                tools.push_back(toolDef);
            }
        }
    }

    std::string run(const std::string& userInput) {
        messages.push_back({{"role", "user"}, {"content", userInput}});

        for (int iteration = 0; iteration < 5; ++iteration) {
            auto response = llm.chat(messages, tools);
            
            // ===== 防御性解析 =====
            auto parsed = ResponseParser::parse(response);
            if (!parsed.success) {
                return "Error: " + parsed.error_message;
            }

            // ===== 检查工具调用 =====
            if (parsed.tool_calls.empty()) {
                messages.push_back({{"role", "assistant"}, {"content", parsed.content}});
                return parsed.content;
            }

            // ===== 处理工具调用 =====
            messages.push_back({
                {"role", "assistant"},
                {"content", parsed.content},
                {"tool_calls", parsed.tool_calls}
            });

            for (auto& tc : parsed.tool_calls) {
                std::string toolName = tc["function"]["name"];
                json raw_args = json::parse(tc["function"]["arguments"].get<std::string>());

                // ===== 字段归一化(关键!) =====
                json args = normalizer.normalize(raw_args);

                // 执行工具
                auto result = mcp.callTool(toolName, args);
                
                std::string resultContent;
                if (result.contains("result") && result["result"].contains("content")) {
                    auto& content = result["result"]["content"];
                    if (content.is_array() && !content.empty()) {
                        resultContent = content[0].value("text", "");
                    }
                }

                messages.push_back({
                    {"role", "tool"},
                    {"tool_call_id", tc["id"]},
                    {"content", resultContent}
                });
            }
        }

        return "Agent reached maximum iteration limit";
    }

private:
    DeepSeekClient llm;
    MCPClient mcp;
    JsonNormalizer normalizer;
    json messages = json::array();
    json tools = json::array();
};

// ==================== 6. 使用示例 ====================
int main() {
    std::string apiKey = std::getenv("DEEPSEEK_API_KEY");
    if (apiKey.empty()) {
        std::cerr << "Please set DEEPSEEK_API_KEY environment variable" << std::endl;
        return 1;
    }

    Agent agent(apiKey, "./my_mcp_server");
    agent.initialize();

    std::cout << "Agent ready. Type 'quit' to exit." << std::endl;
    std::string input;
    while (true) {
        std::cout << "> ";
        std::getline(std::cin, input);
        if (input == "quit") break;

        try {
            std::string response = agent.run(input);
            std::cout << "Agent: " << response << std::endl;
        } catch (const std::exception& e) {
            std::cerr << "Error: " << e.what() << std::endl;
        }
    }

    return 0;
}

6.3 关键设计要点

  1. 消息历史管理messages 数组必须包含完整的对话轮次,DeepSeek 才能理解上下文。

  2. 工具调用回传 :MCP Server 执行完毕后,必须将结果作为 role: "tool" 的消息回传,让模型润色。

  3. 防御性解析 :使用 contains() + value() 代替直接索引,防止字段缺失或类型不匹配导致崩溃。

  4. 字段归一化 :在工具执行前统一处理别名(如 path vs filePath),确保业务逻辑始终使用标准字段名。

  5. 迭代限制:Agent 循环必须设置最大迭代次数(如 5-10 次),防止无限循环。

七、记忆管理与上下文蒸馏

7.1 三层记忆架构

大模型 API 是**无状态(Stateless)**的,每次调用都是一次全新的"脑部清零"。Agent 需要在软件层实现记忆管理:

层级 内容 存储位置 生命周期
短期记忆 当前会话的对话历史 内存(messages 数组) 单次会话
长期记忆 用户偏好、历史事实 向量数据库 / SQLite 跨会话持久
永久记忆 核心身份信息 加密存储 永久

7.2 C++ 向量数据库集成

数据库 特点
Endee C++ 向量数据库,专为 RAG 和 Agent 记忆设计,支持 HNSW 索引
SeekDB AI-native 向量+SQL 数据库,MySQL 兼容接口,ACID 保证
Zvec 阿里巴巴开源,轻量级嵌入式向量数据库

记忆存储示例

cpp 复制代码
struct Memory {
    std::string id;
    std::string content;
    std::vector<float> embedding;
    std::string summary;
    time_t timestamp;
};

class MemoryStore {
public:
    void store(const Memory& mem) {
        sqlite_insert(mem.id, mem.content, mem.summary, mem.timestamp);
        vector_index.insert(mem.id, mem.embedding);
    }

    std::vector<Memory> retrieve(const std::vector<float>& queryEmbedding, int topK) {
        auto ids = vector_index.search(queryEmbedding, topK);
        return sqlite_get_by_ids(ids);
    }
};

7.3 上下文蒸馏

上下文蒸馏(Context Distillation) 是用大模型"理解"长文本后,将核心含义压缩成精炼摘要的过程。

蒸馏 vs 压缩

压缩(Compression) 蒸馏(Distillation)
核心动作 删除、缩写、截断 阅读、理解、重写
执行者 算法/规则 大模型(LLM)
成本 极低 消耗 Token
典型产出 "中华人民共和国"→"中国" 5000 字对话 → 200 字摘要

蒸馏实现

cpp 复制代码
class ContextDistiller {
public:
    std::string distill(const json& messages, DeepSeekClient& llm) {
        std::string conversation = formatMessages(messages);
        std::string prompt = 
            "请将以下对话压缩为200字以内的摘要,"
            "保留关键事实、用户偏好和未完成任务:\n\n" + conversation;

        json msgs = json::array({
            {{"role", "system"}, {"content", "你是一个专业的对话摘要助手"}},
            {{"role", "user"}, {"content", prompt}}
        });

        auto response = llm.chat(msgs);
        return response["choices"][0]["message"]["content"];
    }

    bool needsDistillation(const json& messages, size_t limit = 8000) {
        return estimateTokens(messages) > limit;
    }

private:
    size_t estimateTokens(const json& messages) {
        size_t total = 0;
        for (auto& msg : messages) {
            total += msg["content"].get<std::string>().size() / 4;
        }
        return total;
    }

    std::string formatMessages(const json& messages) {
        std::string result;
        for (auto& msg : messages) {
            result += msg["role"].get<std::string>() + ": " + 
                      msg["content"].get<std::string>() + "\n";
        }
        return result;
    }
};

八、最佳实践与避坑指南

8.1 架构选型建议

场景 推荐方案 理由
个人项目/内部工具(<5 个工具) 原生 Function Calling 简单直接,延迟最低
企业内部系统(5-20 个工具) 自建轻量路由 + 可选 MCP 平衡复杂度与扩展性
通用平台/开源生态 MCP 全套 标准化插件体系
高性能/嵌入式场景 C++ MCP Server 零依赖、低延迟

8.2 关键注意事项

  1. R1 不支持 Function Call :DeepSeek-R1 目前不支持 原生 Function Calling,如需工具调用请使用 deepseek-chatdeepseek-v4-pro

  2. MCP Server 输出限制:Server 只能向 stdout 输出合法的 JSON 消息(每行一个),调试日志必须输出到 stderr。

  3. 工具执行结果必须回传 :MCP Server 执行完毕后,必须将结果作为 role: "tool" 消息回传给 DeepSeek 润色。

  4. 迭代限制:Agent 循环必须设置最大迭代次数,防止无限循环。

  5. 记忆写入需有证据:防止 AI 自我强化虚假信息,建议每条记忆都附带原文引用。

  6. Token 管理:DeepSeek 上下文窗口有限(64K-128K),需要主动进行上下文蒸馏。

  7. 字段解析防御 :永远不要用 [] 直接索引嵌套字段;永远不要假设 content 是字符串(工具调用时可能为 null);使用 contains() 检查每一层。

  8. 字段归一化:为大模型或 MCP Server 可能返回的字段别名建立映射表,确保业务逻辑始终使用标准字段名。

8.3 构建与部署

使用 cxxmcp SDK

cmake 复制代码
# CMakeLists.txt
find_package(cxxmcp CONFIG REQUIRED)
target_link_libraries(my_agent PRIVATE cxxmcp::client)

构建 MCP Server

bash 复制代码
bash scripts/install_deps.sh  # OpenSSL, libcurl
bash scripts/build.sh
sh scripts/run_example.sh

部署:C++ 程序可编译成独立静态可执行文件,不依赖 Python 解释器或 Node.js 运行时,只需拷贝一个二进制文件到目标机器即可运行。

九、参考资源

官方文档

C++ MCP SDK

DeepSeek C++ 接入

C++ Agent 项目

  • neoclaw - 100% C++ 本地 Agent
  • myagent - C++ AI Agent 系统,支持 MCP 协议

JSON 处理

十、总结

#mermaid-svg-xbm9nzdlUxzFiTym{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xbm9nzdlUxzFiTym .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xbm9nzdlUxzFiTym .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xbm9nzdlUxzFiTym .error-icon{fill:#552222;}#mermaid-svg-xbm9nzdlUxzFiTym .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xbm9nzdlUxzFiTym .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xbm9nzdlUxzFiTym .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xbm9nzdlUxzFiTym .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xbm9nzdlUxzFiTym .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xbm9nzdlUxzFiTym .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xbm9nzdlUxzFiTym .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xbm9nzdlUxzFiTym .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xbm9nzdlUxzFiTym .marker.cross{stroke:#333333;}#mermaid-svg-xbm9nzdlUxzFiTym svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xbm9nzdlUxzFiTym p{margin:0;}#mermaid-svg-xbm9nzdlUxzFiTym .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xbm9nzdlUxzFiTym .cluster-label text{fill:#333;}#mermaid-svg-xbm9nzdlUxzFiTym .cluster-label span{color:#333;}#mermaid-svg-xbm9nzdlUxzFiTym .cluster-label span p{background-color:transparent;}#mermaid-svg-xbm9nzdlUxzFiTym .label text,#mermaid-svg-xbm9nzdlUxzFiTym span{fill:#333;color:#333;}#mermaid-svg-xbm9nzdlUxzFiTym .node rect,#mermaid-svg-xbm9nzdlUxzFiTym .node circle,#mermaid-svg-xbm9nzdlUxzFiTym .node ellipse,#mermaid-svg-xbm9nzdlUxzFiTym .node polygon,#mermaid-svg-xbm9nzdlUxzFiTym .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xbm9nzdlUxzFiTym .rough-node .label text,#mermaid-svg-xbm9nzdlUxzFiTym .node .label text,#mermaid-svg-xbm9nzdlUxzFiTym .image-shape .label,#mermaid-svg-xbm9nzdlUxzFiTym .icon-shape .label{text-anchor:middle;}#mermaid-svg-xbm9nzdlUxzFiTym .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xbm9nzdlUxzFiTym .rough-node .label,#mermaid-svg-xbm9nzdlUxzFiTym .node .label,#mermaid-svg-xbm9nzdlUxzFiTym .image-shape .label,#mermaid-svg-xbm9nzdlUxzFiTym .icon-shape .label{text-align:center;}#mermaid-svg-xbm9nzdlUxzFiTym .node.clickable{cursor:pointer;}#mermaid-svg-xbm9nzdlUxzFiTym .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xbm9nzdlUxzFiTym .arrowheadPath{fill:#333333;}#mermaid-svg-xbm9nzdlUxzFiTym .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xbm9nzdlUxzFiTym .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xbm9nzdlUxzFiTym .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xbm9nzdlUxzFiTym .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xbm9nzdlUxzFiTym .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xbm9nzdlUxzFiTym .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xbm9nzdlUxzFiTym .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xbm9nzdlUxzFiTym .cluster text{fill:#333;}#mermaid-svg-xbm9nzdlUxzFiTym .cluster span{color:#333;}#mermaid-svg-xbm9nzdlUxzFiTym div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xbm9nzdlUxzFiTym .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xbm9nzdlUxzFiTym rect.text{fill:none;stroke-width:0;}#mermaid-svg-xbm9nzdlUxzFiTym .icon-shape,#mermaid-svg-xbm9nzdlUxzFiTym .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xbm9nzdlUxzFiTym .icon-shape p,#mermaid-svg-xbm9nzdlUxzFiTym .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xbm9nzdlUxzFiTym .icon-shape .label rect,#mermaid-svg-xbm9nzdlUxzFiTym .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xbm9nzdlUxzFiTym .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xbm9nzdlUxzFiTym .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xbm9nzdlUxzFiTym :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 四层架构
交互层

CLI/HTTP/WebSocket
Agent核心层

规划/执行/记忆/蒸馏
MCP适配层

工具发现/路由/回传
工具层

文件/DB/Shell/API
核心原则
防御性解析

永远不假设字段存在
字段归一化

统一别名到标准键
状态管理

消息历史完整传递
迭代控制

防止无限循环
生产就绪的 C++ Agent

AI Agent 开发的核心是系统化思维 。LLM 是"大脑",MCP 是"神经系统",记忆是"经验",C++ 是"强健的骨骼"。而防御性解析字段归一化是确保整个系统在各种"意外"情况下仍能稳定运行的"免疫系统"。

  • 先用原生 Function Calling 跑通最简版本,验证核心逻辑。
  • 再逐步引入 MCP,实现工具标准化和动态扩展。
  • 同时建立防御性解析和字段归一化层,确保系统能应对各种 JSON 结构变化。
  • 最后加入记忆管理和上下文蒸馏,让 Agent 拥有跨会话的长期记忆。

让架构服务于业务,而非被架构绑架。

相关推荐
躺柒1 小时前
读数据可视化13空间标量场(上)
人工智能·深度学习·信息可视化·数据可视化·空间·大数据分析
小王2041 小时前
Day 28:目标检测入门 — 两阶段 vs 单阶段
人工智能·目标检测·计算机视觉
CIO_Alliance1 小时前
AI深度系列(3)| 从RNN到LSTM:序列数据处理的技术逻辑与企业AI化转型启示
人工智能·rnn·深度学习·神经网络·lstm·企业cio联盟·企业级ai化转型
像风一样自由20201 小时前
11.PostgreSQ、-MySQL与MongoDB-AI应用如何选择数据库
数据库·人工智能·mysql·mongodb·大模型·rag·智能体
随风而飘1861 小时前
Keithley美国吉时利 2016-P 6.5位音频分析数字多用表
网络·人工智能·功能测试
hetao17338371 小时前
2026-08-21~23 hetao1733837 的刷题记录
c++·算法
2401_890095611 小时前
如何判断武汉人工智能应用软件开发是否适用?从部署步骤入手
人工智能·武汉自动意志科技有限公司·智钳claw·人工智能应用软件开发·企业ai智能体系统·企业数字化服务
东莞市奥普新音频技术有限公司1 小时前
国产音频分析仪怎么选?从性能参数、测试软件到自动化能力
人工智能·科技·测试工具·自动化·音视频·音频
一只积极向上的小咸鱼1 小时前
pytorch 与资源核算
人工智能·pytorch·python