first commit

main
hkr04 2025-03-10 02:38:39 +08:00
commit 75f016659e
36 changed files with 2776 additions and 0 deletions

106
.gitignore vendored 100644
View File

@ -0,0 +1,106 @@
# Extensions
*.a
*.bat
*.bin
*.d
*.dll
*.dot
*.etag
*.exe
*.gcda
*.gcno
*.gcov
*.gguf
*.gguf.json
*.lastModified
*.log
*.metallib
*.o
*.so
*.tmp
# IDE / OS
.cache/
.ccls-cache/
.direnv/
.DS_Store
.envrc
.idea/
.swiftpm
.vs/
.vscode/
nppBackup
# Coverage
gcovr-report/
lcov-report/
# Build Artifacts
tags
.build/
build*
!build-info.cmake
!build-info.cpp.in
!build-info.sh
!build.zig
!docs/build.md
/libllama.so
/llama-*
/vulkan-shaders-gen
android-ndk-*
arm_neon.h
cmake-build-*
CMakeSettings.json
compile_commands.json
ggml-metal-embed.metal
llama-batched-swift
/rpc-server
out/
tmp/
autogen-*.md
# CI
!.github/workflows/*.yml
# Models
models/*
models-mnt
!models/.editorconfig
# Zig
# Logs
# Examples
# Server Web UI temporary files
node_modules
examples/server/webui/dist
# Python
/.venv
__pycache__/
*/poetry.lock
poetry.toml
# Nix
/result
# Test binaries
# Scripts
# Test models for lora adapters
# Local scripts

44
CMakeLists.txt 100644
View File

@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 3.10)
project(OpenManusCpp VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# OpenSSL3.0.0
find_package(OpenSSL REQUIRED)
if(OPENSSL_FOUND)
message(STATUS "OpenSSL found: ${OPENSSL_VERSION}")
message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
message(STATUS "OpenSSL libraries: ${OPENSSL_LIBRARIES}")
include_directories(${OPENSSL_INCLUDE_DIR})
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT)
else()
message(FATAL_ERROR "OpenSSL not found. Please install OpenSSL development libraries.")
endif()
# MCP
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/mcp)
#
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/mcp/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/mcp/common)
#
find_package(Threads REQUIRED)
#
file(GLOB_RECURSE SOURCES
"src/*.cpp"
"src/*.cc"
)
#
add_executable(openmanus_cpp ${SOURCES} main.cpp)
#
target_link_libraries(openmanus_cpp PRIVATE Threads::Threads mcp ${OPENSSL_LIBRARIES})
#
install(TARGETS openmanus_cpp DESTINATION bin)

204
README.md 100644
View File

@ -0,0 +1,204 @@
# OpenManus C++版本
这是OpenManus项目的C++实现版本旨在提供与原始Python版本相同的功能。
## 项目结构
```
cpp/
├── include/ # 头文件目录
│ ├── agent_base.h # 代理基类
│ ├── manus.h # Manus代理
│ ├── tool_base.h # 工具基类
│ ├── tool_call.h # 工具调用类
│ ├── tool_call_agent.h # 工具调用代理
│ ├── tool_collection.h # 工具集合类
│ ├── flow/ # 流程头文件目录
│ │ ├── base_flow.h # 流程基类
│ │ ├── planning_flow.h # 规划流程
│ │ └── flow_factory.h # 流程工厂
│ └── tools/ # 工具头文件目录
│ └── terminate.h # 终止工具
├── src/ # 源文件目录
│ ├── agent_base.cpp # 代理基类实现
│ ├── manus.cpp # Manus代理实现
│ ├── tool_base.cpp # 工具基类实现
│ ├── tool_call.cpp # 工具调用类实现
│ ├── tool_call_agent.cpp # 工具调用代理实现
│ ├── tool_collection.cpp # 工具集合类实现
│ ├── flow/ # 流程实现目录
│ │ ├── base_flow.cpp # 流程基类实现
│ │ ├── planning_flow.cpp # 规划流程实现
│ │ └── flow_factory.cpp # 流程工厂实现
│ └── tools/ # 工具实现目录
│ └── terminate.cpp # 终止工具实现
├── server/ # MCP服务器目录
│ ├── mcp_server_main.cpp # MCP服务器主程序
│ └── CMakeLists.txt # 服务器构建文件
├── main.cpp # 主程序入口
├── mcp/ # MCP协议库
└── CMakeLists.txt # CMake构建文件
```
## 已完成工作
1. **基础框架搭建**
- 创建了基本的项目结构和CMakeLists.txt文件
- 实现了基础代理类(AgentBase)
- 实现了工具集合类(ToolCollection)
- 实现了工具基类(ToolBase)
- 实现了工具调用类(ToolCall)
- 实现了工具调用代理类(ToolCallAgent)
- 实现了Manus代理类
2. **流程层实现**
- 实现了流程基类(BaseFlow)
- 实现了规划流程(PlanningFlow)
- 实现了流程工厂(FlowFactory)
- 修改了主程序,使用流程层执行任务
3. **工具实现**
- 实现了终止工具(Terminate),用于终止代理执行
- 实现了Python执行工具(PythonExecute)可以执行Python代码
4. **MCP服务器**
- 实现了基本的MCP服务器
- 实现了PythonExecute工具可以执行Python代码
5. **LLM集成**
- 使用httplib.h实现了与LLM API的通信
- 实现了think方法能够调用LLM API获取下一步行动
- 实现了工具调用的解析和执行
- 添加了Authorization头部支持API密钥认证
6. **配置系统**
- 实现了TOML格式的配置文件读取
- 支持从配置文件中读取LLM API、工具和代理等参数
- 支持在运行时指定配置文件路径
7. **编译与运行**
- 成功编译并运行了基本框架
- 解决了与MCP协议库的集成问题
## 未完成工作
1. **工具实现**
- Google搜索工具(GoogleSearch)
- 浏览器使用工具(BrowserUseTool)
- 文件保存工具(FileSaver)
- 其他工具...
2. **LLM集成完善**
- 添加更多的LLM模型支持
- 实现流式响应处理
- 添加更多的提示模板
3. **MCP协议完整集成**
- 完善客户端与MCP服务器的通信
- 实现完整的请求-响应流程
4. **错误处理与日志**
- 添加更完善的错误处理机制
- 实现日志记录功能
5. **测试与文档**
- 编写单元测试
- 编写集成测试
- 完善API文档
## 配置文件
OpenManus使用TOML格式的配置文件来配置LLM API、工具和代理等参数。默认配置文件为`config.toml`,位于可执行文件所在目录。
### 配置文件示例
```toml
# OpenManus配置文件
[llm]
# LLM API主机
host = "localhost"
# LLM API端口
port = 8000
# LLM API路径
path = "/chat/completions"
# LLM API密钥
api_key = "your_api_key_here"
# LLM模型名称
model = "gpt-3.5-turbo"
# 是否启用流式响应
stream = false
[tools]
# 是否启用Python执行工具
enable_python_execute = true
# 是否启用Google搜索工具
enable_google_search = false
# 是否启用浏览器使用工具
enable_browser_use = false
# 是否启用文件保存工具
enable_file_saver = false
[agent]
# 最大步骤数
max_steps = 30
# 系统提示
system_prompt = "你是Manus一个通用的智能助手可以使用多种工具解决各种任务。"
# 下一步提示
next_step_prompt = "请思考下一步行动,使用可用的工具来解决用户的问题。"
```
### 运行时指定配置文件
可以在运行时通过命令行参数指定配置文件路径:
```bash
./openmanus_cpp /path/to/your/config.toml
```
## 构建与运行
### 构建主程序
```bash
cd cpp
mkdir -p build
cd build
cmake ..
make
```
### 运行主程序
```bash
./openmanus_cpp
```
### 构建MCP服务器
```bash
cd cpp/server
mkdir -p build
cd build
cmake ..
make
```
### 运行MCP服务器
```bash
./openmanus_mcp_server
```
## 依赖
- C++17
- CMake 3.10+
- nlohmann/json (包含在mcp/common目录中)
- httplib.h (包含在项目中)
- Python 3 (用于PythonExecute工具)
- LLM API服务 (如OpenAI API或本地部署的LLM服务)
## 许可证
与原始OpenManus项目相同的许可证。

14
config.toml 100644
View File

@ -0,0 +1,14 @@
[llm]
model = "deepseek-chat"
base_url = "https://api.deepseek.com"
api_key = "sk-93c5bfcb920c4a8aa345791d429b8536"
max_tokens = 4096
provider = "openrouter"
[agent]
# 最大步骤数
max_steps = 30
# 最大连续工具调用次数
max_consecutive_tool_calls = 10
# 是否每次运行都清空历史
clear_history_each_run = false

18
config.toml.backup 100644
View File

