From 75f016659e2d98f74d8527ec047a36a740f15a85 Mon Sep 17 00:00:00 2001 From: hkr04 Date: Mon, 10 Mar 2025 02:38:39 +0800 Subject: [PATCH] first commit --- .gitignore | 106 +++++++ CMakeLists.txt | 44 +++ README.md | 204 ++++++++++++++ config.toml | 14 + config.toml.backup | 18 ++ include/agent_base.h | 58 ++++ include/config.h | 72 +++++ include/flow/base_flow.h | 77 +++++ include/flow/flow_factory.h | 43 +++ include/flow/planning_flow.h | 64 +++++ include/manus.h | 35 +++ include/tool_base.h | 54 ++++ include/tool_call.h | 70 +++++ include/tool_call_agent.h | 179 ++++++++++++ include/tool_collection.h | 52 ++++ include/tools/planning.h | 73 +++++ include/tools/python_execute.h | 27 ++ include/tools/terminate.h | 27 ++ main.cpp | 72 +++++ mcp | 1 + request.json | 0 server/CMakeLists.txt | 37 +++ server/mcp_server_main.cpp | 123 ++++++++ src/agent_base.cpp | 23 ++ src/config.cpp | 117 ++++++++ src/flow/base_flow.cpp | 50 ++++ src/flow/flow_factory.cpp | 30 ++ src/flow/planning_flow.cpp | 170 ++++++++++++ src/manus.cpp | 55 ++++ src/tool_base.cpp | 11 + src/tool_call.cpp | 26 ++ src/tool_call_agent.cpp | 494 +++++++++++++++++++++++++++++++++ src/tool_collection.cpp | 35 +++ src/tools/planning.cpp | 242 ++++++++++++++++ src/tools/python_execute.cpp | 48 ++++ src/tools/terminate.cpp | 25 ++ 36 files changed, 2776 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 config.toml create mode 100644 config.toml.backup create mode 100644 include/agent_base.h create mode 100644 include/config.h create mode 100644 include/flow/base_flow.h create mode 100644 include/flow/flow_factory.h create mode 100644 include/flow/planning_flow.h create mode 100644 include/manus.h create mode 100644 include/tool_base.h create mode 100644 include/tool_call.h create mode 100644 include/tool_call_agent.h create mode 100644 include/tool_collection.h create mode 100644 include/tools/planning.h create mode 100644 include/tools/python_execute.h create mode 100644 include/tools/terminate.h create mode 100644 main.cpp create mode 160000 mcp create mode 100644 request.json create mode 100644 server/CMakeLists.txt create mode 100644 server/mcp_server_main.cpp create mode 100644 src/agent_base.cpp create mode 100644 src/config.cpp create mode 100644 src/flow/base_flow.cpp create mode 100644 src/flow/flow_factory.cpp create mode 100644 src/flow/planning_flow.cpp create mode 100644 src/manus.cpp create mode 100644 src/tool_base.cpp create mode 100644 src/tool_call.cpp create mode 100644 src/tool_call_agent.cpp create mode 100644 src/tool_collection.cpp create mode 100644 src/tools/planning.cpp create mode 100644 src/tools/python_execute.cpp create mode 100644 src/tools/terminate.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1013fc6 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5d3fbdc --- /dev/null +++ b/CMakeLists.txt @@ -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) + +# 查找OpenSSL库,尝试查找3.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) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..04045f8 --- /dev/null +++ b/README.md @@ -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项目相同的许可证。 \ No newline at end of file diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..8effc8b --- /dev/null +++ b/config.toml @@ -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 \ No newline at end of file diff --git a/config.toml.backup b/config.toml.backup new file mode 100644 index 0000000..5dbf806 --- /dev/null +++ b/config.toml.backup @@ -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 \ No newline at end of file diff --git a/include/agent_base.h b/include/agent_base.h new file mode 100644 index 0000000..c3ad7e7 --- /dev/null +++ b/include/agent_base.h @@ -0,0 +1,58 @@ +#ifndef OPENMANUS_AGENT_BASE_H +#define OPENMANUS_AGENT_BASE_H + +#include +#include +#include +#include "tool_collection.h" +#include "mcp/include/mcp_message.h" + +namespace openmanus { + +/** + * @class AgentBase + * @brief 代理的基类,定义了所有代理的基本接口 + */ +class AgentBase : public std::enable_shared_from_this { +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 available_tools_; +}; + +} // namespace openmanus + +#endif // OPENMANUS_AGENT_BASE_H \ No newline at end of file diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..e58b122 --- /dev/null +++ b/include/config.h @@ -0,0 +1,72 @@ +#ifndef OPENMANUS_CONFIG_H +#define OPENMANUS_CONFIG_H + +#include +#include +#include +#include + +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> config_data_; +}; + +} // namespace openmanus + +#endif // OPENMANUS_CONFIG_H \ No newline at end of file diff --git a/include/flow/base_flow.h b/include/flow/base_flow.h new file mode 100644 index 0000000..338d31f --- /dev/null +++ b/include/flow/base_flow.h @@ -0,0 +1,77 @@ +#ifndef OPENMANUS_FLOW_BASE_FLOW_H +#define OPENMANUS_FLOW_BASE_FLOW_H + +#include +#include +#include +#include +#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 agent); + + /** + * @brief 构造函数 + * @param agents 多个代理 + */ + BaseFlow(const std::vector>& 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 getPrimaryAgent() const; + + /** + * @brief 获取指定代理 + * @param key 代理键 + * @return 代理 + */ + std::shared_ptr getAgent(const std::string& key) const; + + /** + * @brief 添加代理 + * @param key 代理键 + * @param agent 代理 + */ + void addAgent(const std::string& key, std::shared_ptr agent); + +protected: + std::unordered_map> agents_; + std::string primary_agent_key_; +}; + +} // namespace openmanus + +#endif // OPENMANUS_FLOW_BASE_FLOW_H \ No newline at end of file diff --git a/include/flow/flow_factory.h b/include/flow/flow_factory.h new file mode 100644 index 0000000..2eef521 --- /dev/null +++ b/include/flow/flow_factory.h @@ -0,0 +1,43 @@ +#ifndef OPENMANUS_FLOW_FLOW_FACTORY_H +#define OPENMANUS_FLOW_FLOW_FACTORY_H + +#include +#include +#include +#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 createFlow( + FlowType flow_type, + std::shared_ptr agent + ); + + /** + * @brief 创建流程 + * @param flow_type 流程类型 + * @param agents 多个代理 + * @return 流程 + */ + static std::shared_ptr createFlow( + FlowType flow_type, + const std::vector>& agents + ); +}; + +} // namespace openmanus + +#endif // OPENMANUS_FLOW_FLOW_FACTORY_H \ No newline at end of file diff --git a/include/flow/planning_flow.h b/include/flow/planning_flow.h new file mode 100644 index 0000000..8d4adc1 --- /dev/null +++ b/include/flow/planning_flow.h @@ -0,0 +1,64 @@ +#ifndef OPENMANUS_FLOW_PLANNING_FLOW_H +#define OPENMANUS_FLOW_PLANNING_FLOW_H + +#include +#include +#include +#include "base_flow.h" + +namespace openmanus { + +/** + * @class PlanningFlow + * @brief 规划流程,使用规划代理执行任务 + */ +class PlanningFlow : public BaseFlow { +public: + /** + * @brief 构造函数 + * @param agent 主要代理 + */ + PlanningFlow(std::shared_ptr agent); + + /** + * @brief 构造函数 + * @param agents 多个代理 + */ + PlanningFlow(const std::vector>& 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> step_execution_tracker_; +}; + +} // namespace openmanus + +#endif // OPENMANUS_FLOW_PLANNING_FLOW_H \ No newline at end of file diff --git a/include/manus.h b/include/manus.h new file mode 100644 index 0000000..260517c --- /dev/null +++ b/include/manus.h @@ -0,0 +1,35 @@ +#ifndef OPENMANUS_MANUS_H +#define OPENMANUS_MANUS_H + +#include +#include +#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 \ No newline at end of file diff --git a/include/tool_base.h b/include/tool_base.h new file mode 100644 index 0000000..12c7784 --- /dev/null +++ b/include/tool_base.h @@ -0,0 +1,54 @@ +#ifndef OPENMANUS_TOOL_BASE_H +#define OPENMANUS_TOOL_BASE_H + +#include +#include +#include +#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 \ No newline at end of file diff --git a/include/tool_call.h b/include/tool_call.h new file mode 100644 index 0000000..0da3d12 --- /dev/null +++ b/include/tool_call.h @@ -0,0 +1,70 @@ +#ifndef OPENMANUS_TOOL_CALL_H +#define OPENMANUS_TOOL_CALL_H + +#include +#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 \ No newline at end of file diff --git a/include/tool_call_agent.h b/include/tool_call_agent.h new file mode 100644 index 0000000..fc7897b --- /dev/null +++ b/include/tool_call_agent.h @@ -0,0 +1,179 @@ +#ifndef OPENMANUS_TOOL_CALL_AGENT_H +#define OPENMANUS_TOOL_CALL_AGENT_H + +#include +#include +#include +#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& 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& messages); + +protected: + std::vector messages_; // 消息历史 + std::vector 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 http_client_; // HTTP客户端 +}; + +} // namespace openmanus + +#endif // OPENMANUS_TOOL_CALL_AGENT_H \ No newline at end of file diff --git a/include/tool_collection.h b/include/tool_collection.h new file mode 100644 index 0000000..28984ec --- /dev/null +++ b/include/tool_collection.h @@ -0,0 +1,52 @@ +#ifndef OPENMANUS_TOOL_COLLECTION_H +#define OPENMANUS_TOOL_COLLECTION_H + +#include +#include +#include +#include +#include "tool_base.h" + +namespace openmanus { + +/** + * @class ToolCollection + * @brief 工具集合类,管理所有可用的工具 + */ +class ToolCollection { +public: + ToolCollection(); + ~ToolCollection() = default; + + /** + * @brief 添加工具到集合中 + * @param tool 要添加的工具 + */ + void addTool(std::unique_ptr tool); + + /** + * @brief 根据名称获取工具 + * @param name 工具名称 + * @return 工具指针,如果不存在则返回nullptr + */ + ToolBase* getTool(const std::string& name); + + /** + * @brief 获取所有工具的名称 + * @return 工具名称列表 + */ + std::vector getToolNames() const; + + /** + * @brief 获取工具数量 + * @return 工具数量 + */ + size_t size() const { return tools_.size(); } + +private: + std::unordered_map> tools_; +}; + +} // namespace openmanus + +#endif // OPENMANUS_TOOL_COLLECTION_H \ No newline at end of file diff --git a/include/tools/planning.h b/include/tools/planning.h new file mode 100644 index 0000000..d1114f7 --- /dev/null +++ b/include/tools/planning.h @@ -0,0 +1,73 @@ +#ifndef OPENMANUS_TOOLS_PLANNING_H +#define OPENMANUS_TOOLS_PLANNING_H + +#include "../tool_base.h" +#include +#include +#include + +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 step_ids; + std::unordered_map step_descriptions; + std::unordered_map step_statuses; + int current_step_index; + }; + + std::unordered_map plans_; +}; + +} // namespace openmanus + +#endif // OPENMANUS_TOOLS_PLANNING_H \ No newline at end of file diff --git a/include/tools/python_execute.h b/include/tools/python_execute.h new file mode 100644 index 0000000..7e06755 --- /dev/null +++ b/include/tools/python_execute.h @@ -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 Python执行工具,用于执行Python代码 + */ +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 \ No newline at end of file diff --git a/include/tools/terminate.h b/include/tools/terminate.h new file mode 100644 index 0000000..d066ffe --- /dev/null +++ b/include/tools/terminate.h @@ -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 \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..3167c39 --- /dev/null +++ b/main.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#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(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; +} \ No newline at end of file diff --git a/mcp b/mcp new file mode 160000 index 0000000..175d668 --- /dev/null +++ b/mcp @@ -0,0 +1 @@ +Subproject commit 175d66864253ab305d57524ca58edc4ecffb2ec9 diff --git a/request.json b/request.json new file mode 100644 index 0000000..e69de29 diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt new file mode 100644 index 0000000..f428dfa --- /dev/null +++ b/server/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/server/mcp_server_main.cpp b/server/mcp_server_main.cpp new file mode 100644 index 0000000..99c3f92 --- /dev/null +++ b/server/mcp_server_main.cpp @@ -0,0 +1,123 @@ +/** + * @file mcp_server_main.cpp + * @brief OpenManus MCP服务器实现 + * + * 这个文件实现了OpenManus的MCP服务器,提供工具调用功能。 + * 当前实现了PythonExecute工具。 + */ + +#include "mcp/include/mcp_server.h" +#include "mcp/include/mcp_tool.h" +#include "mcp/include/mcp_resource.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// 执行系统命令并返回输出 +std::string exec_command(const std::string& cmd) { + std::array 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; +} \ No newline at end of file diff --git a/src/agent_base.cpp b/src/agent_base.cpp new file mode 100644 index 0000000..28957ed --- /dev/null +++ b/src/agent_base.cpp @@ -0,0 +1,23 @@ +#include "include/agent_base.h" +#include + +namespace openmanus { + +AgentBase::AgentBase(const std::string& name, const std::string& description) + : name_(name), description_(description) { + // 初始化工具集合 + available_tools_ = std::make_unique(); +} + +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 \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..347a288 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,117 @@ +#include "include/config.h" +#include +#include +#include +#include +#include + +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 \ No newline at end of file diff --git a/src/flow/base_flow.cpp b/src/flow/base_flow.cpp new file mode 100644 index 0000000..fd83dea --- /dev/null +++ b/src/flow/base_flow.cpp @@ -0,0 +1,50 @@ +#include "include/flow/base_flow.h" +#include + +namespace openmanus { + +BaseFlow::BaseFlow(std::shared_ptr agent) { + if (agent) { + primary_agent_key_ = "default"; + agents_[primary_agent_key_] = agent; + } +} + +BaseFlow::BaseFlow(const std::vector>& 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 BaseFlow::getPrimaryAgent() const { + auto it = agents_.find(primary_agent_key_); + if (it != agents_.end()) { + return it->second; + } + return nullptr; +} + +std::shared_ptr 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 agent) { + if (agent) { + agents_[key] = agent; + if (agents_.size() == 1) { + primary_agent_key_ = key; + } + } +} + +} // namespace openmanus \ No newline at end of file diff --git a/src/flow/flow_factory.cpp b/src/flow/flow_factory.cpp new file mode 100644 index 0000000..1c24806 --- /dev/null +++ b/src/flow/flow_factory.cpp @@ -0,0 +1,30 @@ +#include "include/flow/flow_factory.h" +#include + +namespace openmanus { + +std::shared_ptr FlowFactory::createFlow( + FlowType flow_type, + std::shared_ptr agent +) { + switch (flow_type) { + case FlowType::PLANNING: + return std::make_shared(agent); + default: + throw std::runtime_error("未知的流程类型"); + } +} + +std::shared_ptr FlowFactory::createFlow( + FlowType flow_type, + const std::vector>& agents +) { + switch (flow_type) { + case FlowType::PLANNING: + return std::make_shared(agents); + default: + throw std::runtime_error("未知的流程类型"); + } +} + +} // namespace openmanus \ No newline at end of file diff --git a/src/flow/planning_flow.cpp b/src/flow/planning_flow.cpp new file mode 100644 index 0000000..287b2e4 --- /dev/null +++ b/src/flow/planning_flow.cpp @@ -0,0 +1,170 @@ +#include "include/flow/planning_flow.h" +#include +#include +#include +#include +#include "mcp/include/mcp_message.h" + +namespace openmanus { + +PlanningFlow::PlanningFlow(std::shared_ptr 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>& 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()) { + if (result.contains("plan")) { + return result["plan"].dump(4); + } + } + + // 如果没有计划,创建一个新计划 + if (!result.contains("success") || !result["success"].get()) { + 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()) { + 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()) { + 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()) { + 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 \ No newline at end of file diff --git a/src/manus.cpp b/src/manus.cpp new file mode 100644 index 0000000..b50183d --- /dev/null +++ b/src/manus.cpp @@ -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 +#include + +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()); + + // 添加规划工具 + available_tools_->addTool(std::make_unique()); + + // 根据配置决定是否添加Python执行工具 + if (config_.getBool("tools", "enable_python_execute", true)) { + available_tools_->addTool(std::make_unique()); + } + + // 在这里添加其他Manus特定的工具 + // 根据配置决定是否添加其他工具 + if (config_.getBool("tools", "enable_google_search", false)) { + // available_tools_->addTool(std::make_unique()); + std::cout << "Google搜索工具尚未实现" << std::endl; + } + + if (config_.getBool("tools", "enable_browser_use", false)) { + // available_tools_->addTool(std::make_unique()); + std::cout << "浏览器使用工具尚未实现" << std::endl; + } + + if (config_.getBool("tools", "enable_file_saver", false)) { + // available_tools_->addTool(std::make_unique()); + std::cout << "文件保存工具尚未实现" << std::endl; + } + + std::cout << "添加Manus特定的工具..." << std::endl; +} + +} // namespace openmanus \ No newline at end of file diff --git a/src/tool_base.cpp b/src/tool_base.cpp new file mode 100644 index 0000000..bd80f82 --- /dev/null +++ b/src/tool_base.cpp @@ -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 \ No newline at end of file diff --git a/src/tool_call.cpp b/src/tool_call.cpp new file mode 100644 index 0000000..299dee0 --- /dev/null +++ b/src/tool_call.cpp @@ -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 name = json.contains("name") ? json["name"].get() : ""; + 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 \ No newline at end of file diff --git a/src/tool_call_agent.cpp b/src/tool_call_agent.cpp new file mode 100644 index 0000000..8afbc9f --- /dev/null +++ b/src/tool_call_agent.cpp @@ -0,0 +1,494 @@ +#include "include/tool_call_agent.h" +#include "mcp/include/mcp_tool.h" +#include +#include + +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(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(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(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(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(); + 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()); + } 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& 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 \ No newline at end of file diff --git a/src/tool_collection.cpp b/src/tool_collection.cpp new file mode 100644 index 0000000..9d328ce --- /dev/null +++ b/src/tool_collection.cpp @@ -0,0 +1,35 @@ +#include "include/tool_collection.h" + +namespace openmanus { + +ToolCollection::ToolCollection() { + // 初始化工具集合 +} + +void ToolCollection::addTool(std::unique_ptr 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 ToolCollection::getToolNames() const { + std::vector names; + names.reserve(tools_.size()); + + for (const auto& pair : tools_) { + names.push_back(pair.first); + } + + return names; +} + +} // namespace openmanus \ No newline at end of file diff --git a/src/tools/planning.cpp b/src/tools/planning.cpp new file mode 100644 index 0000000..9a4f3d3 --- /dev/null +++ b/src/tools/planning.cpp @@ -0,0 +1,242 @@ +#include "include/tools/planning.h" +#include "mcp/include/mcp_tool.h" +#include +#include +#include + +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 \ No newline at end of file diff --git a/src/tools/python_execute.cpp b/src/tools/python_execute.cpp new file mode 100644 index 0000000..b6e287f --- /dev/null +++ b/src/tools/python_execute.cpp @@ -0,0 +1,48 @@ +#include "include/tools/python_execute.h" +#include "mcp/include/mcp_client.h" +#include "mcp/include/mcp_tool.h" +#include + +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 \ No newline at end of file diff --git a/src/tools/terminate.cpp b/src/tools/terminate.cpp new file mode 100644 index 0000000..882e454 --- /dev/null +++ b/src/tools/terminate.cpp @@ -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 \ No newline at end of file