C++手把手教你从0到1搭建一个 Claude Opus 5 AI Agent(智能体)
大家好,我是 JavaPub。
前面我们已经分别使用 Python、Node.js 手写过 AI Agent。
这一次来点更硬核的:
C++
+
Claude Opus 5
+
OpenAI Compatible API
+
Tool Calling
+
Agent Loop
我们不使用 LangChain。
不使用 LangGraph。
不使用任何成熟 Agent Framework。
直接使用:
C++17
libcurl
nlohmann/json
从 HTTP 请求开始,自己实现一个真正能够:
理解任务
↓
决定调用工具
↓
执行 C++ 函数
↓
读取工具返回值
↓
继续思考
↓
再次调用工具
↓
完成任务
的 AI Agent。
本文接口继续使用:
https://api.chongplus.plus
调用协议:
OpenAI Compatible API
模型:
claude-opus-5
Anthropic 官方当前公布的 Claude Opus 5 API model ID 也是:
claude-opus-5
并且官方将 Claude Opus 5 推荐用于复杂工具调用、Agent 和较模糊的多步骤任务。
下面默认你的
https://api.chongplus.plus已经将claude-opus-5映射到对应上游模型。
最终,我们要让 C++ 程序实现:
你:
帮我计算 12345 * 6789,
然后把结果保存到笔记。
Claude Opus 5:
[Agent] 准备调用 calculator
[Tool] calculator
参数:
{
"expression": "12345 * 6789"
}
返回:
83810205
Claude Opus 5:
[Agent] 准备调用 save_note
参数:
{
"content": "12345 * 6789 = 83810205"
}
返回:
笔记保存成功
Agent:
计算完成:
12345 × 6789 = 83810205
已经保存到笔记。
最重要的是:
我们不会在 C++ 里面写死:
calculator();
save_note();
而是让:
Claude Opus 5
自己决定:
调用哪个工具?
传什么参数?
调用之后下一步干什么?
什么时候任务完成?
这才是真正的:
AI Agent
一、先理解整个 Agent
传统大模型调用:
User
↓
LLM
↓
Answer
Agent:
User
↓
Claude Opus 5
↓
Decision
↓
Tool Call
↓
C++ Tool
↓
Tool Result
↓
Claude Opus 5
↓
Decision
↓
Tool Call
↓
...
↓
Final Answer
所以一个最基础的 Agent 可以理解为:
Agent
=
LLM
+
Prompt
+
Tools
+
Memory
+
Agent Loop
今天我们一个一个把它写出来。
二、准备 C++ 环境
我们需要:
C++17
CMake
libcurl
nlohmann/json
Ubuntu:
sudo apt update
sudo apt install -y \
build-essential \
cmake \
libcurl4-openssl-dev \
nlohmann-json3-dev
macOS:
brew install cmake
brew install curl
brew install nlohmann-json
查看:
g++ --version
项目目录:
cpp-claude-agent/
├── CMakeLists.txt
└── main.cpp
三、CMakeLists.txt
这一份后面几个例子都可以使用。
cmake_minimum_required(VERSION 3.16)
project(claude_agent)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(CURL REQUIRED)
add_executable(
claude_agent
main.cpp
)
target_link_libraries(
claude_agent
PRIVATE
CURL::libcurl
)
编译:
mkdir build
cd build
cmake ..
cmake --build .
运行:
./claude_agent
四、第一个完整程序:C++ 调用 Claude Opus 5
先不要管 Agent。
第一步:
使用 C++ 成功调用一次 Claude Opus 5。
下面是一份完整代码。
直接保存:
main.cpp
完整代码 1:最简单 Claude Opus 5 对话
#include <curl/curl.h>
#include <iostream>
#include <string>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
/**
* API 配置
*/
const std::string API_KEY =
"sk-xxxxxxxxxxxxxxxx";
const std::string API_URL =
"https://api.chongplus.plus/v1/chat/completions";
const std::string MODEL =
"claude-opus-5";
/**
* libcurl 回调
*
* HTTP 返回的数据会进入这里
*/
size_t WriteCallback(
void* contents,
size_t size,
size_t nmemb,
void* userp
) {
size_t totalSize =
size * nmemb;
std::string* response =
static_cast<std::string*>(userp);
response->append(
static_cast<char*>(contents),
totalSize
);
return totalSize;
}
/**
* 发送 HTTP POST
*/
std::string httpPost(
const std::string& url,
const std::string& body
) {
CURL* curl =
curl_easy_init();
if (!curl) {
throw std::runtime_error(
"curl_easy_init failed"
);
}
std::string response;
struct curl_slist* headers =
nullptr;
headers = curl_slist_append(
headers,
"Content-Type: application/json"
);
std::string authHeader =
"Authorization: Bearer "
+ API_KEY;
headers = curl_slist_append(
headers,
authHeader.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_URL,
url.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_HTTPHEADER,
headers
);
curl_easy_setopt(
curl,
CURLOPT_POST,
1L
);
curl_easy_setopt(
curl,
CURLOPT_POSTFIELDS,
body.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_WRITEFUNCTION,
WriteCallback
);
curl_easy_setopt(
curl,
CURLOPT_WRITEDATA,
&response
);
curl_easy_setopt(
curl,
CURLOPT_TIMEOUT,
300L
);
CURLcode result =
curl_easy_perform(curl);
if (
result
!= CURLE_OK
) {
std::string error =
curl_easy_strerror(result);
curl_slist_free_all(
headers
);
curl_easy_cleanup(
curl
);
throw std::runtime_error(
error
);
}
curl_slist_free_all(
headers
);
curl_easy_cleanup(
curl
);
return response;
}
/**
* 调用 Claude
*/
std::string callClaude(
const std::string& prompt
) {
json request;
request["model"] =
MODEL;
request["messages"] =
json::array({
{
{
"role",
"user"
},
{
"content",
prompt
}
}
});
std::string rawResponse =
httpPost(
API_URL,
request.dump()
);
json response =
json::parse(
rawResponse
);
if (
response.contains(
"error"
)
) {
throw std::runtime_error(
response.dump(2)
);
}
return response[
"choices"
][0][
"message"
][
"content"
];
}
/**
* main
*/
int main() {
curl_global_init(
CURL_GLOBAL_DEFAULT
);
try {
std::string result =
callClaude(
"你好,请简单介绍一下你自己。"
);
std::cout
<< "Claude:"
<< std::endl
<< result
<< std::endl;
}
catch (
const std::exception& e
) {
std::cerr
<< "Error: "
<< e.what()
<< std::endl;
}
curl_global_cleanup();
return 0;
}
编译:
mkdir build
cd build
cmake ..
cmake --build .
运行:
./claude_agent
可能得到:
Claude:
你好!我是 Claude,可以帮助你进行编程、分析、写作以及完成复杂任务。
到这里:
C++
↓
HTTPS
↓
api.chongplus.plus
↓
Claude Opus 5
↓
Response
已经跑通。
但是现在只是:
ChatBot
还不是:
Agent
五、第二个完整程序:让 Claude 调用 C++ Calculator
接下来进入核心:
Tool Calling
Anthropic 官方的工具调用机制本质也是:
模型决定调用工具
↓
应用程序执行工具
↓
工具结果交还模型
↓
模型继续完成任务
对于 client-side tool,工具真正执行的位置仍然是在我们的程序里,而不是模型服务器替你运行本地 C++ 函数。
假设我们的 C++ 有一个函数:
calculator()
用户:
12345 * 6789 是多少?
Claude 不直接计算。
而是返回:
我要调用 calculator
然后:
C++
真正执行。
六、完整代码 2:单 Tool Agent
下面还是一份完全独立的:
main.cpp
不用拼上一篇代码。
完整代码
#include <curl/curl.h>
#include <iostream>
#include <regex>
#include <stdexcept>
#include <string>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
/**
* ============================
* Config
* ============================
*/
const std::string API_KEY =
"sk-xxxxxxxxxxxxxxxx";
const std::string API_URL =
"https://api.chongplus.plus/v1/chat/completions";
const std::string MODEL =
"claude-opus-5";
/**
* ============================
* HTTP
* ============================
*/
size_t WriteCallback(
void* contents,
size_t size,
size_t nmemb,
void* userp
) {
size_t total =
size * nmemb;
auto* response =
static_cast<std::string*>(
userp
);
response->append(
static_cast<char*>(
contents
),
total
);
return total;
}
std::string httpPost(
const std::string& url,
const json& payload
) {
CURL* curl =
curl_easy_init();
if (!curl) {
throw std::runtime_error(
"Unable to initialize curl"
);
}
std::string response;
struct curl_slist* headers =
nullptr;
headers = curl_slist_append(
headers,
"Content-Type: application/json"
);
std::string auth =
"Authorization: Bearer "
+ API_KEY;
headers = curl_slist_append(
headers,
auth.c_str()
);
std::string body =
payload.dump();
curl_easy_setopt(
curl,
CURLOPT_URL,
url.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_HTTPHEADER,
headers
);
curl_easy_setopt(
curl,
CURLOPT_POSTFIELDS,
body.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_WRITEFUNCTION,
WriteCallback
);
curl_easy_setopt(
curl,
CURLOPT_WRITEDATA,
&response
);
curl_easy_setopt(
curl,
CURLOPT_TIMEOUT,
300L
);
CURLcode code =
curl_easy_perform(
curl
);
if (
code
!= CURLE_OK
) {
std::string error =
curl_easy_strerror(
code
);
curl_slist_free_all(
headers
);
curl_easy_cleanup(
curl
);
throw std::runtime_error(
error
);
}
curl_slist_free_all(
headers
);
curl_easy_cleanup(
curl
);
return response;
}
/**
* ============================
* Calculator Tool
* ============================
*
* 为了教程简单:
*
* 支持:
*
* 10 + 20
* 10 - 20
* 10 * 20
* 10 / 20
*
* 不使用 eval。
*/
std::string calculator(
const std::string& expression
) {
std::regex pattern(
R"(^\s*(-?\d+(?:\.\d+)?)\s*([\+\-\*\/])\s*(-?\d+(?:\.\d+)?)\s*$)"
);
std::smatch match;
if (
!std::regex_match(
expression,
match,
pattern
)
) {
return
"计算失败:暂时只支持两个数字的 + - * / 运算";
}
double a =
std::stod(
match[1].str()
);
char op =
match[2].str()[0];
double b =
std::stod(
match[3].str()
);
double result;
switch (op) {
case '+':
result =
a + b;
break;
case '-':
result =
a - b;
break;
case '*':
result =
a * b;
break;
case '/':
if (
b == 0
) {
return
"计算失败:除数不能为 0";
}
result =
a / b;
break;
default:
return
"计算失败:未知运算符";
}
std::string value =
std::to_string(
result
);
while (
value.size() > 1
&&
value.back() == '0'
) {
value.pop_back();
}
if (
!value.empty()
&&
value.back() == '.'
) {
value.pop_back();
}
return value;
}
/**
* ============================
* Tool Schema
* ============================
*/
json createTools() {
return json::array({
{
{
"type",
"function"
},
{
"function",
{
{
"name",
"calculator"
},
{
"description",
"执行数学计算。当用户要求进行加减乘除计算时调用这个工具。不要自己猜测计算结果,应该调用 calculator 获取真正的计算结果。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
{
{
"expression",
{
{
"type",
"string"
},
{
"description",
"数学表达式,例如 12345 * 6789"
}
}
}
}
},
{
"required",
json::array({
"expression"
})
}
}
}
}
}
}
});
}
/**
* ============================
* Main
* ============================
*/
int main() {
curl_global_init(
CURL_GLOBAL_DEFAULT
);
try {
json messages =
json::array({
{
{
"role",
"system"
},
{
"content",
"你是一个 AI Agent。需要数学计算时必须调用 calculator 工具。"
}
},
{
{
"role",
"user"
},
{
"content",
"帮我计算 12345 * 6789"
}
}
});
json request;
request["model"] =
MODEL;
request["messages"] =
messages;
request["tools"] =
createTools();
request["tool_choice"] =
"auto";
std::string raw =
httpPost(
API_URL,
request
);
json response =
json::parse(
raw
);
if (
response.contains(
"error"
)
) {
throw std::runtime_error(
response.dump(2)
);
}
json assistantMessage =
response[
"choices"
][0][
"message"
];
std::cout
<< "第一次模型返回:"
<< std::endl
<< assistantMessage.dump(2)
<< std::endl;
/**
* 如果模型要调用 Tool
*/
if (
assistantMessage.contains(
"tool_calls"
)
) {
messages.push_back(
assistantMessage
);
for (
const auto& toolCall :
assistantMessage[
"tool_calls"
]
) {
std::string toolName =
toolCall[
"function"
][
"name"
];
std::string argumentsString =
toolCall[
"function"
][
"arguments"
];
json arguments =
json::parse(
argumentsString
);
std::string result;
if (
toolName
==
"calculator"
) {
result =
calculator(
arguments[
"expression"
]
);
}
else {
result =
"未知工具";
}
std::cout
<< std::endl
<< "[Tool] "
<< toolName
<< std::endl;
std::cout
<< "参数:"
<< arguments.dump(2)
<< std::endl;
std::cout
<< "结果:"
<< result
<< std::endl;
messages.push_back({
{
"role",
"tool"
},
{
"tool_call_id",
toolCall[
"id"
]
},
{
"content",
result
}
});
}
/**
* 将 Tool Result
* 再次发送给 Claude
*/
json secondRequest;
secondRequest["model"] =
MODEL;
secondRequest["messages"] =
messages;
secondRequest["tools"] =
createTools();
secondRequest[
"tool_choice"
] =
"auto";
std::string secondRaw =
httpPost(
API_URL,
secondRequest
);
json secondResponse =
json::parse(
secondRaw
);
std::cout
<< std::endl
<< "Claude:"
<< std::endl
<< secondResponse[
"choices"
][0][
"message"
][
"content"
]
<< std::endl;
}
else {
std::cout
<< "Claude:"
<< assistantMessage[
"content"
]
<< std::endl;
}
}
catch (
const std::exception& e
) {
std::cerr
<< "Error: "
<< e.what()
<< std::endl;
}
curl_global_cleanup();
return 0;
}
运行:
./claude_agent
第一次 Claude 可能返回:
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "calculator",
"arguments": "{\"expression\":\"12345 * 6789\"}"
}
}
]
}
C++ 执行:
[Tool] calculator
参数:
{
"expression": "12345 * 6789"
}
结果:
83810205
然后我们把:
83810205
重新告诉 Claude。
Claude:
12345 × 6789 = 83810205。
现在已经出现第一个真正的:
Claude Tool Calling
七、这里发生了什么?
特别重要。
Claude 并没有真正执行:
calculator()
Claude 做的是:
“我决定调用 calculator。”
然后返回:
{
"name": "calculator",
"arguments": {
"expression": "12345 * 6789"
}
}
真正执行的是:
我们的 C++ 程序
所以:
Claude
负责决策
C++
负责执行
这就是 Agent 最核心的分工。
八、第三个完整程序:三个 Tools
现在继续升级。
增加:
calculator
get_current_time
save_note
其中:
calculator
负责计算。
get_current_time
获取当前时间。
save_note
把信息保存到:
notes.txt
现在用户就可以说:
计算 12345 * 6789,
然后把结果保存下来。
Agent 理论上需要:
calculator
↓
save_note
但这时候会出现一个问题。
我们的上一份代码:
最多只执行一轮工具。
真正复杂任务需要:
多轮 Tool Calling
所以先实现多工具。
九、完整代码 3:多 Tool C++ Agent
这一份依然:
完全独立、直接复制就能运行。
#include <curl/curl.h>
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <nlohmann/json.hpp>
using json =
nlohmann::json;
/**
* ==========================
* Config
* ==========================
*/
const std::string API_KEY =
"sk-xxxxxxxxxxxxxxxx";
const std::string API_URL =
"https://api.chongplus.plus/v1/chat/completions";
const std::string MODEL =
"claude-opus-5";
/**
* ==========================
* HTTP
* ==========================
*/
size_t WriteCallback(
void* contents,
size_t size,
size_t nmemb,
void* userp
) {
size_t total =
size * nmemb;
auto* output =
static_cast<std::string*>(
userp
);
output->append(
static_cast<char*>(
contents
),
total
);
return total;
}
json httpPost(
const json& request
) {
CURL* curl =
curl_easy_init();
if (!curl) {
throw std::runtime_error(
"curl init failed"
);
}
std::string response;
struct curl_slist* headers =
nullptr;
headers = curl_slist_append(
headers,
"Content-Type: application/json"
);
std::string authorization =
"Authorization: Bearer "
+ API_KEY;
headers = curl_slist_append(
headers,
authorization.c_str()
);
std::string body =
request.dump();
curl_easy_setopt(
curl,
CURLOPT_URL,
API_URL.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_HTTPHEADER,
headers
);
curl_easy_setopt(
curl,
CURLOPT_POSTFIELDS,
body.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_WRITEFUNCTION,
WriteCallback
);
curl_easy_setopt(
curl,
CURLOPT_WRITEDATA,
&response
);
curl_easy_setopt(
curl,
CURLOPT_TIMEOUT,
300L
);
CURLcode code =
curl_easy_perform(
curl
);
curl_slist_free_all(
headers
);
curl_easy_cleanup(
curl
);
if (
code
!= CURLE_OK
) {
throw std::runtime_error(
curl_easy_strerror(
code
)
);
}
return json::parse(
response
);
}
/**
* ==========================
* Calculator
* ==========================
*/
std::string calculator(
const std::string& expression
) {
std::regex pattern(
R"(^\s*(-?\d+(?:\.\d+)?)\s*([\+\-\*\/])\s*(-?\d+(?:\.\d+)?)\s*$)"
);
std::smatch match;
if (
!std::regex_match(
expression,
match,
pattern
)
) {
return
"暂时只支持两个数字之间的 + - * /";
}
double left =
std::stod(
match[1].str()
);
char op =
match[2].str()[0];
double right =
std::stod(
match[3].str()
);
double result = 0;
switch (op) {
case '+':
result =
left + right;
break;
case '-':
result =
left - right;
break;
case '*':
result =
left * right;
break;
case '/':
if (
right == 0
) {
return
"除数不能为 0";
}
result =
left / right;
break;
}
std::ostringstream stream;
stream
<< std::setprecision(15)
<< result;
return stream.str();
}
/**
* ==========================
* Current Time
* ==========================
*/
std::string getCurrentTime() {
auto now =
std::chrono::system_clock::now();
std::time_t time =
std::chrono::system_clock::to_time_t(
now
);
std::tm tm{};
#ifdef _WIN32
localtime_s(
&tm,
&time
);
#else
localtime_r(
&time,
&tm
);
#endif
std::ostringstream output;
output
<< std::put_time(
&tm,
"%Y-%m-%d %H:%M:%S"
);
return output.str();
}
/**
* ==========================
* Save Note
* ==========================
*/
std::string saveNote(
const std::string& content
) {
std::ofstream file(
"notes.txt",
std::ios::app
);
if (
!file.is_open()
) {
return
"笔记保存失败";
}
file
<< content
<< std::endl;
file.close();
return
"笔记保存成功";
}
/**
* ==========================
* Tool Schema
* ==========================
*/
json createTools() {
return json::array({
{
{
"type",
"function"
},
{
"function",
{
{
"name",
"calculator"
},
{
"description",
"执行数学计算。当用户明确要求计算数学表达式时调用。应该使用工具得到准确结果,而不是猜测答案。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
{
{
"expression",
{
{
"type",
"string"
},
{
"description",
"数学表达式,例如 12345 * 6789"
}
}
}
}
},
{
"required",
json::array({
"expression"
})
}
}
}
}
}
},
{
{
"type",
"function"
},
{
"function",
{
{
"name",
"get_current_time"
},
{
"description",
"获取程序当前运行环境的本地时间。当用户询问现在时间、当前日期或要求记录当前时间时调用。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
json::object()
}
}
}
}
}
},
{
{
"type",
"function"
},
{
"function",
{
{
"name",
"save_note"
},
{
"description",
"把指定文本保存到本地 notes.txt 文件。当用户明确要求保存、记录或写入笔记时调用。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
{
{
"content",
{
{
"type",
"string"
},
{
"description",
"需要保存到笔记中的完整文本"
}
}
}
}
},
{
"required",
json::array({
"content"
})
}
}
}
}
}
}
});
}
/**
* ==========================
* Tool Executor
* ==========================
*/
std::string executeTool(
const std::string& name,
const json& arguments
) {
if (
name
==
"calculator"
) {
return calculator(
arguments.value(
"expression",
""
)
);
}
if (
name
==
"get_current_time"
) {
return getCurrentTime();
}
if (
name
==
"save_note"
) {
return saveNote(
arguments.value(
"content",
""
)
);
}
return
"未知工具:"
+ name;
}
/**
* ==========================
* Main
* ==========================
*/
int main() {
curl_global_init(
CURL_GLOBAL_DEFAULT
);
try {
json messages =
json::array({
{
{
"role",
"system"
},
{
"content",
R"(你是一个 AI Agent。
你拥有三个工具:
calculator:
进行数学计算。
get_current_time:
获取当前时间。
save_note:
保存笔记。
需要工具时必须调用工具。
不要伪造工具返回值。
工具执行完成以后,根据结果继续帮助用户。)"
}
},
{
{
"role",
"user"
},
{
"content",
"帮我计算 12345 * 6789"
}
}
});
json request;
request["model"] =
MODEL;
request["messages"] =
messages;
request["tools"] =
createTools();
request["tool_choice"] =
"auto";
json response =
httpPost(
request
);
if (
response.contains(
"error"
)
) {
throw std::runtime_error(
response.dump(2)
);
}
json message =
response[
"choices"
][0][
"message"
];
messages.push_back(
message
);
if (
message.contains(
"tool_calls"
)
) {
for (
const auto& call :
message[
"tool_calls"
]
) {
std::string name =
call[
"function"
][
"name"
];
json arguments =
json::parse(
call[
"function"
][
"arguments"
].get<
std::string
>()
);
std::cout
<< "[Agent] 调用工具:"
<< name
<< std::endl;
std::cout
<< "[Agent] 参数:"
<< arguments.dump(2)
<< std::endl;
std::string result =
executeTool(
name,
arguments
);
std::cout
<< "[Tool] 返回:"
<< result
<< std::endl;
messages.push_back({
{
"role",
"tool"
},
{
"tool_call_id",
call[
"id"
]
},
{
"content",
result
}
});
}
json finalRequest;
finalRequest["model"] =
MODEL;
finalRequest["messages"] =
messages;
finalRequest["tools"] =
createTools();
finalRequest[
"tool_choice"
] =
"auto";
json finalResponse =
httpPost(
finalRequest
);
std::cout
<< std::endl
<< "Agent:"
<< std::endl
<< finalResponse[
"choices"
][0][
"message"
][
"content"
]
<< std::endl;
}
else {
std::cout
<< message[
"content"
]
<< std::endl;
}
}
catch (
const std::exception& e
) {
std::cerr
<< "Error: "
<< e.what()
<< std::endl;
}
curl_global_cleanup();
return 0;
}
这时候:
User
↓
Claude Opus 5
↓
选择 Tool
↓
C++
↓
Tool Result
↓
Claude
已经完全跑通。
但是:
现在仍然不能算一个完整 Agent。
因为:
它只执行一轮。
十、真正的 Agent 核心:Agent Loop
假设用户:
帮我计算 12345 * 6789,
然后把计算结果保存成笔记。
第一轮:
Claude
↓
calculator
结果:
83810205
这时候任务其实还没完成。
因为用户还要求:
保存结果
于是应该:
83810205
↓
Claude
↓
save_note
↓
保存成功
↓
Claude
↓
Final Answer
所以不能:
if (tool_calls) {
}
执行完一次就结束。
而应该:
while (true)
不断执行。
这就是:
Agent Loop
十一、第四个完整程序:真正的 Claude Opus 5 Agent
下面这份代码是本文最重要的一份。
它已经具备:
HTTP Client
Claude Opus 5
System Prompt
Tools
Tool Executor
Tool Calling
Agent Loop
最大执行次数
多 Tool
异常处理
完整代码 4:真正的 C++ Agent
#include <curl/curl.h>
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <nlohmann/json.hpp>
using json =
nlohmann::json;
/**
* ==================================================
* Config
* ==================================================
*/
const std::string API_KEY =
"sk-xxxxxxxxxxxxxxxx";
const std::string API_URL =
"https://api.chongplus.plus/v1/chat/completions";
const std::string MODEL =
"claude-opus-5";
const int MAX_AGENT_STEPS =
20;
/**
* ==================================================
* libcurl callback
* ==================================================
*/
size_t WriteCallback(
void* contents,
size_t size,
size_t nmemb,
void* userp
) {
size_t total =
size * nmemb;
std::string* output =
static_cast<std::string*>(
userp
);
output->append(
static_cast<char*>(
contents
),
total
);
return total;
}
/**
* ==================================================
* HTTP Client
* ==================================================
*/
json postJson(
const json& payload
) {
CURL* curl =
curl_easy_init();
if (!curl) {
throw std::runtime_error(
"curl_easy_init failed"
);
}
std::string response;
struct curl_slist* headers =
nullptr;
headers =
curl_slist_append(
headers,
"Content-Type: application/json"
);
std::string authorization =
"Authorization: Bearer "
+ API_KEY;
headers =
curl_slist_append(
headers,
authorization.c_str()
);
std::string requestBody =
payload.dump();
curl_easy_setopt(
curl,
CURLOPT_URL,
API_URL.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_HTTPHEADER,
headers
);
curl_easy_setopt(
curl,
CURLOPT_POST,
1L
);
curl_easy_setopt(
curl,
CURLOPT_POSTFIELDS,
requestBody.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_POSTFIELDSIZE,
requestBody.size()
);
curl_easy_setopt(
curl,
CURLOPT_WRITEFUNCTION,
WriteCallback
);
curl_easy_setopt(
curl,
CURLOPT_WRITEDATA,
&response
);
curl_easy_setopt(
curl,
CURLOPT_TIMEOUT,
600L
);
CURLcode code =
curl_easy_perform(
curl
);
if (
code
!= CURLE_OK
) {
std::string message =
curl_easy_strerror(
code
);
curl_slist_free_all(
headers
);
curl_easy_cleanup(
curl
);
throw std::runtime_error(
"HTTP Error: "
+ message
);
}
long statusCode = 0;
curl_easy_getinfo(
curl,
CURLINFO_RESPONSE_CODE,
&statusCode
);
curl_slist_free_all(
headers
);
curl_easy_cleanup(
curl
);
if (
statusCode < 200
||
statusCode >= 300
) {
throw std::runtime_error(
"HTTP "
+ std::to_string(
statusCode
)
+ "\n"
+ response
);
}
return json::parse(
response
);
}
/**
* ==================================================
* Calculator Tool
* ==================================================
*/
std::string calculator(
const std::string& expression
) {
std::regex pattern(
R"(^\s*(-?\d+(?:\.\d+)?)\s*([\+\-\*\/])\s*(-?\d+(?:\.\d+)?)\s*$)"
);
std::smatch match;
if (
!std::regex_match(
expression,
match,
pattern
)
) {
return
"计算失败:只支持两个数字之间的 + - * /";
}
double a =
std::stod(
match[1].str()
);
char operation =
match[2].str()[0];
double b =
std::stod(
match[3].str()
);
double result = 0;
switch (
operation
) {
case '+':
result =
a + b;
break;
case '-':
result =
a - b;
break;
case '*':
result =
a * b;
break;
case '/':
if (
b == 0
) {
return
"计算失败:除数不能为 0";
}
result =
a / b;
break;
default:
return
"计算失败";
}
std::ostringstream output;
output
<< std::setprecision(15)
<< result;
return output.str();
}
/**
* ==================================================
* Time Tool
* ==================================================
*/
std::string getCurrentTime() {
auto now =
std::chrono::system_clock::now();
std::time_t current =
std::chrono::system_clock::to_time_t(
now
);
std::tm timeInfo{};
#ifdef _WIN32
localtime_s(
&timeInfo,
¤t
);
#else
localtime_r(
¤t,
&timeInfo
);
#endif
std::ostringstream output;
output
<< std::put_time(
&timeInfo,
"%Y-%m-%d %H:%M:%S"
);
return output.str();
}
/**
* ==================================================
* Save Note Tool
* ==================================================
*/
std::string saveNote(
const std::string& content
) {
std::ofstream file(
"notes.txt",
std::ios::app
);
if (
!file
) {
return
"保存失败:无法打开 notes.txt";
}
file
<< content
<< std::endl;
file.close();
return
"笔记保存成功";
}
/**
* ==================================================
* Tool Schemas
* ==================================================
*/
json buildTools() {
json tools =
json::array();
tools.push_back({
{
"type",
"function"
},
{
"function",
{
{
"name",
"calculator"
},
{
"description",
"执行基础数学计算。当用户需要获得加减乘除计算结果时调用。不要自己估算或猜测数学结果,应优先使用该工具获取准确结果。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
{
{
"expression",
{
{
"type",
"string"
},
{
"description",
"要执行的数学表达式,例如 12345 * 6789"
}
}
}
}
},
{
"required",
json::array({
"expression"
})
}
}
}
}
}
});
tools.push_back({
{
"type",
"function"
},
{
"function",
{
{
"name",
"get_current_time"
},
{
"description",
"读取运行 Agent 的计算机当前本地时间。当用户询问当前时间、日期,或者任务需要真实时间数据时调用。不要根据模型知识猜测当前时间。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
json::object()
}
}
}
}
}
});
tools.push_back({
{
"type",
"function"
},
{
"function",
{
{
"name",
"save_note"
},
{
"description",
"把指定文本追加保存到本地 notes.txt 文件。只有当用户要求保存、记录、写入笔记,或者前面的任务明确需要持久化结果时调用。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
{
{
"content",
{
{
"type",
"string"
},
{
"description",
"需要完整保存到笔记文件的内容"
}
}
}
}
},
{
"required",
json::array({
"content"
})
}
}
}
}
}
});
return tools;
}
/**
* ==================================================
* Tool Executor
* ==================================================
*/
std::string executeTool(
const std::string& toolName,
const json& arguments
) {
if (
toolName
==
"calculator"
) {
return calculator(
arguments.value(
"expression",
""
)
);
}
if (
toolName
==
"get_current_time"
) {
return getCurrentTime();
}
if (
toolName
==
"save_note"
) {
return saveNote(
arguments.value(
"content",
""
)
);
}
return
"Tool Error:未知工具 "
+ toolName;
}
/**
* ==================================================
* Claude API
* ==================================================
*/
json callClaude(
const json& messages
) {
json request;
request["model"] =
MODEL;
request["messages"] =
messages;
request["tools"] =
buildTools();
request["tool_choice"] =
"auto";
return postJson(
request
);
}
/**
* ==================================================
* Agent
* ==================================================
*/
std::string runAgent(
const std::string& userInput
) {
json messages =
json::array();
messages.push_back({
{
"role",
"system"
},
{
"content",
R"(你是一个 Claude Opus 5 AI Agent。
你的目标不是简单回答用户问题,而是尽可能帮助用户真正完成任务。
你拥有以下工具:
1. calculator
负责数学计算。
2. get_current_time
负责获取真实的当前时间。
3. save_note
负责保存笔记。
工作规则:
1. 首先理解用户真正想完成什么。
2. 如果能够直接回答,可以直接回答。
3. 如果任务需要使用工具,就调用对应工具。
4. 不允许伪造工具执行结果。
5. 每次获得 Tool Result 以后继续判断任务是否已经完成。
6. 如果还需要其他工具,可以继续调用。
7. 一个任务可以连续调用多个工具。
8. 当任务真正完成以后,再返回最终答案。)"
}
});
messages.push_back({
{
"role",
"user"
},
{
"content",
userInput
}
});
/**
* Agent Loop
*/
for (
int step = 1;
step <= MAX_AGENT_STEPS;
++step
) {
std::cout
<< std::endl
<< "========== Agent Step "
<< step
<< " =========="
<< std::endl;
json response =
callClaude(
messages
);
if (
response.contains(
"error"
)
) {
throw std::runtime_error(
response.dump(2)
);
}
json message =
response[
"choices"
][0][
"message"
];
/**
* 保存 Assistant Message
*/
messages.push_back(
message
);
/**
* 没有 Tool Call
*
* 说明 Claude 认为任务已经完成。
*/
if (
!message.contains(
"tool_calls"
)
||
message[
"tool_calls"
].empty()
) {
if (
message.contains(
"content"
)
&&
!message[
"content"
].is_null()
) {
return message[
"content"
].get<
std::string
>();
}
return
"Agent 已结束,但没有返回文本。";
}
/**
* 处理所有 Tool Calls
*/
for (
const auto& toolCall :
message[
"tool_calls"
]
) {
std::string toolName =
toolCall[
"function"
][
"name"
];
std::string argumentText =
toolCall[
"function"
][
"arguments"
].get<
std::string
>();
json arguments;
try {
arguments =
json::parse(
argumentText
);
}
catch (...) {
arguments =
json::object();
}
std::cout
<< "[Agent] 调用工具:"
<< toolName
<< std::endl;
std::cout
<< "[Agent] 参数:"
<< arguments.dump(2)
<< std::endl;
std::string toolResult;
try {
toolResult =
executeTool(
toolName,
arguments
);
}
catch (
const std::exception& e
) {
toolResult =
std::string(
"Tool Error: "
)
+
e.what();
}
std::cout
<< "[Tool] 返回:"
<< toolResult
<< std::endl;
/**
* Tool Result
*/
messages.push_back({
{
"role",
"tool"
},
{
"tool_call_id",
toolCall[
"id"
]
},
{
"content",
toolResult
}
});
}
}
throw std::runtime_error(
"Agent 超过最大执行步数:"
+
std::to_string(
MAX_AGENT_STEPS
)
);
}
/**
* ==================================================
* main
* ==================================================
*/
int main() {
curl_global_init(
CURL_GLOBAL_DEFAULT
);
try {
std::string result =
runAgent(
"帮我计算 12345 * 6789,然后把计算结果保存到笔记。"
);
std::cout
<< std::endl
<< "============================"
<< std::endl;
std::cout
<< "Agent:"
<< std::endl
<< result
<< std::endl;
}
catch (
const std::exception& e
) {
std::cerr
<< std::endl
<< "Agent Error:"
<< e.what()
<< std::endl;
}
curl_global_cleanup();
return 0;
}
运行:
./claude_agent
可能输出:
========== Agent Step 1 ==========
[Agent] 调用工具:calculator
[Agent] 参数:
{
"expression": "12345 * 6789"
}
[Tool] 返回:
83810205
注意:
程序没有结束。
进入:
Agent Step 2
Claude 再次看到:
User:
计算并保存结果
Tool:
83810205
于是它发现:
还没有保存。
继续:
========== Agent Step 2 ==========
[Agent] 调用工具:save_note
[Agent] 参数:
{
"content": "12345 × 6789 = 83810205"
}
[Tool] 返回:
笔记保存成功
然后:
========== Agent Step 3 ==========
Claude 发现:
计算完成
保存完成
不再产生:
tool_calls
最终:
Agent:
计算完成。
12345 × 6789 = 83810205。
计算结果已经保存到笔记。
同时生成:
notes.txt
内容:
12345 × 6789 = 83810205
现在:
我们已经真正实现了一个 C++ AI Agent。
十二、Agent Loop 才是核心
上面几百行代码看起来很多。
但真正的 Agent 核心其实只有:
for (
int step = 0;
step < MAX_AGENT_STEPS;
++step
) {
auto response =
callClaude(
messages
);
if (
response
没有工具调用
) {
return
最终答案;
}
for (
每一个 Tool Call
) {
auto result =
executeTool();
messages.push_back(
Tool Result
);
}
}
翻译成人话:
让 Claude 思考
↓
想调用工具?
↓
是
↓
执行工具
↓
告诉 Claude 工具结果
↓
继续思考
↓
还需要工具?
↓
继续
↓
直到任务完成
这就是:
Agent Loop
十三、第五个完整程序:加入 Memory + CLI
刚才还有一个问题。
我们的:
runAgent()
每调用一次:
messages
都会重新创建。
那么:
你:
我叫 JavaPub。
Claude:
好的。
你:
我叫什么?
第二次运行的时候:
Claude 已经忘了。
所以最后我们实现:
Conversation Memory
以及:
CLI Agent
最终效果:
Claude Opus 5 C++ Agent
================================
你:
我叫 JavaPub。
Agent:
好的,我记住了。
你:
帮我计算 111 * 222
Agent:
24642
你:
把刚才那个数字保存起来
Agent:
已经保存 24642。
你:
我叫什么?
Agent:
你叫 JavaPub。
十四、完整代码 5:最终版 C++ Claude Opus 5 Agent
下面就是最终完整版本。
这一份建议直接作为:
main.cpp
保存。
#include <curl/curl.h>
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <nlohmann/json.hpp>
using json =
nlohmann::json;
/**
* ==================================================
* Configuration
* ==================================================
*/
const std::string API_KEY =
"sk-xxxxxxxxxxxxxxxx";
const std::string API_URL =
"https://api.chongplus.plus/v1/chat/completions";
const std::string MODEL =
"claude-opus-5";
const int MAX_AGENT_STEPS =
20;
/**
* ==================================================
* System Prompt
* ==================================================
*/
const std::string SYSTEM_PROMPT =
R"(你是一个 Claude Opus 5 AI Agent。
你的目标不是简单回答问题,而是帮助用户真正完成任务。
你拥有:
calculator
用于数学计算。
get_current_time
用于获取运行环境的当前时间。
save_note
用于把信息保存到本地笔记。
规则:
1. 先理解用户真正想完成的事情。
2. 可以直接回答的问题直接回答。
3. 需要工具时调用工具。
4. 不允许假装调用工具。
5. 不允许伪造工具返回结果。
6. 工具执行完成以后,根据真实结果继续分析。
7. 一个任务允许连续调用多个工具。
8. 当前任务没有完成时继续工作。
9. 完成任务以后,再向用户返回最终答案。)";
/**
* ==================================================
* CURL Callback
* ==================================================
*/
size_t WriteCallback(
void* contents,
size_t size,
size_t nmemb,
void* userp
) {
size_t bytes =
size * nmemb;
auto* result =
static_cast<std::string*>(
userp
);
result->append(
static_cast<char*>(
contents
),
bytes
);
return bytes;
}
/**
* ==================================================
* HTTP JSON POST
* ==================================================
*/
json postJson(
const json& payload
) {
CURL* curl =
curl_easy_init();
if (!curl) {
throw std::runtime_error(
"无法初始化 CURL"
);
}
struct curl_slist* headers =
nullptr;
headers =
curl_slist_append(
headers,
"Content-Type: application/json"
);
std::string authHeader =
"Authorization: Bearer "
+ API_KEY;
headers =
curl_slist_append(
headers,
authHeader.c_str()
);
std::string requestBody =
payload.dump();
std::string responseBody;
curl_easy_setopt(
curl,
CURLOPT_URL,
API_URL.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_HTTPHEADER,
headers
);
curl_easy_setopt(
curl,
CURLOPT_POST,
1L
);
curl_easy_setopt(
curl,
CURLOPT_POSTFIELDS,
requestBody.c_str()
);
curl_easy_setopt(
curl,
CURLOPT_POSTFIELDSIZE,
requestBody.size()
);
curl_easy_setopt(
curl,
CURLOPT_WRITEFUNCTION,
WriteCallback
);
curl_easy_setopt(
curl,
CURLOPT_WRITEDATA,
&responseBody
);
curl_easy_setopt(
curl,
CURLOPT_TIMEOUT,
600L
);
CURLcode code =
curl_easy_perform(
curl
);
long httpCode = 0;
curl_easy_getinfo(
curl,
CURLINFO_RESPONSE_CODE,
&httpCode
);
curl_slist_free_all(
headers
);
curl_easy_cleanup(
curl
);
if (
code
!= CURLE_OK
) {
throw std::runtime_error(
curl_easy_strerror(
code
)
);
}
if (
httpCode < 200
||
httpCode >= 300
) {
throw std::runtime_error(
"HTTP "
+
std::to_string(
httpCode
)
+
"\n"
+
responseBody
);
}
return json::parse(
responseBody
);
}
/**
* ==================================================
* Tool: calculator
* ==================================================
*/
std::string calculator(
const std::string& expression
) {
std::regex pattern(
R"(^\s*(-?\d+(?:\.\d+)?)\s*([\+\-\*\/])\s*(-?\d+(?:\.\d+)?)\s*$)"
);
std::smatch result;
if (
!std::regex_match(
expression,
result,
pattern
)
) {
return
"计算失败:当前计算器只支持两个数字之间的 + - * /";
}
double left =
std::stod(
result[1].str()
);
char operation =
result[2].str()[0];
double right =
std::stod(
result[3].str()
);
double answer = 0;
switch (operation) {
case '+':
answer =
left + right;
break;
case '-':
answer =
left - right;
break;
case '*':
answer =
left * right;
break;
case '/':
if (
right == 0
) {
return
"计算失败:除数不能为 0";
}
answer =
left / right;
break;
default:
return
"计算失败";
}
std::ostringstream stream;
stream
<< std::setprecision(15)
<< answer;
return stream.str();
}
/**
* ==================================================
* Tool: current time
* ==================================================
*/
std::string getCurrentTime() {
auto now =
std::chrono::system_clock::now();
std::time_t nowTime =
std::chrono::system_clock::to_time_t(
now
);
std::tm local{};
#ifdef _WIN32
localtime_s(
&local,
&nowTime
);
#else
localtime_r(
&nowTime,
&local
);
#endif
std::ostringstream stream;
stream
<< std::put_time(
&local,
"%Y-%m-%d %H:%M:%S"
);
return stream.str();
}
/**
* ==================================================
* Tool: save note
* ==================================================
*/
std::string saveNote(
const std::string& content
) {
std::ofstream output(
"notes.txt",
std::ios::app
);
if (
!output.is_open()
) {
return
"保存失败:notes.txt 无法打开";
}
output
<< content
<< '\n';
output.close();
return
"笔记保存成功";
}
/**
* ==================================================
* Tool Definitions
* ==================================================
*/
json buildTools() {
return json::array({
{
{
"type",
"function"
},
{
"function",
{
{
"name",
"calculator"
},
{
"description",
"执行数学计算。用户要求计算、运算或者任务中需要得到准确数学结果时使用。当前工具支持两个数字之间的加、减、乘、除。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
{
{
"expression",
{
{
"type",
"string"
},
{
"description",
"数学表达式,例如 12345 * 6789"
}
}
}
}
},
{
"required",
json::array({
"expression"
})
}
}
}
}
}
},
{
{
"type",
"function"
},
{
"function",
{
{
"name",
"get_current_time"
},
{
"description",
"返回运行 Agent 的计算机当前本地时间。询问当前日期和时间,或者其他任务依赖真实当前时间时使用。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
json::object()
}
}
}
}
}
},
{
{
"type",
"function"
},
{
"function",
{
{
"name",
"save_note"
},
{
"description",
"把文字追加保存到当前程序目录的 notes.txt 文件。用户明确要求保存、记录或者持久化某个结果时使用。"
},
{
"parameters",
{
{
"type",
"object"
},
{
"properties",
{
{
"content",
{
{
"type",
"string"
},
{
"description",
"准备保存到 notes.txt 中的完整内容"
}
}
}
}
},
{
"required",
json::array({
"content"
})
}
}
}
}
}
}
});
}
/**
* ==================================================
* Tool Executor
* ==================================================
*/
std::string executeTool(
const std::string& name,
const json& arguments
) {
if (
name
==
"calculator"
) {
return calculator(
arguments.value(
"expression",
""
)
);
}
if (
name
==
"get_current_time"
) {
return getCurrentTime();
}
if (
name
==
"save_note"
) {
return saveNote(
arguments.value(
"content",
""
)
);
}
return
"Tool Error:不存在工具 "
+ name;
}
/**
* ==================================================
* Agent Class
* ==================================================
*/
class Agent {
private:
json messages;
public:
Agent() {
messages =
json::array();
messages.push_back({
{
"role",
"system"
},
{
"content",
SYSTEM_PROMPT
}
});
}
/**
* 一次用户任务
*/
std::string run(
const std::string& userInput
) {
/**
* Conversation Memory
*
* 不清空历史消息。
*/
messages.push_back({
{
"role",
"user"
},
{
"content",
userInput
}
});
/**
* Agent Loop
*/
for (
int step = 1;
step <= MAX_AGENT_STEPS;
++step
) {
std::cout
<< std::endl
<< "[Agent Step "
<< step
<< "]"
<< std::endl;
json request;
request["model"] =
MODEL;
request["messages"] =
messages;
request["tools"] =
buildTools();
request["tool_choice"] =
"auto";
json response =
postJson(
request
);
if (
response.contains(
"error"
)
) {
throw std::runtime_error(
response.dump(2)
);
}
json assistant =
response[
"choices"
][0][
"message"
];
messages.push_back(
assistant
);
/**
* Claude 没有继续调用工具
*
* Agent Task Finished
*/
if (
!assistant.contains(
"tool_calls"
)
||
assistant[
"tool_calls"
].empty()
) {
if (
assistant.contains(
"content"
)
&&
!assistant[
"content"
].is_null()
) {
return assistant[
"content"
].get<
std::string
>();
}
return
"Agent 已完成。";
}
/**
* Tool Calls
*/
for (
const auto& toolCall :
assistant[
"tool_calls"
]
) {
std::string name =
toolCall[
"function"
][
"name"
];
std::string rawArguments =
toolCall[
"function"
][
"arguments"
].get<
std::string
>();
json arguments;
try {
arguments =
json::parse(
rawArguments
);
}
catch (...) {
arguments =
json::object();
}
std::cout
<< "[Agent] Tool:"
<< name
<< std::endl;
std::cout
<< "[Agent] Args:"
<< arguments.dump(2)
<< std::endl;
std::string toolResult;
try {
toolResult =
executeTool(
name,
arguments
);
}
catch (
const std::exception& e
) {
toolResult =
"Tool Error:"
+
std::string(
e.what()
);
}
std::cout
<< "[Tool] Result:"
<< toolResult
<< std::endl;
messages.push_back({
{
"role",
"tool"
},
{
"tool_call_id",
toolCall[
"id"
]
},
{
"content",
toolResult
}
});
}
}
throw std::runtime_error(
"Agent 已达到最大执行步数 "
+
std::to_string(
MAX_AGENT_STEPS
)
);
}
};
/**
* ==================================================
* Main CLI
* ==================================================
*/
int main() {
curl_global_init(
CURL_GLOBAL_DEFAULT
);
Agent agent;
std::cout
<< "===================================="
<< std::endl;
std::cout
<< "Claude Opus 5 C++ AI Agent"
<< std::endl;
std::cout
<< "API: "
<< API_URL
<< std::endl;
std::cout
<< "Model: "
<< MODEL
<< std::endl;
std::cout
<< "输入 exit / quit 退出"
<< std::endl;
std::cout
<< "===================================="
<< std::endl;
while (true) {
std::cout
<< std::endl
<< "你:";
std::string input;
std::getline(
std::cin,
input
);
if (
input.empty()
) {
continue;
}
if (
input == "exit"
||
input == "quit"
) {
std::cout
<< "Agent 已退出。"
<< std::endl;
break;
}
try {
std::string response =
agent.run(
input
);
std::cout
<< std::endl
<< "Agent:"
<< std::endl
<< response
<< std::endl;
}
catch (
const std::exception& e
) {
std::cerr
<< std::endl
<< "Agent Error:"
<< e.what()
<< std::endl;
}
}
curl_global_cleanup();
return 0;
}
十五、运行最终 Agent
编译:
mkdir build
cd build
cmake ..
cmake --build .
运行:
./claude_agent
输出:
====================================
Claude Opus 5 C++ AI Agent
API: https://api.chongplus.plus/v1/chat/completions
Model: claude-opus-5
输入 exit / quit 退出
====================================
你:
输入:
帮我计算 99876 * 6677
运行:
[Agent Step 1]
[Agent] Tool:calculator
[Agent] Args:
{
"expression": "99876 * 6677"
}
[Tool] Result:
666472052
第二轮:
[Agent Step 2]
最终:
Agent:
99876 × 6677 = 666472052。
十六、继续对话测试 Memory
不要关闭程序。
继续:
你:
把刚才的计算结果保存一下。
Claude 可以看到前面的:
Conversation History
所以知道:
刚才结果 = 666472052
然后:
[Agent Step 1]
[Agent] Tool:save_note
[Agent] Args:
{
"content": "99876 × 6677 = 666472052"
}
[Tool] Result:
笔记保存成功
最终:
Agent:
已经把刚才的计算结果保存到 notes.txt。
这就是:
Short-Term Memory
十七、再测试一个真正的多步骤任务
输入:
现在几点?
计算 98765 * 4321,
把计算结果和当前时间一起保存到笔记。
Agent 可能调用:
Agent Step 1
get_current_time
返回:
2026-08-14 12:47:10
然后:
Agent Step 2
calculator
返回:
426741565
然后:
Agent Step 3
save_note
参数:
{
"content": "时间:2026-08-14 12:47:10,98765 × 4321 = 426741565"
}
返回:
笔记保存成功
最后:
Agent Step 4
不再调用工具。
最终:
Agent:
任务完成。
当前时间:
2026-08-14 12:47:10
计算结果:
98765 × 4321 = 426741565
以上信息已经保存到 notes.txt。
这就是一个非常标准的:
Multi-Step Agent
十八、现在再看 Agent 架构
我们的完整 C++ 程序现在已经有:
┌──────────────────────────────┐
│ User │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Conversation │
│ Memory │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Claude Opus 5 │
│ │
│ Reason / Decision │
└──────────────┬───────────────┘
│
│ Tool Call
▼
┌──────────────────────────────┐
│ Tool Executor │
├──────────────────────────────┤
│ calculator │
│ get_current_time │
│ save_note │
└──────────────┬───────────────┘
│
│ Tool Result
▼
┌──────────────────────────────┐
│ Claude Opus 5 │
└──────────────┬───────────────┘
│
Need More Tools?
│ │
YES NO
│ │
▼ ▼
Tool Call Final Answer
十九、Agent 最核心的五个组件
现在再看:
Agent
=
Model
+
Prompt
+
Tools
+
Memory
+
Loop
就很好理解了。
Model
我们的 Model:
Claude Opus 5
API model ID:
claude-opus-5
Anthropic 当前官方文档显示 Claude Opus 5 针对深度推理、Agentic Coding、长时间多步骤任务和工具使用进行了重点增强。
它相当于:
Agent 的大脑
Prompt
我们写:
SYSTEM_PROMPT
定义:
你是谁?
你有哪些工具?
什么时候调用?
什么情况下继续?
什么时候结束?
Tools
我们的工具:
calculator
get_current_time
save_note
相当于:
Agent 的手和脚
模型只能生成 Token。
Tools 才能让 AI:
真正执行操作。
官方 Claude 工具定义也强调了工具的:
name
description
input schema
其中工具描述越清楚,模型越容易正确选择和使用工具。
二十、Tools 可以继续变成什么?
现在只有:
calculator
time
note
但是原理完全一样。
可以继续写:
readFile()
对应:
read_file
可以写:
writeFile()
对应:
write_file
可以写:
runCommand()
对应:
run_shell
可以写:
queryDatabase()
对应:
query_database
可以写:
searchWeb()
对应:
search_web
然后 Agent 就可以变成:
Coding Agent
拥有:
read_file
write_file
list_files
run_command
git_diff
git_status
用户:
帮我检查这个 C++ 项目为什么编译失败。
Claude:
list_files
↓
read_file
↓
run_command
↓
看到 Compiler Error
↓
read_file
↓
write_file
↓
run_command
↓
测试通过
这就是:
Coding Agent
最基础的工作方式。
二十一、还可以做 DevOps Agent
工具:
docker_ps
docker_logs
restart_container
read_nginx_config
curl_url
check_port
用户:
为什么 api.silicogrove.com 返回 502?
Agent:
检查 Nginx
↓
检查 Docker
↓
检查端口
↓
查看日志
↓
发现 upstream 不通
↓
给出原因
如果再给予:
restart_container
权限。
它甚至可以:
发现服务挂了
↓
重启 Container
↓
再次 curl
↓
验证恢复
这就开始成为:
DevOps Agent
二十二、Agent 和传统 C++ 程序最大的区别
传统 C++:
auto result =
calculator(
"123 * 456"
);
saveNote(
result
);
流程完全由:
程序员
决定。
也就是:
calculator
↓
save_note
但 Agent:
while (true) {
auto decision =
claude(messages);
if (
decision.toolCalls
) {
executeTool();
continue;
}
return answer;
}
决定:
下一步执行什么
的不再完全是 C++。
而是:
Claude Opus 5
所以 AI Agent 一个非常重要的变化就是:
模型开始参与软件执行流程的控制。
二十三、Agent 与 Workflow
如果代码:
auto data =
search();
auto article =
writeArticle(
data
);
auto image =
generateImage(
article
);
publish(
article,
image
);
这是:
Workflow
程序员已经规定:
搜索
↓
文章
↓
图片
↓
发布
Agent 则是:
用户:
帮我做一篇今天 AI 行业的重要新闻。
Claude 自己决定:
要不要搜索?
搜索什么?
资料够不够?
需不需要第二次搜索?
什么时候开始写?
要不要生成图片?
还缺什么?
可以简单理解:
Workflow
程序控制流程。
Agent
模型参与控制流程。
生产系统通常会:
Agent
+
Workflow
一起使用。
二十四、为什么一定要限制 MAX_AGENT_STEPS?
我们代码里面:
const int MAX_AGENT_STEPS =
20;
非常重要。
如果直接:
while (true)
理论上 Claude 可能:
Tool
↓
Tool
↓
Tool
↓
Tool
↓
Tool
一直继续。
结果:
Token 不断消耗
API 不断调用
任务永远不结束
所以生产环境至少要有:
MAX_STEPS
MAX_TOKENS
TIMEOUT
MAX_TOOL_CALLS
RATE LIMIT
二十五、不要直接给 Agent Root 权限
这个也非常重要。
比如以后增加:
run_shell
千万不要直接:
system(
command.c_str()
);
然后允许 Claude 任意执行:
rm -rf /
DROP DATABASE
shutdown
docker rm
生产 Agent 至少需要:
Tool 白名单
命令白名单
参数校验
Sandbox
目录隔离
权限控制
执行超时
Audit Log
Human Confirmation
尤其是:
删除数据
转账
付款
部署
数据库修改
服务器命令
这种操作。
最好:
Claude:
我准备执行:
DELETE FROM users
是否确认?
用户:
确认
程序才真正调用工具。
这就是:
Human in the Loop
二十六、为什么这篇没有直接用 Anthropic 原生协议?
Anthropic 原生 Messages API 的 Tool Schema 形式主要是:
{
"name": "calculator",
"description": "...",
"input_schema": {
"type": "object"
}
}
官方文档目前也是按照 name + description + input_schema 定义 client-side tools。
但本文为了:
Python
Node.js
C++
几个版本保持统一,也方便中转平台接入,所以使用:
OpenAI Compatible API
也就是:
POST /v1/chat/completions
Tools:
{
"type": "function",
"function": {
"name": "calculator",
"parameters": {}
}
}
这样你无论换:
GPT
Claude
Gemini
DeepSeek
只要你的中转平台实现了统一兼容协议,上层 Agent Runtime 就可以尽量保持一致。
这也是统一 API 协议很有价值的地方。
二十七、Claude Opus 5 为什么适合 Agent?
Anthropic 当前官方文档对 Claude Opus 5 的重点描述就包括:
Deep reasoning
Agentic coding
Long-horizon tasks
Multi-tool work
而且当前默认带有 thinking,并提供不同 effort 档位来控制推理投入。
这恰好对应 Agent 最容易遇到的问题:
不是回答一次问题。
而是连续工作很多步。
例如:
读代码
↓
修改代码
↓
运行
↓
报错
↓
分析
↓
重新修改
↓
测试
↓
继续修复
这种任务:
Tool Calling
+
Long Context
+
Long-Horizon Reasoning
才真正能发挥 Agent 的价值。
二十八、最终我们到底写出了什么?
回头看。
我们没有使用:
LangChain
LangGraph
CrewAI
AutoGen
只有:
C++17
libcurl
JSON
Claude Opus 5
但是已经实现:
用户输入
↓
Conversation Memory
↓
Claude Opus 5
↓
Tool Decision
↓
Tool Arguments
↓
C++ Tool Executor
↓
Tool Result
↓
Conversation Context
↓
Claude Opus 5
↓
继续 Tool Call
↓
或者 Final Answer
这已经是:
一个最小可用 Agent Runtime。
二十九、真正要记住的代码其实只有这一段
如果整篇文章只记一段代码:
for (
int step = 0;
step < MAX_STEPS;
++step
) {
auto response =
callClaude(
messages
);
messages.push_back(
response
);
if (
!response.hasToolCalls()
) {
return
response.answer();
}
for (
auto& call :
response.toolCalls()
) {
auto result =
executeTool(
call
);
messages.push_back(
result
);
}
}
换成人话:
让 AI 思考
↓
AI 决定行动
↓
C++ 执行动作
↓
AI 查看结果
↓
继续思考
↓
继续行动
↓
直到完成
这就是:
Agent
三十、总结
今天我们使用:
C++17
从零实现了:
Claude Opus 5 AI Agent
模型:
claude-opus-5
API:
https://api.chongplus.plus
协议:
OpenAI Compatible API
整个 Agent 最核心的组成:
AI Agent
=
Model
+
Prompt
+
Tools
+
Memory
+
Agent Loop
其中:
Claude Opus 5
负责:
理解
分析
决策
选择工具
生成参数
判断下一步
而:
C++
负责:
HTTP
状态管理
Memory
执行 Tool
权限
异常处理
控制 Agent Loop
最大的变化不是:
AI 可以回答问题了。
而是:
AI 可以根据目标决定下一步应该执行什么。
当 Claude 第一次返回:
我要调用 calculator
C++ 执行:
83810205
再把结果返回 Claude。
Claude 又决定:
我要调用 save_note
最后才回答:
任务完成。
这时候:
它就已经不是一个普通聊天机器人,而是真正开始成为一个 Agent。
接下来如果继续给它增加:
文件系统
Shell
Git
GitHub
Browser
Database
HTTP
Docker
Kubernetes
搜索
图片生成
视频生成
这个 C++ Mini Agent 就能进一步发展成:
Coding Agent
DevOps Agent
Research Agent
Browser Agent
Data Agent
Enterprise Agent
Agent 工程真正难的部分,也会逐渐从:
“怎么调用一个 Tool?”
变成:
“怎么让模型在几十甚至几百个步骤中,
稳定、
安全、
可恢复、
低成本地完成任务?”
到了这里,才是真正的 AI Agent 工程。
一起成为勇猛精进的人类。
更多推荐


所有评论(0)