@ -0,0 +1,18 @@
[llm]
model = "deepseek-chat"
base_url = "https://api.deepseek.com"
api_key = "sk-93c5bfcb920c4a8aa345791d429b8536"
max_tokens = 4096
provider = "openrouter"
[agent]
# 最大步骤数
max_steps = 30
# 最大连续工具调用次数
max_consecutive_tool_calls = 10
# 系统提示
system_prompt = "你是Manus一个通用的智能助手可以使用多种工具解决各种任务。你可以使用规划工具来创建和管理结构化计划使用Python执行工具来执行Python代码。请注意以下重要规则\n1. 你最多只能连续调用工具5次超过限制将被强制停止。\n2. 当任务完成时你必须调用Terminate工具来结束执行。\n3. 如果你已经获得了足够的信息来回答用户的问题请提供答案然后调用Terminate工具。\n4. 不要无限循环调用工具,确保每次工具调用都有明确的目的。"
# 下一步提示
next_step_prompt = "请思考下一步行动使用可用的工具来解决用户的问题。如果你已经获得了足够的信息请直接回答问题然后调用Terminate工具来结束执行。记住任务完成后必须调用Terminate工具。"
# 是否每次运行都清空历史
clear_history_each_run = false

View File

@ -0,0 +1,58 @@
#ifndef OPENMANUS_AGENT_BASE_H
#define OPENMANUS_AGENT_BASE_H
#include <string>
#include <vector>
#include <memory>
#include "tool_collection.h"
#include "mcp/include/mcp_message.h"
namespace openmanus {
/**
* @class AgentBase
* @brief
*/
class AgentBase : public std::enable_shared_from_this<AgentBase> {
public:
AgentBase(const std::string& name, const std::string& description);
virtual ~AgentBase() = default;
/**
* @brief
* @param prompt
* @return
*/
virtual std::string run(const std::string& prompt) = 0;
/**
* @brief
* @param tool_name
* @param params
* @return
*/
virtual mcp::json executeToolCall(const std::string& tool_name, const mcp::json& params);
/**
* @brief
* @return
*/
std::string getName() const { return name_; }
/**
* @brief
* @return
*/
std::string getDescription() const { return description_; }
protected:
std::string name_;
std::string description_;
std::string system_prompt_;
std::string next_step_prompt_;
std::unique_ptr<ToolCollection> available_tools_;
};
} // namespace openmanus
#endif // OPENMANUS_AGENT_BASE_H

72
include/config.h 100644
View File

@ -0,0 +1,72 @@
#ifndef OPENMANUS_CONFIG_H
#define OPENMANUS_CONFIG_H
#include <string>
#include <map>
#include <fstream>
#include <sstream>
namespace openmanus {
/**
* @class Config
* @brief TOML
*/
class Config {
public:
/**
* @brief
* @param config_file
*/
Config(const std::string& config_file = "config.toml");
/**
* @brief
* @param config_file
* @return
*/
bool load(const std::string& config_file);
/**
* @brief
* @param section
* @param key
* @param default_value
* @return
*/
std::string getString(const std::string& section, const std::string& key, const std::string& default_value = "") const;
/**
* @brief
* @param section
* @param key
* @param default_value
* @return
*/
int getInt(const std::string& section, const std::string& key, int default_value = 0) const;
/**
* @brief
* @param section
* @param key
* @param default_value
* @return
*/
double getDouble(const std::string& section, const std::string& key, double default_value = 0.0) const;
/**
* @brief
* @param section
* @param key
* @param default_value
* @return
*/
bool getBool(const std::string& section, const std::string& key, bool default_value = false) const;
private:
std::map<std::string, std::map<std::string, std::string>> config_data_;
};
} // namespace openmanus
#endif // OPENMANUS_CONFIG_H

View File

@ -0,0 +1,77 @@
#ifndef OPENMANUS_FLOW_BASE_FLOW_H
#define OPENMANUS_FLOW_BASE_FLOW_H
#include <string>
#include <memory>
#include <unordered_map>
#include <vector>
#include "../agent_base.h"
namespace openmanus {
/**
* @enum FlowType
* @brief
*/
enum class FlowType {
PLANNING
};
/**
* @class BaseFlow
* @brief
*/
class BaseFlow {
public:
/**
* @brief
* @param agent
*/
BaseFlow(std::shared_ptr<AgentBase> agent);
/**
* @brief
* @param agents
*/
BaseFlow(const std::vector<std::shared_ptr<AgentBase>>& agents);
/**
* @brief
*/
virtual ~BaseFlow() = default;
/**
* @brief
* @param input_text
* @return
*/
virtual std::string execute(const std::string& input_text) = 0;
/**
* @brief
* @return
*/
std::shared_ptr<AgentBase> getPrimaryAgent() const;
/**
* @brief
* @param key
* @return
*/
std::shared_ptr<AgentBase> getAgent(const std::string& key) const;
/**
* @brief
* @param key
* @param agent
*/
void addAgent(const std::string& key, std::shared_ptr<AgentBase> agent);
protected:
std::unordered_map<std::string, std::shared_ptr<AgentBase>> agents_;
std::string primary_agent_key_;
};
} // namespace openmanus
#endif // OPENMANUS_FLOW_BASE_FLOW_H

View File

@ -0,0 +1,43 @@
#ifndef OPENMANUS_FLOW_FLOW_FACTORY_H
#define OPENMANUS_FLOW_FLOW_FACTORY_H
#include <string>
#include <memory>
#include <vector>
#include "base_flow.h"
#include "planning_flow.h"
namespace openmanus {
/**
* @class FlowFactory
* @brief
*/
class FlowFactory {
public:
/**
* @brief
* @param flow_type
* @param agent
* @return
*/
static std::shared_ptr<BaseFlow> createFlow(
FlowType flow_type,
std::shared_ptr<AgentBase> agent
);
/**
* @brief
* @param flow_type
* @param agents
* @return
*/
static std::shared_ptr<BaseFlow> createFlow(
FlowType flow_type,
const std::vector<std::shared_ptr<AgentBase>>& agents
);
};
} // namespace openmanus
#endif // OPENMANUS_FLOW_FLOW_FACTORY_H

View File

@ -0,0 +1,64 @@
#ifndef OPENMANUS_FLOW_PLANNING_FLOW_H
#define OPENMANUS_FLOW_PLANNING_FLOW_H
#include <string>
#include <memory>
#include <unordered_map>
#include "base_flow.h"
namespace openmanus {
/**
* @class PlanningFlow
* @brief 使
*/
class PlanningFlow : public BaseFlow {
public:
/**
* @brief
* @param agent
*/
PlanningFlow(std::shared_ptr<AgentBase> agent);
/**
* @brief
* @param agents
*/
PlanningFlow(const std::vector<std::shared_ptr<AgentBase>>& agents);
/**
* @brief
* @param input_text
* @return
*/
virtual std::string execute(const std::string& input_text) override;
private:
/**
* @brief
* @return
*/
std::string getPlanStatus();
/**
* @brief
* @param step_id ID
* @param status
* @return
*/
bool updateStepStatus(const std::string& step_id, const std::string& status);
/**
* @brief
* @return
*/
int getCurrentStepIndex();
private:
std::string active_plan_id_;
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> step_execution_tracker_;
};
} // namespace openmanus
#endif // OPENMANUS_FLOW_PLANNING_FLOW_H

35
include/manus.h 100644
View File

@ -0,0 +1,35 @@
#ifndef OPENMANUS_MANUS_H
#define OPENMANUS_MANUS_H
#include <string>
#include <memory>
#include "tool_call_agent.h"
namespace openmanus {
/**
* @class Manus
* @brief Manus
*/
class Manus : public ToolCallAgent {
public:
/**
* @brief
* @param config_file
*/
Manus(const std::string& config_file = "config.toml");
virtual ~Manus() = default;
/**
* @brief
*/
void initialize();
private:
// 添加特定于Manus的工具
void addManusTools();
};
} // namespace openmanus
#endif // OPENMANUS_MANUS_H

View File

@ -0,0 +1,54 @@
#ifndef OPENMANUS_TOOL_BASE_H
#define OPENMANUS_TOOL_BASE_H
#include <string>
#include <vector>
#include <functional>
#include "mcp/include/mcp_message.h"
#include "mcp/include/mcp_tool.h"
namespace openmanus {
/**
* @class ToolBase
* @brief
*/
class ToolBase {
public:
ToolBase(const std::string& name, const std::string& description);
virtual ~ToolBase() = default;
/**
* @brief
* @param params
* @return
*/
virtual mcp::json execute(const mcp::json& params) = 0;
/**
* @brief
* @return
*/
std::string getName() const { return name_; }
/**
* @brief
* @return
*/
std::string getDescription() const { return description_; }
/**
* @brief
* @return
*/
mcp::json getParameters() const { return parameters_; }
protected:
std::string name_;
std::string description_;
mcp::json parameters_; // 参数定义使用JSON Schema格式
};
} // namespace openmanus
#endif // OPENMANUS_TOOL_BASE_H

