软件接入大模型实现 Agent ------ 从原理到 C++ 落地完全指南
一、概述:从"对话"到"行动"
1.1 什么是 AI Agent?
传统的大模型应用以"对话"为核心------用户提问,模型回答。而 AI Agent(智能体) 在此基础上更进一步:它不仅能"理解"和"回答",还能"规划"和"执行"。
| 传统 Chatbot | AI Agent | |
|---|---|---|
| 能力边界 | 生成文本回复 | 理解意图 → 规划任务 → 调用工具 → 迭代优化 |
| 与外部交互 | 无 | 可读写文件、查数据库、执行系统命令 |
| 自主性 | 被动响应 | 主动规划并执行复杂任务 |
一个典型的 Agent 场景是:"帮我查一下今天的天气,然后发邮件给团队。"Agent 不仅要理解这句话,还要拆解步骤、调用工具、检查结果、迭代修正。
1.2 为什么用 C++ 构建 Agent?
在 AI 工具链中,Python 因其丰富的库占据主导地位。但用 C++ 构建 Agent 有几个不可替代的优势:
- 性能与资源效率:C++ 的静态类型、编译时优化和对硬件资源的直接掌控,可以显著降低延迟、减少内存分配次数,避免 Python GIL 问题。
- 系统级集成:许多核心基础设施本身就是 C/C++ 编写的,C++ MCP 服务器可以直接链接这些原生库。
- 部署优势: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-httplib 和 nlohmann/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-chat或deepseek-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 关键设计要点
-
消息历史管理 :
messages数组必须包含完整的对话轮次,DeepSeek 才能理解上下文。 -
工具调用回传 :MCP Server 执行完毕后,必须将结果作为
role: "tool"的消息回传,让模型润色。 -
防御性解析 :使用
contains()+value()代替直接索引,防止字段缺失或类型不匹配导致崩溃。 -
字段归一化 :在工具执行前统一处理别名(如
pathvsfilePath),确保业务逻辑始终使用标准字段名。 -
迭代限制: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 关键注意事项
-
R1 不支持 Function Call :DeepSeek-R1 目前不支持 原生 Function Calling,如需工具调用请使用
deepseek-chat或deepseek-v4-pro。 -
MCP Server 输出限制:Server 只能向 stdout 输出合法的 JSON 消息(每行一个),调试日志必须输出到 stderr。
-
工具执行结果必须回传 :MCP Server 执行完毕后,必须将结果作为
role: "tool"消息回传给 DeepSeek 润色。 -
迭代限制:Agent 循环必须设置最大迭代次数,防止无限循环。
-
记忆写入需有证据:防止 AI 自我强化虚假信息,建议每条记忆都附带原文引用。
-
Token 管理:DeepSeek 上下文窗口有限(64K-128K),需要主动进行上下文蒸馏。
-
字段解析防御 :永远不要用
[]直接索引嵌套字段;永远不要假设content是字符串(工具调用时可能为null);使用contains()检查每一层。 -
字段归一化:为大模型或 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
- cxxmcp - 生产就绪 C++17 SDK
- gopher-mcp - 最全面的 C++ MCP 实现
- TinyMCP - 轻量级 C++ MCP SDK
- TSAR-MCP - IBM 零依赖 C/C++ SDK
DeepSeek C++ 接入
- DeepSeekAPI - C++ DeepSeek API 封装库
C++ Agent 项目
JSON 处理
- nlohmann/json - C++ JSON 库
- cpp-httplib - C++ HTTP 客户端/服务端库
十、总结
#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 拥有跨会话的长期记忆。
让架构服务于业务,而非被架构绑架。