View File

@ -0,0 +1,70 @@
#ifndef OPENMANUS_TOOL_CALL_H
#define OPENMANUS_TOOL_CALL_H
#include <string>
#include "mcp/include/mcp_message.h"
namespace openmanus {
/**
* @class ToolCall
* @brief
*/
class ToolCall {
public:
ToolCall(const std::string& id, const std::string& name, const mcp::json& params);
~ToolCall() = default;
/**
* @brief ID
* @return ID
*/
std::string getId() const { return id_; }
/**
* @brief
* @return
*/
std::string getName() const { return name_; }
/**
* @brief
* @return
*/
mcp::json getParams() const { return params_; }
/**
* @brief
* @param result
*/
void setResult(const std::string& result) { result_ = result; }
/**
* @brief
* @return
*/
std::string getResult() const { return result_; }
/**
* @brief JSON
* @param json JSON
* @return
*/
static ToolCall fromJson(const mcp::json& json);
/**
* @brief JSON
* @return JSON
*/
mcp::json toJson() const;
private:
std::string id_; // 工具调用ID
std::string name_; // 工具名称
mcp::json params_; // 工具参数
std::string result_; // 执行结果
};
} // namespace openmanus
#endif // OPENMANUS_TOOL_CALL_H

View File

@ -0,0 +1,179 @@
#ifndef OPENMANUS_TOOL_CALL_AGENT_H
#define OPENMANUS_TOOL_CALL_AGENT_H
#include <string>
#include <vector>
#include <memory>
#include "agent_base.h"
#include "tool_call.h"
#include "config.h"
#include "mcp/common/httplib.h"
namespace openmanus {
// 消息类型枚举
enum class MessageRole {
SYSTEM,
USER,
ASSISTANT,
TOOL
};
// 消息结构体
struct Message {
MessageRole role;
std::string content;
std::string name; // 用于工具消息的工具名称
std::string tool_call_id; // 用于工具消息的工具调用ID
// 创建系统消息
static Message systemMessage(const std::string& content) {
return {MessageRole::SYSTEM, content, "", ""};
}
// 创建用户消息
static Message userMessage(const std::string& content) {
return {MessageRole::USER, content, "", ""};
}
// 创建助手消息
static Message assistantMessage(const std::string& content) {
return {MessageRole::ASSISTANT, content, "", ""};
}
// 创建工具消息
static Message toolMessage(const std::string& content, const std::string& name, const std::string& tool_call_id) {
return {MessageRole::TOOL, content, name, tool_call_id};
}
};
/**
* @class ToolCallAgent
* @brief
*/
class ToolCallAgent : public AgentBase {
public:
ToolCallAgent(const std::string& name, const std::string& description, const std::string& config_file = "config.toml");
virtual ~ToolCallAgent() = default;
/**
* @brief
* @param prompt
* @return
*/
virtual std::string run(const std::string& prompt) override;
/**
* @brief LLM API
* @param host LLM API
* @param port LLM API
* @param endpoint LLM API
*/
void setLLMAPIParams(const std::string& host, int port, const std::string& endpoint);
/**
* @brief LLM API
* @param provider "deepseek"
* @param base_url URL
* @param endpoint API
*/
void setCloudLLMAPIParams(const std::string& provider, const std::string& base_url, const std::string& endpoint);
/**
* @brief LLM API
* @param api_key LLM API
*/
void setLLMAPIKey(const std::string& api_key);
/**
* @brief
*/
void clearMessages();
/**
* @brief
* @param message
*/
void addMessage(const Message& message);
/**
* @brief
* @return
*/
const std::vector<Message>& getMessages() const { return messages_; }
protected:
/**
* @brief
* @return
*/
virtual bool think();
/**
* @brief
* @return
*/
virtual std::string act();
/**
* @brief
* @param tool_call
* @return
*/
virtual std::string executeToolCall(const ToolCall& tool_call);
/**
* @brief
* @param name
* @param result
* @return
*/
virtual bool handleSpecialTool(const std::string& name, const std::string& result);
/**
* @brief
* @return
*/
virtual bool shouldFinishExecution();
/**
* @brief
* @param name
* @return
*/
virtual bool isSpecialTool(const std::string& name);
/**
* @brief LLM API
* @param messages
* @return JSON
*/
virtual mcp::json callLLMAPI(const std::vector<Message>& messages);
protected:
std::vector<Message> messages_; // 消息历史
std::vector<ToolCall> tool_calls_; // 工具调用列表
int max_steps_; // 最大步骤数
int current_step_; // 当前步骤
bool should_clear_history_; // 是否应该清空历史
int max_consecutive_tool_calls_; // 最大连续工具调用次数
int consecutive_tool_calls_; // 当前连续工具调用次数
// 配置
Config config_; // 配置
// LLM API相关
std::string llm_api_host_; // LLM API主机
int llm_api_port_; // LLM API端口
std::string llm_api_base_url_; // LLM API基础URL
std::string llm_api_endpoint_; // LLM API端点
std::string llm_api_key_; // LLM API密钥
std::string llm_model_; // LLM模型名称
int llm_max_tokens_; // LLM最大生成token数
std::string llm_provider_; // LLM提供商
std::unique_ptr<httplib::Client> http_client_; // HTTP客户端
};
} // namespace openmanus
#endif // OPENMANUS_TOOL_CALL_AGENT_H

View File

@ -0,0 +1,52 @@
#ifndef OPENMANUS_TOOL_COLLECTION_H
#define OPENMANUS_TOOL_COLLECTION_H
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include "tool_base.h"
namespace openmanus {
/**
* @class ToolCollection
* @brief
*/
class ToolCollection {
public:
ToolCollection();
~ToolCollection() = default;
/**
* @brief
* @param tool
*/
void addTool(std::unique_ptr<ToolBase> tool);
/**
* @brief
* @param name
* @return nullptr
*/
ToolBase* getTool(const std::string& name);
/**
* @brief
* @return
*/
std::vector<std::string> getToolNames() const;
/**
* @brief
* @return
*/
size_t size() const { return tools_.size(); }
private:
std::unordered_map<std::string, std::unique_ptr<ToolBase>> tools_;
};
} // namespace openmanus
#endif // OPENMANUS_TOOL_COLLECTION_H

View File

@ -0,0 +1,73 @@
#ifndef OPENMANUS_TOOLS_PLANNING_H
#define OPENMANUS_TOOLS_PLANNING_H
#include "../tool_base.h"
#include <string>
#include <vector>
#include <unordered_map>
namespace openmanus {
/**
* @class Planning
* @brief
*/
class Planning : public ToolBase {
public:
Planning();
virtual ~Planning() = default;
/**
* @brief
* @param params
* @return
*/
virtual mcp::json execute(const mcp::json& params) override;
private:
/**
* @brief
* @param params
* @return
*/
mcp::json createPlan(const mcp::json& params);
/**
* @brief
* @param params
* @return
*/
mcp::json getPlanStatus(const mcp::json& params);
/**
* @brief
* @param params
* @return
*/
mcp::json updateStepStatus(const mcp::json& params);
/**
* @brief
* @param params
* @return
*/
mcp::json addStep(const mcp::json& params);
private:
// 计划结构
struct Plan {
std::string id;
std::string title;
std::string description;
std::vector<std::string> step_ids;
std::unordered_map<std::string, std::string> step_descriptions;
std::unordered_map<std::string, std::string> step_statuses;
int current_step_index;
};
std::unordered_map<std::string, Plan> plans_;
};
} // namespace openmanus
#endif // OPENMANUS_TOOLS_PLANNING_H

View File

@ -0,0 +1,27 @@
#ifndef OPENMANUS_TOOLS_PYTHON_EXECUTE_H
#define OPENMANUS_TOOLS_PYTHON_EXECUTE_H
#include "../tool_base.h"
namespace openmanus {
/**
* @class PythonExecute
* @brief PythonPython
*/
class PythonExecute : public ToolBase {
public:
PythonExecute();
virtual ~PythonExecute() = default;
/**
* @brief Python
* @param params code
* @return
*/
virtual mcp::json execute(const mcp::json& params) override;
};
} // namespace openmanus
#endif // OPENMANUS_TOOLS_PYTHON_EXECUTE_H

View File

@ -0,0 +1,27 @@
#ifndef OPENMANUS_TOOLS_TERMINATE_H
#define OPENMANUS_TOOLS_TERMINATE_H
#include "../tool_base.h"
namespace openmanus {
/**
* @class Terminate
* @brief
*/
class Terminate : public ToolBase {
public:
Terminate();
virtual ~Terminate() = default;
/**
* @brief
* @param params
* @return
*/
virtual mcp::json execute(const mcp::json& params) override;
};
} // namespace openmanus
#endif // OPENMANUS_TOOLS_TERMINATE_H

72
main.cpp 100644
View File

@ -0,0 +1,72 @@
#include <iostream>
#include <string>
#include <memory>
#include "include/manus.h"
#include "include/flow/flow_factory.h"
#include "include/config.h"
int main(int argc, char* argv[]) {
try {
// 获取配置文件路径
std::string config_file = "config.toml";
if (argc > 1) {
config_file = argv[1];
}
std::cout << "使用配置文件: " << config_file << std::endl;
// 创建Manus代理实例
auto agent = std::make_shared<openmanus::Manus>(config_file);
// 读取配置文件
openmanus::Config config(config_file);
std::string provider = config.getString("llm", "provider", "local");
// 根据提供商设置LLM API参数
if (provider == "local") {
std::string host = config.getString("llm", "host", "localhost");
int port = config.getInt("llm", "port", 8000);
std::string path = config.getString("llm", "path", "/chat/completions");
agent->setLLMAPIParams(host, port, path);
} else {
std::string base_url = config.getString("llm", "base_url", "");
std::string path = config.getString("llm", "path", "/v1/chat/completions");
agent->setCloudLLMAPIParams(provider, base_url, path);
}
// 设置API密钥
std::string api_key = config.getString("llm", "api_key", "");
if (!api_key.empty()) {
agent->setLLMAPIKey(api_key);
}
while (true) {
std::string prompt;
std::cout << "输入你的提示 (或输入 'exit' 退出): ";
std::getline(std::cin, prompt);
if (prompt == "exit") {
std::cout << "再见!" << std::endl;
break;
}
std::cout << "正在处理你的请求..." << std::endl;
// 创建规划流程
auto flow = openmanus::FlowFactory::createFlow(
openmanus::FlowType::PLANNING,
agent
);
// 执行流程
std::string result = flow->execute(prompt);
std::cout << "处理结果: " << result << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
return 1;
}
return 0;
}

1
mcp 160000

@ -0,0 +1 @@
Subproject commit 175d66864253ab305d57524ca58edc4ecffb2ec9

0
request.json 100644
View File

View File

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.10)
project(OpenManusMCPServer VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# OpenSSL
find_package(OpenSSL REQUIRED)
if(OPENSSL_FOUND)
message(STATUS "OpenSSL found: ${OPENSSL_VERSION}")
message(STATUS "OpenSSL include directory: ${OPENSSL_INCLUDE_DIR}")
message(STATUS "OpenSSL libraries: ${OPENSSL_LIBRARIES}")
include_directories(${OPENSSL_INCLUDE_DIR})
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT)
else()
message(FATAL_ERROR "OpenSSL not found. Please install OpenSSL development libraries.")
endif()
# MCP
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../mcp ${CMAKE_CURRENT_BINARY_DIR}/mcp)
#
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../mcp/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../mcp/common)
#
find_package(Threads REQUIRED)
#
add_executable(openmanus_mcp_server mcp_server_main.cpp)
#
target_link_libraries(openmanus_mcp_server PRIVATE Threads::Threads mcp ${OPENSSL_LIBRARIES})
#
install(TARGETS openmanus_mcp_server DESTINATION bin)

View File

@ -0,0 +1,123 @@
/**
* @file mcp_server_main.cpp
* @brief OpenManus MCP
*
* OpenManusMCP
* PythonExecute
*/
#include "mcp/include/mcp_server.h"
#include "mcp/include/mcp_tool.h"
#include "mcp/include/mcp_resource.h"
#include <iostream>
#include <fstream>
#include <string>
#include <memory>
#include <filesystem>
#include <cstdio>
#include <array>
#include <stdexcept>
// 执行系统命令并返回输出
std::string exec_command(const std::string& cmd) {
std::array<char, 128> buffer;
std::string result;
// 打开管道,执行命令
FILE* pipe = popen(cmd.c_str(), "r");
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
// 读取命令输出
while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
result += buffer.data();
}
// 关闭管道
int status = pclose(pipe);
if (status == -1) {
throw std::runtime_error("pclose() failed!");
}
return result;
}
// PythonExecute工具处理函数
mcp::json python_execute_handler(const mcp::json& args) {
if (!args.contains("code")) {
std::cout << args.dump() << std::endl;
throw mcp::mcp_exception(mcp::error_code::invalid_params, "缺少'code'参数");
}
std::string code = args["code"];
// 创建临时Python文件
std::string temp_file = "/tmp/openmanus_temp.py";
// 写入Python代码到临时文件
std::ofstream out(temp_file);
if (!out) {
throw mcp::mcp_exception(mcp::error_code::internal_error, "无法创建临时Python文件");
}
out << code;
out.close();
try {
// 执行Python代码
std::string cmd = "python3 " + temp_file + " 2>&1";
std::string output = exec_command(cmd);
// 删除临时文件
std::filesystem::remove(temp_file);
if (output.empty()) {
output = "Executed successfully but w/o output. Maybe you should print more information.";
}
return {{
{"type", "text"},
{"text", output}
}};
} catch (const std::exception& e) {
// 删除临时文件
std::filesystem::remove(temp_file);
throw mcp::mcp_exception(mcp::error_code::internal_error,
"执行Python代码失败: " + std::string(e.what()));
}
}
int main() {
// 创建并配置服务器
mcp::server server("localhost", 8080);
server.set_server_info("OpenManusMCPServer", "2024-11-05");
// 设置服务器能力
mcp::json capabilities = {
{"tools", {{"listChanged", true}}},
{"resources", {{"listChanged", true}}}
};
server.set_capabilities(capabilities);
// 注册方法处理函数
server.register_method("ping", [](const mcp::json& params) {
return mcp::json{{"pong", true}};
});
// 注册PythonExecute工具
mcp::tool python_tool = mcp::tool_builder("PythonExecute")
.with_description("执行Python代码并返回结果")
.with_string_param("code", "要执行的Python代码", true)
.build();
server.register_tool(python_tool, python_execute_handler);
// 启动服务器
std::cout << "启动OpenManus MCP服务器地址: localhost:8080..." << std::endl;
std::cout << "按Ctrl+C停止服务器" << std::endl;
server.start(true); // 阻塞模式
return 0;
}

23
src/agent_base.cpp 100644
View File

@ -0,0 +1,23 @@
#include "include/agent_base.h"
#include <stdexcept>
namespace openmanus {
AgentBase::AgentBase(const std::string& name, const std::string& description)
: name_(name), description_(description) {
// 初始化工具集合
available_tools_ = std::make_unique<ToolCollection>();
}
mcp::json AgentBase::executeToolCall(const std::string& tool_name, const mcp::json& params) {
// 获取工具
ToolBase* tool = available_tools_->getTool(tool_name);
if (!tool) {
throw std::runtime_error("找不到工具: " + tool_name);
}
// 执行工具
return tool->execute(params);
}
} // namespace openmanus

117
src/config.cpp 100644
View File

@ -0,0 +1,117 @@
#include "include/config.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cctype>
namespace openmanus {
Config::Config(const std::string& config_file) {
load(config_file);
}
bool Config::load(const std::string& config_file) {
std::ifstream file(config_file);
if (!file.is_open()) {
std::cerr << "无法打开配置文件: " << config_file << std::endl;
return false;
}
std::string line;
std::string current_section;
while (std::getline(file, line)) {
// 去除前后空白
line.erase(0, line.find_first_not_of(" \t"));
line.erase(line.find_last_not_of(" \t") + 1);
// 跳过空行和注释
if (line.empty() || line[0] == '#') {
continue;
}
// 处理节
if (line[0] == '[' && line[line.length() - 1] == ']') {
current_section = line.substr(1, line.length() - 2);
continue;
}
// 处理键值对
size_t pos = line.find('=');
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
// 去除键和值的前后空白
key.erase(0, key.find_first_not_of(" \t"));
key.erase(key.find_last_not_of(" \t") + 1);
value.erase(0, value.find_first_not_of(" \t"));
value.erase(value.find_last_not_of(" \t") + 1);
// 处理引号
if (value.length() >= 2 && (value[0] == '"' && value[value.length() - 1] == '"' ||
value[0] == '\'' && value[value.length() - 1] == '\'')) {
value = value.substr(1, value.length() - 2);
}
config_data_[current_section][key] = value;
}
}
file.close();
return true;
}
std::string Config::getString(const std::string& section, const std::string& key, const std::string& default_value) const {
auto section_it = config_data_.find(section);
if (section_it != config_data_.end()) {
auto key_it = section_it->second.find(key);
if (key_it != section_it->second.end()) {
return key_it->second;
}
}
return default_value;
}
int Config::getInt(const std::string& section, const std::string& key, int default_value) const {
std::string value = getString(section, key, "");
if (!value.empty()) {
try {
return std::stoi(value);
} catch (...) {
// 转换失败,返回默认值
}
}
return default_value;
}
double Config::getDouble(const std::string& section, const std::string& key, double default_value) const {
std::string value = getString(section, key, "");
if (!value.empty()) {
try {
return std::stod(value);
} catch (...) {
// 转换失败,返回默认值
}
}
return default_value;
}
bool Config::getBool(const std::string& section, const std::string& key, bool default_value) const {
std::string value = getString(section, key, "");
if (!value.empty()) {
// 转换为小写
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c) { return std::tolower(c); });
if (value == "true" || value == "yes" || value == "1") {
return true;
} else if (value == "false" || value == "no" || value == "0") {
return false;
}
}
return default_value;
}
} // namespace openmanus

View File

@ -0,0 +1,50 @@
#include "include/flow/base_flow.h"
#include <stdexcept>
namespace openmanus {
BaseFlow::BaseFlow(std::shared_ptr<AgentBase> agent) {
if (agent) {
primary_agent_key_ = "default";
agents_[primary_agent_key_] = agent;
}
}
BaseFlow::BaseFlow(const std::vector<std::shared_ptr<AgentBase>>& agents) {
if (!agents.empty()) {
primary_agent_key_ = "agent0";
for (size_t i = 0; i < agents.size(); ++i) {
if (agents[i]) {
std::string key = "agent" + std::to_string(i);
agents_[key] = agents[i];
}
}
}
}
std::shared_ptr<AgentBase> BaseFlow::getPrimaryAgent() const {
auto it = agents_.find(primary_agent_key_);
if (it != agents_.end()) {
return it->second;
}
return nullptr;
}
std::shared_ptr<AgentBase> BaseFlow::getAgent(const std::string& key) const {
auto it = agents_.find(key);
if (it != agents_.end()) {
return it->second;
}
return nullptr;
}
void BaseFlow::addAgent(const std::string& key, std::shared_ptr<AgentBase> agent) {
if (agent) {
agents_[key] = agent;
if (agents_.size() == 1) {
primary_agent_key_ = key;
}
}
}
} // namespace openmanus

View File

@ -0,0 +1,30 @@
#include "include/flow/flow_factory.h"
#include <stdexcept>
namespace openmanus {
std::shared_ptr<BaseFlow> FlowFactory::createFlow(
FlowType flow_type,
std::shared_ptr<AgentBase> agent
) {
switch (flow_type) {
case FlowType::PLANNING:
return std::make_shared<PlanningFlow>(agent);
default:
throw std::runtime_error("未知的流程类型");
}
}
std::shared_ptr<BaseFlow> FlowFactory::createFlow(
FlowType flow_type,
const std::vector<std::shared_ptr<AgentBase>>& agents
) {
switch (flow_type) {
case FlowType::PLANNING:
return std::make_shared<PlanningFlow>(agents);
default:
throw std::runtime_error("未知的流程类型");
}
}
} // namespace openmanus

View File

@ -0,0 +1,170 @@
#include "include/flow/planning_flow.h"
#include <iostream>
#include <chrono>
#include <sstream>
#include <iomanip>
#include "mcp/include/mcp_message.h"
namespace openmanus {
PlanningFlow::PlanningFlow(std::shared_ptr<AgentBase> agent)
: BaseFlow(agent) {
// 生成计划ID
auto now = std::chrono::system_clock::now();
auto now_time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << "plan_" << std::put_time(std::localtime(&now_time_t), "%Y%m%d%H%M%S");
active_plan_id_ = ss.str();
}
PlanningFlow::PlanningFlow(const std::vector<std::shared_ptr<AgentBase>>& agents)
: BaseFlow(agents) {
// 生成计划ID
auto now = std::chrono::system_clock::now();
auto now_time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << "plan_" << std::put_time(std::localtime(&now_time_t), "%Y%m%d%H%M%S");
active_plan_id_ = ss.str();
}
std::string PlanningFlow::execute(const std::string& input_text) {
auto primary_agent = getPrimaryAgent();
if (!primary_agent) {
throw std::runtime_error("没有可用的代理");
}
std::cout << "使用规划流程执行任务..." << std::endl;
// 构建提示,包含计划状态
std::string prompt = "CURRENT PLAN STATUS:\n" + getPlanStatus() + "\n\n" + input_text;
// 执行主要代理
std::string result = primary_agent->run(prompt);
// 获取当前步骤索引
int current_step_index = getCurrentStepIndex();
// 如果有工具调用,更新步骤状态
// 注意这里假设ToolCallAgent会在执行工具后更新消息历史
// 在实际实现中,需要根据具体情况调整
return result;
}
std::string PlanningFlow::getPlanStatus() {
auto primary_agent = getPrimaryAgent();
if (!primary_agent) {
return "没有可用的代理";
}
// 构建获取计划状态的参数
mcp::json params = {
{"action", "get_plan_status"},
{"plan_id", active_plan_id_}
};
// 创建工具调用
mcp::json tool_call = {
{"name", "planning"},
{"params", params}
};
try {
// 执行工具调用
// 注意这里假设ToolBase有一个静态方法可以执行工具调用
// 在实际实现中,需要根据具体情况调整
mcp::json result = primary_agent->executeToolCall("planning", params);
// 解析结果
if (result.contains("success") && result["success"].get<bool>()) {
if (result.contains("plan")) {
return result["plan"].dump(4);
}
}
// 如果没有计划,创建一个新计划
if (!result.contains("success") || !result["success"].get<bool>()) {
mcp::json create_params = {
{"action", "create_plan"},
{"plan_id", active_plan_id_},
{"title", "任务计划"},
{"description", "执行用户请求的任务"}
};
mcp::json create_result = primary_agent->executeToolCall("planning", create_params);
if (create_result.contains("success") && create_result["success"].get<bool>()) {
active_plan_id_ = create_result["plan_id"];
// 重新获取计划状态
return getPlanStatus();
}
}
return "无法获取计划状态";
} catch (const std::exception& e) {
return std::string("获取计划状态出错: ") + e.what();
}
}
bool PlanningFlow::updateStepStatus(const std::string& step_id, const std::string& status) {
auto primary_agent = getPrimaryAgent();
if (!primary_agent) {
return false;
}
// 构建更新步骤状态的参数
mcp::json params = {
{"action", "update_step_status"},
{"plan_id", active_plan_id_},
{"step_id", step_id},
{"status", status}
};
try {
// 执行工具调用
mcp::json result = primary_agent->executeToolCall("planning", params);
// 解析结果
if (result.contains("success") && result["success"].get<bool>()) {
return true;
}
return false;
} catch (const std::exception& e) {
std::cerr << "更新步骤状态出错: " << e.what() << std::endl;
return false;
}
}
int PlanningFlow::getCurrentStepIndex() {
auto primary_agent = getPrimaryAgent();
if (!primary_agent) {
return -1;
}
// 构建获取计划状态的参数
mcp::json params = {
{"action", "get_plan_status"},
{"plan_id", active_plan_id_}
};
try {
// 执行工具调用
mcp::json result = primary_agent->executeToolCall("planning", params);
// 解析结果
if (result.contains("success") && result["success"].get<bool>()) {
if (result.contains("plan") && result["plan"].contains("current_step_index")) {
return result["plan"]["current_step_index"];
}
}
return -1;
} catch (const std::exception& e) {
std::cerr << "获取当前步骤索引出错: " << e.what() << std::endl;
return -1;
}
}
} // namespace openmanus

55
src/manus.cpp 100644
View File

@ -0,0 +1,55 @@
#include "include/manus.h"
#include "include/tools/terminate.h"
#include "include/tools/python_execute.h"
#include "include/tools/planning.h"
#include <iostream>
#include <memory>
namespace openmanus {
Manus::Manus(const std::string& config_file)
: ToolCallAgent("Manus", "一个通用的智能助手,可以使用多种工具解决各种任务", config_file) {
// 初始化Manus代理
initialize();
}
void Manus::initialize() {
// 系统提示和下一步提示已经在ToolCallAgent构造函数中从配置文件读取
// 添加Manus特定的工具
addManusTools();
}
void Manus::addManusTools() {
// 添加终止工具
available_tools_->addTool(std::make_unique<Terminate>());
// 添加规划工具
available_tools_->addTool(std::make_unique<Planning>());
// 根据配置决定是否添加Python执行工具
if (config_.getBool("tools", "enable_python_execute", true)) {
available_tools_->addTool(std::make_unique<PythonExecute>());
}
// 在这里添加其他Manus特定的工具
// 根据配置决定是否添加其他工具
if (config_.getBool("tools", "enable_google_search", false)) {
// available_tools_->addTool(std::make_unique<GoogleSearch>());
std::cout << "Google搜索工具尚未实现" << std::endl;
}
if (config_.getBool("tools", "enable_browser_use", false)) {
// available_tools_->addTool(std::make_unique<BrowserUseTool>());
std::cout << "浏览器使用工具尚未实现" << std::endl;
}
if (config_.getBool("tools", "enable_file_saver", false)) {
// available_tools_->addTool(std::make_unique<FileSaver>());
std::cout << "文件保存工具尚未实现" << std::endl;
}
std::cout << "添加Manus特定的工具..." << std::endl;
}
} // namespace openmanus

11
src/tool_base.cpp 100644
View File

@ -0,0 +1,11 @@
#include "include/tool_base.h"
namespace openmanus {
ToolBase::ToolBase(const std::string& name, const std::string& description)
: name_(name), description_(description) {
// 初始化参数定义为空对象
parameters_ = mcp::json::object();
}
} // namespace openmanus

26
src/tool_call.cpp 100644
View File

@ -0,0 +1,26 @@
#include "include/tool_call.h"
namespace openmanus {
ToolCall::ToolCall(const std::string& id, const std::string& name, const mcp::json& params)
: id_(id), name_(name), params_(params), result_("") {
}
ToolCall ToolCall::fromJson(const mcp::json& json) {
std::string id = json.contains("id") ? json["id"].get<std::string>() : "";
std::string name = json.contains("name") ? json["name"].get<std::string>() : "";
mcp::json params = json.contains("params") ? json["params"] : mcp::json::object();
return ToolCall(id, name, params);
}
mcp::json ToolCall::toJson() const {
mcp::json json;
json["id"] = id_;
json["name"] = name_;
json["params"] = params_;
json["result"] = result_;
return json;
}
} // namespace openmanus

View File

@ -0,0 +1,494 @@
#include "include/tool_call_agent.h"
#include "mcp/include/mcp_tool.h"
#include <iostream>
#include <sstream>
namespace openmanus {
ToolCallAgent::ToolCallAgent(const std::string& name, const std::string& description, const std::string& config_file)
: AgentBase(name, description), config_(config_file), max_steps_(30), current_step_(0), should_clear_history_(false), max_consecutive_tool_calls_(5), consecutive_tool_calls_(0) {
// 从配置文件中读取LLM API相关信息
llm_provider_ = config_.getString("llm", "provider", "local");
if (llm_provider_ == "local") {
// 本地服务器配置
llm_api_host_ = config_.getString("llm", "host", "localhost");
llm_api_port_ = config_.getInt("llm", "port", 8000);
llm_api_endpoint_ = config_.getString("llm", "path", "/chat/completions");
// 初始化HTTP客户端 - 本地服务器
http_client_ = std::make_unique<httplib::Client>(llm_api_host_, llm_api_port_);
} else {
// 云服务提供商配置
llm_api_base_url_ = config_.getString("llm", "base_url", "");
llm_api_endpoint_ = config_.getString("llm", "path", "/v1/chat/completions");
// 初始化HTTP客户端 - 云服务
http_client_ = std::make_unique<httplib::Client>(llm_api_base_url_);
}
// 通用配置
llm_api_key_ = config_.getString("llm", "api_key", "");
llm_model_ = config_.getString("llm", "model", "gpt-3.5-turbo");
llm_max_tokens_ = config_.getInt("llm", "max_tokens", 4096);
// 设置HTTP客户端超时
http_client_->set_connection_timeout(300); // 设置连接超时时间为5分钟
http_client_->set_read_timeout(300); // 设置读取超时时间为5分钟
// 从配置文件中读取代理相关信息
max_steps_ = config_.getInt("agent", "max_steps", 30);
max_consecutive_tool_calls_ = config_.getInt("agent", "max_consecutive_tool_calls", 5);
system_prompt_ = config_.getString("agent", "system_prompt", "你是一个能够使用工具的智能助手。");
next_step_prompt_ = config_.getString("agent", "next_step_prompt", "请思考下一步行动。");
should_clear_history_ = config_.getBool("agent", "clear_history_each_run", false);
// 添加系统提示
if (!system_prompt_.empty()) {
messages_.push_back(Message::systemMessage(system_prompt_));
}
}
void ToolCallAgent::setLLMAPIParams(const std::string& host, int port, const std::string& endpoint) {
// 设置为本地服务器模式
llm_provider_ = "local";
llm_api_host_ = host;
llm_api_port_ = port;
llm_api_endpoint_ = endpoint;
// 重新初始化HTTP客户端
http_client_ = std::make_unique<httplib::Client>(llm_api_host_, llm_api_port_);
http_client_->set_connection_timeout(300); // 设置连接超时时间为5分钟
http_client_->set_read_timeout(300); // 设置读取超时时间为5分钟
}
// 新增方法用于设置云服务提供商的API参数
void ToolCallAgent::setCloudLLMAPIParams(const std::string& provider, const std::string& base_url, const std::string& endpoint) {
llm_provider_ = provider;
llm_api_base_url_ = base_url;
llm_api_endpoint_ = endpoint;
// 重新初始化HTTP客户端
http_client_ = std::make_unique<httplib::Client>(llm_api_base_url_);
http_client_->set_connection_timeout(300); // 设置连接超时时间为5分钟
http_client_->set_read_timeout(300); // 设置读取超时时间为5分钟
}
void ToolCallAgent::setLLMAPIKey(const std::string& api_key) {
llm_api_key_ = api_key;
}
void ToolCallAgent::clearMessages() {
messages_.clear();
// 添加系统提示
if (!system_prompt_.empty()) {
messages_.push_back(Message::systemMessage(system_prompt_));
}
}
void ToolCallAgent::addMessage(const Message& message) {
messages_.push_back(message);
}
std::string ToolCallAgent::run(const std::string& prompt) {
// 清空工具调用
tool_calls_.clear();
current_step_ = 0;
consecutive_tool_calls_ = 0; // 重置连续工具调用计数器
// 如果配置为每次运行清空历史,则清空
if (should_clear_history_) {
clearMessages();
}
// 添加用户提示到消息历史
addMessage(Message::userMessage(prompt));
std::string result;
bool terminated = false;
// 执行代理循环
while (current_step_ < max_steps_ && !terminated) {
// 思考下一步行动
bool continue_execution = think();
if (!continue_execution) {
break;
}
// 检查是否有Terminate工具调用
for (const auto& tool_call : tool_calls_) {
if (tool_call.getName() == "Terminate") {
std::cout << "🛑 检测到Terminate工具调用将在此步骤后停止执行" << std::endl;
terminated = true;
break;
}
}
// 执行行动
result = act();
// 检查是否应该结束执行
if (terminated || shouldFinishExecution()) {
break;
}
current_step_++;
}
return result;
}
bool ToolCallAgent::think() {
// 检查连续工具调用次数
if (consecutive_tool_calls_ >= max_consecutive_tool_calls_) {
std::cout << "⚠️ 达到最大连续工具调用次数限制(" << max_consecutive_tool_calls_ << "),强制停止" << std::endl;
// 添加一条消息告知LLM已达到工具调用限制
std::string limit_message = "已达到最大连续工具调用次数限制(" + std::to_string(max_consecutive_tool_calls_) +
")。请总结目前的进展并提供最终答案,不要再使用工具。";
addMessage(Message::userMessage(limit_message));
// 重置计数器
consecutive_tool_calls_ = 0;
}
// 如果有下一步提示,添加到消息历史
if (!next_step_prompt_.empty()) {
addMessage(Message::userMessage(next_step_prompt_));
}
try {
// 调用LLM API获取下一步行动
mcp::json response = callLLMAPI(messages_);
// 解析响应,提取工具调用
tool_calls_.clear();
if (response.contains("choices") && response["choices"].is_array() && !response["choices"].empty()) {
auto& choice = response["choices"][0];
// 记录思考内容
std::string content = "";
if (choice.contains("message") && choice["message"].contains("content")) {
content = choice["message"]["content"].get<std::string>();
if (!content.empty()) {
std::cout << "" << name_ << "的思考: " << content << std::endl;
// 添加助手消息到历史
addMessage(Message::assistantMessage(content));
}
}
// 提取工具调用
if (choice.contains("message") && choice["message"].contains("tool_calls") &&
choice["message"]["tool_calls"].is_array()) {
auto& tool_calls_json = choice["message"]["tool_calls"];
std::cout << "🛠️ " << name_ << "选择了 " << tool_calls_json.size() << " 个工具使用" << std::endl;
if (!tool_calls_json.empty()) {
std::cout << "🧰 正在准备的工具: ";
bool has_terminate = false;
for (const auto& tool_call_json : tool_calls_json) {
if (tool_call_json.contains("id") && tool_call_json.contains("function")) {
std::string id = tool_call_json["id"];
std::string name = tool_call_json["function"]["name"];
// 检查是否是Terminate工具
if (name == "Terminate") {
has_terminate = true;
std::cout << "🛑 检测到Terminate工具调用!" << std::endl;
}
mcp::json params = tool_call_json["function"].contains("arguments") ?
tool_call_json["function"]["arguments"] : mcp::json::object();
// 如果参数是字符串尝试解析为JSON
if (params.is_string()) {
try {
params = mcp::json::parse(params.get<std::string>());
} catch (...) {
// 如果解析失败,保持原样
}
}
// 创建工具调用对象
ToolCall tool_call(id, name, params);
tool_calls_.push_back(tool_call);
std::cout << name << " ";
}
}
std::cout << std::endl;
}
}
}
return !tool_calls_.empty();
} catch (const std::exception& e) {
std::cerr << "思考过程中出错: " << e.what() << std::endl;
return false;
}
}
mcp::json ToolCallAgent::callLLMAPI(const std::vector<Message>& messages) {
// 构建请求JSON
mcp::json request;
// 根据不同的LLM提供商构建不同的请求
if (llm_provider_ == "deepseek") {
// DeepSeek API请求格式
request["model"] = llm_model_;
request["messages"] = mcp::json::array();
request["max_tokens"] = llm_max_tokens_;
// 添加消息
for (const auto& message : messages) {
mcp::json msg_json;
// 设置角色
switch (message.role) {
case MessageRole::SYSTEM:
msg_json["role"] = "system";
break;
case MessageRole::USER:
msg_json["role"] = "user";
break;
case MessageRole::ASSISTANT:
msg_json["role"] = "assistant";
break;
case MessageRole::TOOL:
// 工具消息现在被转换为用户消息,所以这里不应该有工具消息
std::cerr << "警告:发现工具消息,但我们现在将工具消息转换为用户消息" << std::endl;
continue; // 跳过这个消息
}
msg_json["content"] = message.content;
request["messages"].push_back(msg_json);
}
// 添加工具定义
if (available_tools_ && available_tools_->size() > 0) {
request["tools"] = mcp::json::array();
for (const auto& tool_name : available_tools_->getToolNames()) {
ToolBase* tool = available_tools_->getTool(tool_name);
if (tool) {
// 构建工具JSON
mcp::json tool_json;
tool_json["type"] = "function";
tool_json["function"]["name"] = tool->getName();
tool_json["function"]["description"] = tool->getDescription();
tool_json["function"]["parameters"] = tool->getParameters();
request["tools"].push_back(tool_json);
}
}
// 设置工具选择策略
request["tool_choice"] = "auto";
}
} else {
// 默认OpenAI兼容格式
request["model"] = llm_model_;
request["messages"] = mcp::json::array();
// 添加消息
for (const auto& message : messages) {
mcp::json msg_json;
// 设置角色
switch (message.role) {
case MessageRole::SYSTEM:
msg_json["role"] = "system";
break;
case MessageRole::USER:
msg_json["role"] = "user";
break;
case MessageRole::ASSISTANT:
msg_json["role"] = "assistant";
break;
case MessageRole::TOOL:
// 工具消息现在被转换为用户消息,所以这里不应该有工具消息
std::cerr << "警告:发现工具消息,但我们现在将工具消息转换为用户消息" << std::endl;
msg_json["role"] = "user";
break;
}
msg_json["content"] = message.content;
request["messages"].push_back(msg_json);
}
// 添加工具定义
if (available_tools_ && available_tools_->size() > 0) {
request["tools"] = mcp::json::array();
for (const auto& tool_name : available_tools_->getToolNames()) {
ToolBase* tool = available_tools_->getTool(tool_name);
if (tool) {
// 构建工具JSON
mcp::json tool_json;
tool_json["type"] = "function";
tool_json["function"]["name"] = tool->getName();
tool_json["function"]["description"] = tool->getDescription();
tool_json["function"]["parameters"] = tool->getParameters();
request["tools"].push_back(tool_json);
}
}
// 设置工具选择策略
request["tool_choice"] = "auto";
}
}
// freopen("request.json", "w", stdout);
// std::cout << request.dump(2) << std::endl;
// fclose(stdout);
// 发送请求
std::string request_str = request.dump();
httplib::Headers headers = {
{"Content-Type", "application/json"}
};
// 添加Authorization头部
if (!llm_api_key_.empty()) {
headers.emplace("Authorization", "Bearer " + llm_api_key_);
}
auto response = http_client_->Post(llm_api_endpoint_, headers, request_str, "application/json");
if (response && response->status == 200) {
// 解析响应
mcp::json response_json = mcp::json::parse(response->body);
return response_json;
} else {
std::string error_msg = "LLM API调用失败";
if (response) {
error_msg += ": " + std::to_string(response->status) + " " + response->body;
}
throw std::runtime_error(error_msg);
}
}
std::string ToolCallAgent::act() {
std::string result;
// 如果有工具调用,增加计数器
if (!tool_calls_.empty()) {
consecutive_tool_calls_++;
std::cout << "🔄 连续工具调用次数: " << consecutive_tool_calls_ << "/" << max_consecutive_tool_calls_ << std::endl;
} else {
// 如果没有工具调用,重置计数器
consecutive_tool_calls_ = 0;
}
// 执行所有工具调用
for (auto& tool_call : tool_calls_) {
std::string tool_result = executeToolCall(tool_call);
tool_call.setResult(tool_result);
// 处理特殊工具
if (isSpecialTool(tool_call.getName())) {
handleSpecialTool(tool_call.getName(), tool_result);
}
// 将工具结果添加为用户消息,使用明确的格式
std::string tool_message = "工具执行结果 [" + tool_call.getName() + "]: \n```json\n" + tool_result + "\n```\n请根据这个结果继续你的任务。";
// std::cout << "📝 添加消息到历史: " << tool_message << std::endl;
addMessage(Message::userMessage(tool_message));
result += tool_result + "\n";
}
// 打印当前消息历史长度
std::cout << "📚 当前消息历史长度: " << messages_.size() << std::endl;
// 清空工具调用列表,准备下一轮
tool_calls_.clear();
return result;
}
std::string ToolCallAgent::executeToolCall(const ToolCall& tool_call) {
std::string name = tool_call.getName();
mcp::json params = tool_call.getParams();
std::cout << "🔧 执行工具: " << name << " 参数: " << params.dump(2) << std::endl;
// 获取工具
ToolBase* tool = available_tools_->getTool(name);
if (!tool) {
std::string error_msg = "错误:找不到工具 " + name;
std::cout << "" << error_msg << std::endl;
return error_msg;
}
try {
// 执行工具
mcp::json result = tool->execute(params);
// 格式化结果确保它是一个有效的JSON字符串
std::string result_str;
try {
// 如果结果已经是一个JSON对象则直接格式化
result_str = result.dump(2);
} catch (...) {
// 如果结果不是一个有效的JSON对象则将其包装为一个JSON对象
mcp::json wrapper;
wrapper["result"] = result.dump();
result_str = wrapper.dump(2);
}
std::cout << "✅ 工具执行结果: " << result_str << std::endl;
return result_str;
} catch (const std::exception& e) {
std::string error_msg = "错误:执行工具 " + name + " 失败:" + e.what();
std::cout << "" << error_msg << std::endl;
return error_msg;
}
}
bool ToolCallAgent::handleSpecialTool(const std::string& name, const std::string& result) {
// 处理特殊工具,如终止工具
if (name == "Terminate") {
std::cout << "🛑 收到终止指令,停止执行" << std::endl;
// 添加一条消息,表示任务已完成
addMessage(Message::assistantMessage("任务已完成,执行已终止。"));
return true;
}
return false;
}
bool ToolCallAgent::shouldFinishExecution() {
// 检查是否应该结束执行
// 检查是否达到最大步骤数
if (current_step_ >= max_steps_) {
std::cout << "⚠️ 达到最大步骤数限制(" << max_steps_ << "),停止执行" << std::endl;
return true;
}
// 如果连续工具调用次数超过限制,也应该结束执行
if (consecutive_tool_calls_ >= max_consecutive_tool_calls_) {
std::cout << "⚠️ 达到最大连续工具调用次数限制(" << max_consecutive_tool_calls_ << "),停止执行" << std::endl;
return true;
}
return false;
}
bool ToolCallAgent::isSpecialTool(const std::string& name) {
// 检查是否是特殊工具
// 目前只有Terminate是特殊工具
if (name == "Terminate") {
std::cout << "🔍 识别到特殊工具: Terminate" << std::endl;
return true;
}
return false;
}
} // namespace openmanus

View File

@ -0,0 +1,35 @@
#include "include/tool_collection.h"
namespace openmanus {
ToolCollection::ToolCollection() {
// 初始化工具集合
}
void ToolCollection::addTool(std::unique_ptr<ToolBase> tool) {
if (tool) {
std::string name = tool->getName();
tools_[name] = std::move(tool);
}
}
ToolBase* ToolCollection::getTool(const std::string& name) {
auto it = tools_.find(name);
if (it != tools_.end()) {
return it->second.get();
}
return nullptr;
}
std::vector<std::string> ToolCollection::getToolNames() const {
std::vector<std::string> names;
names.reserve(tools_.size());
for (const auto& pair : tools_) {
names.push_back(pair.first);
}
return names;
}
} // namespace openmanus

View File

@ -0,0 +1,242 @@
#include "include/tools/planning.h"
#include "mcp/include/mcp_tool.h"
#include <chrono>
#include <sstream>
#include <iomanip>
namespace openmanus {
Planning::Planning()
: ToolBase("planning", "创建和管理结构化计划") {
// 使用tool_builder构建工具定义
mcp::tool_builder builder("planning");
builder.with_description("创建和管理结构化计划");
// 添加参数
builder.with_string_param("action", "要执行的操作", true);
builder.with_string_param("plan_id", "计划ID", false);
builder.with_string_param("title", "计划标题", false);
builder.with_string_param("description", "计划描述", false);
builder.with_array_param("steps", "计划步骤列表", "string", false);
builder.with_string_param("step_id", "步骤ID", false);
builder.with_string_param("step_description", "步骤描述", false);
builder.with_string_param("status", "步骤状态", false);
mcp::tool tool = builder.build();
// 从构建的工具中获取参数定义
parameters_ = tool.parameters_schema;
// 手动添加enum约束
if (parameters_.contains("properties") && parameters_["properties"].contains("action")) {
parameters_["properties"]["action"]["enum"] = {"create_plan", "get_plan_status", "update_step_status", "add_step"};
}
// 手动添加status的enum约束
if (parameters_.contains("properties") && parameters_["properties"].contains("status")) {
parameters_["properties"]["status"]["enum"] = {"pending", "in_progress", "completed", "failed"};
}
}
mcp::json Planning::execute(const mcp::json& params) {
if (!params.contains("action")) {
return {
{"success", false},
{"error", "缺少action参数"}
};
}
std::string action = params["action"];
if (action == "create_plan") {
return createPlan(params);
} else if (action == "get_plan_status") {
return getPlanStatus(params);
} else if (action == "update_step_status") {
return updateStepStatus(params);
} else if (action == "add_step") {
return addStep(params);
} else {
return {
{"success", false},
{"error", "未知的action: " + action}
};
}
}
mcp::json Planning::createPlan(const mcp::json& params) {
if (!params.contains("title") || !params.contains("description")) {
return {
{"success", false},
{"error", "缺少title或description参数"}
};
}
// 生成计划ID
auto now = std::chrono::system_clock::now();
auto now_time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << "plan_" << std::put_time(std::localtime(&now_time_t), "%Y%m%d%H%M%S");
std::string plan_id = ss.str();
// 创建计划
Plan plan;
plan.id = plan_id;
plan.title = params["title"];
plan.description = params["description"];
plan.current_step_index = 0;
// 添加步骤
if (params.contains("steps") && params["steps"].is_array()) {
for (size_t i = 0; i < params["steps"].size(); ++i) {
std::string step_id = "step_" + std::to_string(i + 1);
std::string step_description = params["steps"][i];
plan.step_ids.push_back(step_id);
plan.step_descriptions[step_id] = step_description;
plan.step_statuses[step_id] = "pending";
}
}
// 保存计划
plans_[plan_id] = plan;
return {
{"success", true},
{"plan_id", plan_id},
{"message", "计划创建成功"}
};
}
mcp::json Planning::getPlanStatus(const mcp::json& params) {
if (!params.contains("plan_id")) {
return {
{"success", false},
{"error", "缺少plan_id参数"}
};
}
std::string plan_id = params["plan_id"];
// 查找计划
auto it = plans_.find(plan_id);
if (it == plans_.end()) {
return {
{"success", false},
{"error", "找不到计划: " + plan_id}
};
}
const Plan& plan = it->second;
// 构建步骤信息
mcp::json steps = mcp::json::array();
for (const auto& step_id : plan.step_ids) {
steps.push_back({
{"id", step_id},
{"description", plan.step_descriptions.at(step_id)},
{"status", plan.step_statuses.at(step_id)}
});
}
return {
{"success", true},
{"plan", {
{"id", plan.id},
{"title", plan.title},
{"description", plan.description},
{"steps", steps},
{"current_step_index", plan.current_step_index}
}}
};
}
mcp::json Planning::updateStepStatus(const mcp::json& params) {
if (!params.contains("plan_id") || !params.contains("step_id") || !params.contains("status")) {
return {
{"success", false},
{"error", "缺少plan_id、step_id或status参数"}
};
}
std::string plan_id = params["plan_id"];
std::string step_id = params["step_id"];
std::string status = params["status"];
// 查找计划
auto it = plans_.find(plan_id);
if (it == plans_.end()) {
return {
{"success", false},
{"error", "找不到计划: " + plan_id}
};
}
Plan& plan = it->second;
// 查找步骤
auto step_status_it = plan.step_statuses.find(step_id);
if (step_status_it == plan.step_statuses.end()) {
return {
{"success", false},
{"error", "找不到步骤: " + step_id}
};
}
// 更新步骤状态
step_status_it->second = status;
// 如果步骤完成,更新当前步骤索引
if (status == "completed") {
for (size_t i = 0; i < plan.step_ids.size(); ++i) {
if (plan.step_ids[i] == step_id && i == plan.current_step_index) {
plan.current_step_index = i + 1;
break;
}
}
}
return {
{"success", true},
{"message", "步骤状态更新成功"}
};
}
mcp::json Planning::addStep(const mcp::json& params) {
if (!params.contains("plan_id") || !params.contains("step_description")) {
return {
{"success", false},
{"error", "缺少plan_id或step_description参数"}
};
}
std::string plan_id = params["plan_id"];
std::string step_description = params["step_description"];
// 查找计划
auto it = plans_.find(plan_id);
if (it == plans_.end()) {
return {
{"success", false},
{"error", "找不到计划: " + plan_id}
};
}
Plan& plan = it->second;
// 生成步骤ID
std::string step_id = "step_" + std::to_string(plan.step_ids.size() + 1);
// 添加步骤
plan.step_ids.push_back(step_id);
plan.step_descriptions[step_id] = step_description;
plan.step_statuses[step_id] = "pending";
return {
{"success", true},
{"step_id", step_id},
{"message", "步骤添加成功"}
};
}
} // namespace openmanus

View File

@ -0,0 +1,48 @@
#include "include/tools/python_execute.h"
#include "mcp/include/mcp_client.h"
#include "mcp/include/mcp_tool.h"
#include <iostream>
namespace openmanus {
PythonExecute::PythonExecute()
: ToolBase("PythonExecute", "执行Python代码并返回结果") {
// 使用tool_builder构建工具定义
mcp::tool_builder builder("PythonExecute");
builder.with_description("执行Python代码并返回结果");
builder.with_string_param("code", "要执行的Python代码", true);
mcp::tool tool = builder.build();
// 从构建的工具中获取参数定义
parameters_ = tool.parameters_schema;
}
mcp::json PythonExecute::execute(const mcp::json& params) {
if (!params.contains("code")) {
throw std::runtime_error("缺少'code'参数");
}
std::string code = params["code"];
try {
// 创建MCP客户端
mcp::client client("localhost", 8080);
// 初始化客户端
client.initialize("OpenManusCppClient", "0.1.0");
// 调用PythonExecute工具
mcp::json tool_params = {{"code", code}};
mcp::json result = client.call_tool("PythonExecute", tool_params);
return result;
} catch (const std::exception& e) {
std::cerr << "执行Python代码失败: " << e.what() << std::endl;
mcp::json error_result;
error_result["success"] = false;
error_result["error"] = e.what();
return error_result;
}
}
} // namespace openmanus

View File

@ -0,0 +1,25 @@
#include "include/tools/terminate.h"
#include "mcp/include/mcp_tool.h"
namespace openmanus {
Terminate::Terminate()
: ToolBase("Terminate", "当任务完成时调用此工具来结束执行。任务完成后必须调用此工具。") {
// 使用tool_builder构建工具定义
mcp::tool_builder builder("Terminate");
builder.with_description("当任务完成时调用此工具来结束执行。任务完成后必须调用此工具。");
mcp::tool tool = builder.build();
// 从构建的工具中获取参数定义
parameters_ = tool.parameters_schema;
}
mcp::json Terminate::execute(const mcp::json& params) {
// 终止工具不需要任何参数,也不需要执行任何操作
// 它的存在只是为了通知代理终止执行
mcp::json result;
result["message"] = "执行已终止";
return result;
}
} // namespace openmanus