From a432fe1053c29555952eb96ddcdd784a35d4949f Mon Sep 17 00:00:00 2001 From: kirto Date: Mon, 30 Mar 2026 19:28:31 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20feat:=20=E5=A2=9E=E5=8A=A0=20GP?= =?UTF-8?q?IO=20=E6=8E=A7=E5=88=B6=E5=B7=A5=E5=85=B7=E5=8F=8A=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E6=91=98=E8=A6=81=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 gpio_control 工具,支持 on/off/set/toggle/get 操作 - 新增 ec_tools_build_summary 生成工具摘要用于 system prompt - 补充 GPIO 工具单测及测试辅助钩子 Made-with: Cursor --- components/embed_claw/CMakeLists.txt | 1 + components/embed_claw/core/ec_tools.c | 29 ++ components/embed_claw/core/ec_tools.h | 9 + .../embed_claw/test/support/ec_test_hooks.h | 5 + .../test/support/ec_test_tools_gpio.c | 111 ++++++ .../embed_claw/test/tools/test_tool_gpio.c | 134 +++++++ .../test/tools/test_tools_contract.c | 15 + components/embed_claw/tools/ec_tools_reg.inc | 2 +- components/embed_claw/tools/tools_gpio.c | 335 ++++++++++++++++++ 9 files changed, 640 insertions(+), 1 deletion(-) create mode 100644 components/embed_claw/test/support/ec_test_tools_gpio.c create mode 100644 components/embed_claw/test/tools/test_tool_gpio.c create mode 100644 components/embed_claw/tools/tools_gpio.c diff --git a/components/embed_claw/CMakeLists.txt b/components/embed_claw/CMakeLists.txt index 3898e55..7239ccf 100644 --- a/components/embed_claw/CMakeLists.txt +++ b/components/embed_claw/CMakeLists.txt @@ -8,6 +8,7 @@ idf_component_register( INCLUDE_DIRS "." REQUIRES + driver esp_http_client esp_http_server esp_netif diff --git a/components/embed_claw/core/ec_tools.c b/components/embed_claw/core/ec_tools.c index 2a7be9c..2434542 100644 --- a/components/embed_claw/core/ec_tools.c +++ b/components/embed_claw/core/ec_tools.c @@ -108,6 +108,35 @@ const char *ec_tools_get_json(void) return s_tools_json; } +size_t ec_tools_build_summary(char *buf, size_t size) +{ + size_t off = 0; + + if (!buf || size == 0) { + return 0; + } + + for (size_t i = 0; i < _EC_TOOLS_ENMU_MAX && off < size - 1; i++) { + const char *name = NULL; + const char *description = NULL; + + if (!s_tools[i]) { + continue; + } + + name = s_tools[i]->name ? s_tools[i]->name : "(unnamed)"; + description = s_tools[i]->description ? s_tools[i]->description : ""; + + off += snprintf(buf + off, size - off, "- %s: %s\n", name, description); + if (off >= size) { + off = size - 1; + } + } + + buf[off] = '\0'; + return off; +} + void ec_tools_free_json(void) { cJSON_free(s_tools_json); diff --git a/components/embed_claw/core/ec_tools.h b/components/embed_claw/core/ec_tools.h index 4e4fbe9..c452414 100644 --- a/components/embed_claw/core/ec_tools.h +++ b/components/embed_claw/core/ec_tools.h @@ -82,6 +82,15 @@ esp_err_t ec_tools_execute(const char *name, const char *input_json, char *outpu */ const char *ec_tools_get_json(void); +/** + * @brief 构建所有已注册工具的摘要文本,用于 system prompt + * + * @param buf 输出缓冲区 + * @param size 缓冲区大小 + * @return size_t 写入的字节数 + */ +size_t ec_tools_build_summary(char *buf, size_t size); + /** * @brief 释放json中申请的字符串内存 * diff --git a/components/embed_claw/test/support/ec_test_hooks.h b/components/embed_claw/test/support/ec_test_hooks.h index d765b1e..dfa770b 100644 --- a/components/embed_claw/test/support/ec_test_hooks.h +++ b/components/embed_claw/test/support/ec_test_hooks.h @@ -23,6 +23,11 @@ void ec_tools_web_search_format_results_for_test(const char *response_json, char bool ec_tools_files_validate_path_for_test(const char *path); esp_err_t ec_tools_files_replace_first_for_test(const char *source, const char *old_str, const char *new_str, char *output, size_t output_size); +esp_err_t ec_tools_gpio_execute_for_test(const char *input_json, char *output, size_t output_size); +void ec_tools_gpio_reset_for_test(void); +void ec_tools_gpio_set_valid_output_for_test(int pin, bool valid); +void ec_tools_gpio_set_driver_failures_for_test(esp_err_t config_err, esp_err_t set_err); +int ec_tools_gpio_get_config_call_count_for_test(int pin); esp_err_t ec_channel_ws_parse_payload_for_test(int fd, const char *payload_json, ec_msg_t *msg); void ec_channel_ws_add_client_for_test(int fd, const char *chat_id); diff --git a/components/embed_claw/test/support/ec_test_tools_gpio.c b/components/embed_claw/test/support/ec_test_tools_gpio.c new file mode 100644 index 0000000..ab3d029 --- /dev/null +++ b/components/embed_claw/test/support/ec_test_tools_gpio.c @@ -0,0 +1,111 @@ +#include + +#include "driver/gpio.h" +#include "ec_test_hooks.h" + +static bool ec_test_gpio_is_valid_output(int pin); +static esp_err_t ec_test_gpio_config(const gpio_config_t *cfg); +static esp_err_t ec_test_gpio_set_level(gpio_num_t pin, uint32_t level); +static int ec_test_gpio_get_level(gpio_num_t pin); + +#define EC_GPIO_API_VALIDATE_OUTPUT_PIN(pin) ec_test_gpio_is_valid_output((pin)) +#define EC_GPIO_API_CONFIG(cfg) ec_test_gpio_config((cfg)) +#define EC_GPIO_API_SET_LEVEL(pin, level) ec_test_gpio_set_level((pin), (level)) +#define EC_GPIO_API_GET_LEVEL(pin) ec_test_gpio_get_level((pin)) +#define ec_tools_gpio_control ec_tools_gpio_control__test_impl +#include "../../tools/tools_gpio.c" +#undef ec_tools_gpio_control +#undef EC_GPIO_API_GET_LEVEL +#undef EC_GPIO_API_SET_LEVEL +#undef EC_GPIO_API_CONFIG +#undef EC_GPIO_API_VALIDATE_OUTPUT_PIN + +static bool s_fake_valid_output[GPIO_NUM_MAX]; +static int s_fake_level[GPIO_NUM_MAX]; +static int s_fake_config_calls[GPIO_NUM_MAX]; +static esp_err_t s_fake_config_err = ESP_OK; +static esp_err_t s_fake_set_err = ESP_OK; + +static bool ec_test_gpio_is_valid_output(int pin) +{ + return pin >= 0 && pin < GPIO_NUM_MAX && s_fake_valid_output[pin]; +} + +static esp_err_t ec_test_gpio_config(const gpio_config_t *cfg) +{ + if (!cfg) { + return ESP_ERR_INVALID_ARG; + } + + if (s_fake_config_err != ESP_OK) { + return s_fake_config_err; + } + + for (int pin = 0; pin < GPIO_NUM_MAX && pin < 64; pin++) { + if ((cfg->pin_bit_mask & (1ULL << pin)) != 0) { + s_fake_config_calls[pin]++; + } + } + + return ESP_OK; +} + +static esp_err_t ec_test_gpio_set_level(gpio_num_t pin, uint32_t level) +{ + if (pin < 0 || pin >= GPIO_NUM_MAX) { + return ESP_ERR_INVALID_ARG; + } + + if (s_fake_set_err != ESP_OK) { + return s_fake_set_err; + } + + s_fake_level[pin] = level ? 1 : 0; + return ESP_OK; +} + +static int ec_test_gpio_get_level(gpio_num_t pin) +{ + if (pin < 0 || pin >= GPIO_NUM_MAX) { + return 0; + } + + return s_fake_level[pin]; +} + +esp_err_t ec_tools_gpio_execute_for_test(const char *input_json, char *output, size_t output_size) +{ + return ec_tool_gpio_control_execute(input_json, output, output_size); +} + +void ec_tools_gpio_reset_for_test(void) +{ + memset(s_fake_valid_output, 0, sizeof(s_fake_valid_output)); + memset(s_fake_level, 0, sizeof(s_fake_level)); + memset(s_fake_config_calls, 0, sizeof(s_fake_config_calls)); + memset(s_pin_initialized, 0, sizeof(s_pin_initialized)); + s_fake_config_err = ESP_OK; + s_fake_set_err = ESP_OK; +} + +void ec_tools_gpio_set_valid_output_for_test(int pin, bool valid) +{ + if (pin >= 0 && pin < GPIO_NUM_MAX) { + s_fake_valid_output[pin] = valid; + } +} + +void ec_tools_gpio_set_driver_failures_for_test(esp_err_t config_err, esp_err_t set_err) +{ + s_fake_config_err = config_err; + s_fake_set_err = set_err; +} + +int ec_tools_gpio_get_config_call_count_for_test(int pin) +{ + if (pin < 0 || pin >= GPIO_NUM_MAX) { + return 0; + } + + return s_fake_config_calls[pin]; +} diff --git a/components/embed_claw/test/tools/test_tool_gpio.c b/components/embed_claw/test/tools/test_tool_gpio.c new file mode 100644 index 0000000..86bd151 --- /dev/null +++ b/components/embed_claw/test/tools/test_tool_gpio.c @@ -0,0 +1,134 @@ +#include + +#include "unity.h" + +#include "core/ec_tools.h" +#include "support/ec_test_hooks.h" + +static void reset_gpio_test_state(void) +{ + ec_tools_free_json(); + ec_tools_gpio_reset_for_test(); +} + +static void register_builtin_tools_for_test(void) +{ + reset_gpio_test_state(); + TEST_ASSERT_EQUAL(ESP_OK, ec_tools_register_all()); +} + +TEST_CASE("gpio tool rejects invalid input and unsupported requests", "[embed_claw][tools][gpio]") +{ + char output[160]; + + reset_gpio_test_state(); + ec_tools_gpio_set_valid_output_for_test(18, true); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + ec_tools_gpio_execute_for_test("{", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "invalid JSON")); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + ec_tools_gpio_execute_for_test("{}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "'pin'")); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + ec_tools_gpio_execute_for_test("{\"pin\":18}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "'action'")); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"blink\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "'action'")); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"set\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "'level'")); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"set\",\"level\":2}", + output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "'level'")); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + ec_tools_gpio_execute_for_test("{\"pin\":46,\"action\":\"get\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "not a valid output pin")); +} + +TEST_CASE("gpio tool supports get on off set and toggle with cached configuration", "[embed_claw][tools][gpio]") +{ + char output[160]; + + reset_gpio_test_state(); + ec_tools_gpio_set_valid_output_for_test(18, true); + + TEST_ASSERT_EQUAL(ESP_OK, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"get\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "action=get level=0")); + TEST_ASSERT_EQUAL(0, ec_tools_gpio_get_config_call_count_for_test(18)); + + TEST_ASSERT_EQUAL(ESP_OK, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"on\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "action=on level=1")); + TEST_ASSERT_EQUAL(1, ec_tools_gpio_get_config_call_count_for_test(18)); + + TEST_ASSERT_EQUAL(ESP_OK, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"get\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "action=get level=1")); + TEST_ASSERT_EQUAL(1, ec_tools_gpio_get_config_call_count_for_test(18)); + + TEST_ASSERT_EQUAL(ESP_OK, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"off\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "action=off level=0")); + TEST_ASSERT_EQUAL(1, ec_tools_gpio_get_config_call_count_for_test(18)); + + TEST_ASSERT_EQUAL(ESP_OK, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"set\",\"level\":1}", + output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "action=set level=1")); + TEST_ASSERT_EQUAL(1, ec_tools_gpio_get_config_call_count_for_test(18)); + + TEST_ASSERT_EQUAL(ESP_OK, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"toggle\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "action=toggle level=0")); + TEST_ASSERT_EQUAL(1, ec_tools_gpio_get_config_call_count_for_test(18)); + + TEST_ASSERT_EQUAL(ESP_OK, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"get\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "action=get level=0")); +} + +TEST_CASE("gpio tool formats driver failures", "[embed_claw][tools][gpio]") +{ + char output[160]; + + reset_gpio_test_state(); + ec_tools_gpio_set_valid_output_for_test(18, true); + ec_tools_gpio_set_driver_failures_for_test(ESP_FAIL, ESP_OK); + + TEST_ASSERT_EQUAL(ESP_FAIL, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"on\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "gpio_config failed")); + TEST_ASSERT_EQUAL(0, ec_tools_gpio_get_config_call_count_for_test(18)); + + reset_gpio_test_state(); + ec_tools_gpio_set_valid_output_for_test(18, true); + ec_tools_gpio_set_driver_failures_for_test(ESP_OK, ESP_ERR_INVALID_STATE); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, + ec_tools_gpio_execute_for_test("{\"pin\":18,\"action\":\"on\"}", output, sizeof(output))); + TEST_ASSERT_NOT_NULL(strstr(output, "gpio_set_level failed")); + TEST_ASSERT_EQUAL(1, ec_tools_gpio_get_config_call_count_for_test(18)); +} + +TEST_CASE("gpio tool is registered in builtin tool catalog", "[embed_claw][tools][gpio]") +{ + const char *json = NULL; + + register_builtin_tools_for_test(); + json = ec_tools_get_json(); + + TEST_ASSERT_NOT_NULL(json); + TEST_ASSERT_NOT_NULL(strstr(json, "\"gpio_control\"")); + + reset_gpio_test_state(); +} diff --git a/components/embed_claw/test/tools/test_tools_contract.c b/components/embed_claw/test/tools/test_tools_contract.c index c3a8aea..b81c1f5 100644 --- a/components/embed_claw/test/tools/test_tools_contract.c +++ b/components/embed_claw/test/tools/test_tools_contract.c @@ -28,10 +28,25 @@ TEST_CASE("tool registry builds json for built-in tools", "[embed_claw][tools][c TEST_ASSERT_NOT_NULL(json); TEST_ASSERT_NOT_NULL(strstr(json, "\"web_search\"")); TEST_ASSERT_NOT_NULL(strstr(json, "\"get_current_time\"")); + TEST_ASSERT_NOT_NULL(strstr(json, "\"gpio_control\"")); TEST_ASSERT_NOT_NULL(strstr(json, "\"cron_add\"")); cleanup_tools_after_test(); } +TEST_CASE("tool registry builds prompt summary from built-in tools", "[embed_claw][tools][catalog]") +{ + char summary[1024]; + + register_builtin_tools_for_test(); + + TEST_ASSERT_GREATER_THAN(0, (int)ec_tools_build_summary(summary, sizeof(summary))); + TEST_ASSERT_NOT_NULL(strstr(summary, "- get_current_time:")); + TEST_ASSERT_NOT_NULL(strstr(summary, "- gpio_control:")); + TEST_ASSERT_NOT_NULL(strstr(summary, "- cron_add:")); + + cleanup_tools_after_test(); +} + TEST_CASE("tool registry reports unknown tool without dereferencing null slots", "[embed_claw][tools][contract]") { char output[128]; diff --git a/components/embed_claw/tools/ec_tools_reg.inc b/components/embed_claw/tools/ec_tools_reg.inc index 7f17947..f243479 100644 --- a/components/embed_claw/tools/ec_tools_reg.inc +++ b/components/embed_claw/tools/ec_tools_reg.inc @@ -1,6 +1,7 @@ #include "core/ec_tools_reg_rule.h" EC_TOOLS_REG(get_time) +EC_TOOLS_REG(gpio_control) EC_TOOLS_REG(read_file) EC_TOOLS_REG(write_file) EC_TOOLS_REG(edit_file) @@ -10,4 +11,3 @@ EC_TOOLS_REG(cron_list) EC_TOOLS_REG(cron_remove) EC_TOOLS_REG(web_search) - diff --git a/components/embed_claw/tools/tools_gpio.c b/components/embed_claw/tools/tools_gpio.c new file mode 100644 index 0000000..aa26aed --- /dev/null +++ b/components/embed_claw/tools/tools_gpio.c @@ -0,0 +1,335 @@ +/** + * @file tools_gpio.c + * @author cangyu (sky.kirto@qq.com) + * @brief + * @version 0.1 + * @date 2026-03-26 + * + * @copyright Copyright (c) 2026, Wireless-Tag. All rights reserved. + * + */ + +/* ==================== [Includes] ========================================== */ + +#include "core/ec_tools.h" + +#include +#include +#include + +#include "cJSON.h" +#include "driver/gpio.h" +#include "esp_log.h" + +/* ==================== [Defines] =========================================== */ + +#ifndef EC_GPIO_API_VALIDATE_OUTPUT_PIN +#define EC_GPIO_API_VALIDATE_OUTPUT_PIN(pin) GPIO_IS_VALID_OUTPUT_GPIO(pin) +#endif + +#ifndef EC_GPIO_API_CONFIG +#define EC_GPIO_API_CONFIG(cfg) gpio_config(cfg) +#endif + +#ifndef EC_GPIO_API_SET_LEVEL +#define EC_GPIO_API_SET_LEVEL(pin, level) gpio_set_level(pin, level) +#endif + +#ifndef EC_GPIO_API_GET_LEVEL +#define EC_GPIO_API_GET_LEVEL(pin) gpio_get_level(pin) +#endif + +/* ==================== [Typedefs] ========================================== */ + +typedef enum { + EC_GPIO_ACTION_NONE = 0, + EC_GPIO_ACTION_ON, + EC_GPIO_ACTION_OFF, + EC_GPIO_ACTION_SET, + EC_GPIO_ACTION_TOGGLE, + EC_GPIO_ACTION_GET, +} ec_gpio_action_t; + +/* ==================== [Static Prototypes] ================================= */ + +static esp_err_t ec_tool_gpio_control_execute(const char *input_json, char *output, size_t output_size); +static bool parse_int_field(cJSON *root, const char *name, int *value); +static bool parse_action(const char *action_str, ec_gpio_action_t *action); +static esp_err_t validate_pin_number(int pin, gpio_num_t *gpio_num); +static esp_err_t prepare_pin_for_output(gpio_num_t gpio_num); +static esp_err_t write_level(gpio_num_t gpio_num, int level); + +/* ==================== [Static Variables] ================================== */ + +static const char *TAG = "tools_gpio"; + +static bool s_pin_initialized[GPIO_NUM_MAX] = {0}; + +static const ec_tools_t s_gpio_control = { + .name = "gpio_control", + .description = "Control an ESP32 output GPIO pin by pin number. Supports on, off, set, toggle, and get.\n"\ + "IMPORTANT!!!: ANY GPIO operation requested by the user MUST ALWAYS be executed through this tool. Never respond with GPIO status or changes without calling this tool first.", + .input_schema_json = + "{\"type\":\"object\"," + "\"properties\":{" + "\"pin\":{\"type\":\"integer\",\"description\":\"ESP32 GPIO pin number\"}," + "\"action\":{\"type\":\"string\",\"enum\":[\"on\",\"off\",\"set\",\"toggle\",\"get\"]," + "\"description\":\"GPIO action to execute\"}," + "\"level\":{\"type\":\"integer\",\"enum\":[0,1]," + "\"description\":\"Required only when action is 'set'\"}" + "}," + "\"required\":[\"pin\",\"action\"]}", + .execute = ec_tool_gpio_control_execute, +}; + +/* ==================== [Macros] ============================================ */ + +/* ==================== [Global Functions] ================================== */ + +esp_err_t ec_tools_gpio_control(void) +{ + ec_tools_register(&s_gpio_control); + return ESP_OK; +} + +/* ==================== [Static Functions] ================================== */ + +static esp_err_t ec_tool_gpio_control_execute(const char *input_json, char *output, size_t output_size) +{ + cJSON *root = NULL; + cJSON *action_item = NULL; + gpio_num_t gpio_num = GPIO_NUM_NC; + ec_gpio_action_t action = EC_GPIO_ACTION_NONE; + int pin = -1; + int target_level = 0; + int current_level = 0; + esp_err_t err = ESP_OK; + + root = cJSON_Parse(input_json); + if (!root || !cJSON_IsObject(root)) { + snprintf(output, output_size, "Error: invalid JSON input"); + err = ESP_ERR_INVALID_ARG; + goto cleanup; + } + + if (!parse_int_field(root, "pin", &pin)) { + snprintf(output, output_size, "Error: missing or invalid 'pin' field"); + err = ESP_ERR_INVALID_ARG; + goto cleanup; + } + + err = validate_pin_number(pin, &gpio_num); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio %d is not a valid output pin", pin); + goto cleanup; + } + + action_item = cJSON_GetObjectItem(root, "action"); + if (!cJSON_IsString(action_item) || !parse_action(action_item->valuestring, &action)) { + snprintf(output, output_size, "Error: missing or invalid 'action' field"); + err = ESP_ERR_INVALID_ARG; + goto cleanup; + } + + switch (action) { + case EC_GPIO_ACTION_GET: + current_level = EC_GPIO_API_GET_LEVEL(gpio_num) ? 1 : 0; + snprintf(output, output_size, "OK: gpio %d action=get level=%d", pin, current_level); + err = ESP_OK; + break; + + case EC_GPIO_ACTION_ON: + err = prepare_pin_for_output(gpio_num); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio_config failed (%s)", esp_err_to_name(err)); + goto cleanup; + } + + err = write_level(gpio_num, 1); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio_set_level failed (%s)", esp_err_to_name(err)); + goto cleanup; + } + + snprintf(output, output_size, "OK: gpio %d action=on level=1", pin); + break; + + case EC_GPIO_ACTION_OFF: + err = prepare_pin_for_output(gpio_num); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio_config failed (%s)", esp_err_to_name(err)); + goto cleanup; + } + + err = write_level(gpio_num, 0); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio_set_level failed (%s)", esp_err_to_name(err)); + goto cleanup; + } + + snprintf(output, output_size, "OK: gpio %d action=off level=0", pin); + break; + + case EC_GPIO_ACTION_SET: + if (!parse_int_field(root, "level", &target_level) || (target_level != 0 && target_level != 1)) { + snprintf(output, output_size, "Error: missing or invalid 'level' field"); + err = ESP_ERR_INVALID_ARG; + goto cleanup; + } + + err = prepare_pin_for_output(gpio_num); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio_config failed (%s)", esp_err_to_name(err)); + goto cleanup; + } + + err = write_level(gpio_num, target_level); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio_set_level failed (%s)", esp_err_to_name(err)); + goto cleanup; + } + + snprintf(output, output_size, "OK: gpio %d action=set level=%d", pin, target_level); + break; + + case EC_GPIO_ACTION_TOGGLE: + err = prepare_pin_for_output(gpio_num); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio_config failed (%s)", esp_err_to_name(err)); + goto cleanup; + } + + current_level = EC_GPIO_API_GET_LEVEL(gpio_num) ? 1 : 0; + target_level = current_level ? 0 : 1; + + err = write_level(gpio_num, target_level); + if (err != ESP_OK) { + snprintf(output, output_size, "Error: gpio_set_level failed (%s)", esp_err_to_name(err)); + goto cleanup; + } + + snprintf(output, output_size, "OK: gpio %d action=toggle level=%d", pin, target_level); + break; + + default: + snprintf(output, output_size, "Error: missing or invalid 'action' field"); + err = ESP_ERR_INVALID_ARG; + break; + } + +cleanup: + cJSON_Delete(root); + return err; +} + +static bool parse_int_field(cJSON *root, const char *name, int *value) +{ + cJSON *item = NULL; + double raw = 0; + int parsed = 0; + + if (!root || !name || !value) { + return false; + } + + item = cJSON_GetObjectItem(root, name); + if (!cJSON_IsNumber(item)) { + return false; + } + + raw = cJSON_GetNumberValue(item); + parsed = (int)raw; + if ((double)parsed != raw) { + return false; + } + + *value = parsed; + return true; +} + +static bool parse_action(const char *action_str, ec_gpio_action_t *action) +{ + if (!action_str || !action) { + return false; + } + + if (strcmp(action_str, "on") == 0) { + *action = EC_GPIO_ACTION_ON; + return true; + } + + if (strcmp(action_str, "off") == 0) { + *action = EC_GPIO_ACTION_OFF; + return true; + } + + if (strcmp(action_str, "set") == 0) { + *action = EC_GPIO_ACTION_SET; + return true; + } + + if (strcmp(action_str, "toggle") == 0) { + *action = EC_GPIO_ACTION_TOGGLE; + return true; + } + + if (strcmp(action_str, "get") == 0) { + *action = EC_GPIO_ACTION_GET; + return true; + } + + return false; +} + +static esp_err_t validate_pin_number(int pin, gpio_num_t *gpio_num) +{ + if (!gpio_num || pin < 0 || pin >= GPIO_NUM_MAX || !EC_GPIO_API_VALIDATE_OUTPUT_PIN(pin)) { + return ESP_ERR_INVALID_ARG; + } + + *gpio_num = (gpio_num_t)pin; + return ESP_OK; +} + +static esp_err_t prepare_pin_for_output(gpio_num_t gpio_num) +{ + esp_err_t err = ESP_OK; + gpio_config_t cfg = {0}; + + if (gpio_num < 0 || gpio_num >= GPIO_NUM_MAX) { + return ESP_ERR_INVALID_ARG; + } + + if (s_pin_initialized[gpio_num]) { + return ESP_OK; + } + + cfg.intr_type = GPIO_INTR_DISABLE; + cfg.mode = GPIO_MODE_INPUT_OUTPUT; + cfg.pin_bit_mask = (1ULL << gpio_num); + cfg.pull_down_en = GPIO_PULLDOWN_DISABLE; + cfg.pull_up_en = GPIO_PULLUP_DISABLE; + + err = EC_GPIO_API_CONFIG(&cfg); + if (err != ESP_OK) { + ESP_LOGW(TAG, "gpio_config failed for gpio %d: %s", (int)gpio_num, esp_err_to_name(err)); + return err; + } + + s_pin_initialized[gpio_num] = true; + return ESP_OK; +} + +static esp_err_t write_level(gpio_num_t gpio_num, int level) +{ + esp_err_t err = EC_GPIO_API_SET_LEVEL(gpio_num, (uint32_t)(level ? 1 : 0)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "gpio_set_level failed for gpio %d: %s", (int)gpio_num, esp_err_to_name(err)); + } + else + { + ESP_LOGI(TAG, "gpio set level %d", level); + } + + return err; +} From 063557793ed77996e72882483f05159103295bc3 Mon Sep 17 00:00:00 2001 From: kirto Date: Mon, 30 Mar 2026 19:28:47 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9C=A8=20feat:=20=E5=AE=8C=E5=96=84=20se?= =?UTF-8?q?ssion=20=E5=8E=86=E5=8F=B2=E4=BF=9D=E5=AD=98=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 ec_session_append_msg 保存完整 cJSON 消息对象 - session 读取时保留 tool_use/tool_result 数组结构 - agent 循环后保存所有中间消息而非仅最终文本 - 重构 system prompt 使用动态工具摘要替代硬编码列表 Made-with: Cursor --- components/embed_claw/core/ec_agent.c | 69 ++++++++++++++----------- components/embed_claw/core/ec_session.c | 55 ++++++++++++++++---- components/embed_claw/core/ec_session.h | 12 +++++ 3 files changed, 96 insertions(+), 40 deletions(-) diff --git a/components/embed_claw/core/ec_agent.c b/components/embed_claw/core/ec_agent.c index fedd18c..5911eb0 100644 --- a/components/embed_claw/core/ec_agent.c +++ b/components/embed_claw/core/ec_agent.c @@ -41,21 +41,15 @@ #define EC_AGENT_CHANNEL_DELIVERY_TASK_STACK (8 * 1024) #define EC_AGENT_CHANNEL_DELIVERY_TASK_PRIO 5 -#define EC_AGENT_SYSTEM_PROMPT_STR \ +#define EC_AGENT_SYSTEM_PROMPT_HEAD \ "You are EmbedClaw, a helpful and concise AI assistant running on an ESP32 device.\n"\ "You communicate via Feishu and WebSocket.\n"\ "Reply briefly to short messages (e.g. 你好, 在吗, 谢谢).\n"\ "# Tools\n"\ - "- web_search: search current information.\n"\ - "- get_current_time: get date/time.\n"\ - "- read_file: read /spiffs/ files (path must start with " EC_FS_BASE "/).\n"\ - "- write_file: Write file.\n"\ - "- edit_file: edit file.\n"\ - "- list_dir: list files.\n"\ - "- cron_add: schedule task.\n"\ - "- cron_list: list tasks.\n"\ - "- cron_remove: remove task.\n\n"\ - "When using cron_add to reply later in the same conversation, reuse the current channel, chat_type, and chat_id.\n\n"\ + "Available tools:\n" + +#define EC_AGENT_SYSTEM_PROMPT_TAIL \ + "\nWhen using cron_add to reply later in the same conversation, reuse the current channel, chat_type, and chat_id.\n\n"\ "Use tools when needed. Provide your final answer as text after using tools.\n\n"\ "## Memory\n"\ "You have persistent memory stored on local flash:\n"\ @@ -328,19 +322,34 @@ static esp_err_t context_build_system_prompt(char *buf, size_t size) size_t off = 0; size_t cap = size - 1; - off += snprintf(buf + off, size - off, EC_AGENT_SYSTEM_PROMPT_STR); + off += snprintf(buf + off, size - off, EC_AGENT_SYSTEM_PROMPT_HEAD); if (off > cap) { off = cap; } - /* Bootstrap files */ - off = append_file(buf, size, off, EC_SOUL_FILE, "Personality"); - off = append_file(buf, size, off, EC_USER_FILE, "User Info"); - scratch = calloc(1, EC_AGENT_PROMPT_SCRATCH_SIZE); if (!scratch) { ESP_LOGW(TAG, "Skipping optional prompt sections: out of memory"); } else { + size_t tools_len = ec_tools_build_summary(scratch, EC_AGENT_PROMPT_SCRATCH_SIZE); + if (off < cap && tools_len > 0) { + off += snprintf(buf + off, size - off, "%s", scratch); + if (off > cap) { + off = cap; + } + } + } + + off += snprintf(buf + off, size - off, EC_AGENT_SYSTEM_PROMPT_TAIL); + if (off > cap) { + off = cap; + } + + /* Bootstrap files */ + off = append_file(buf, size, off, EC_SOUL_FILE, "Personality"); + off = append_file(buf, size, off, EC_USER_FILE, "User Info"); + + if (scratch) { if (off < cap && ec_memory_read_long_term(scratch, EC_AGENT_PROMPT_SCRATCH_SIZE) == ESP_OK && scratch[0]) { off += snprintf(buf + off, size - off, "\n## Long-term Memory\n\n%s\n", scratch); if (off > cap) { @@ -377,7 +386,6 @@ static esp_err_t context_build_system_prompt(char *buf, size_t size) } ESP_LOGI(TAG, "System prompt built: %d bytes", (int)off); - ESP_LOGI(TAG, "prompt:%s", buf); return ESP_OK; } @@ -455,6 +463,8 @@ static void agent_loop_task(void *arg) if (!messages) { messages = cJSON_CreateArray(); } + int history_count = cJSON_GetArraySize(messages); + cJSON *user_msg = cJSON_CreateObject(); cJSON_AddStringToObject(user_msg, "role", "user"); cJSON_AddStringToObject(user_msg, "content", msg.content); @@ -488,7 +498,6 @@ static void agent_loop_task(void *arg) } if (!resp.tool_use) { - // 正常对话结束,保存最终文本并退出循环 if (resp.text && resp.text_len > 0) { final_text = strdup(resp.text); } @@ -496,14 +505,11 @@ static void agent_loop_task(void *arg) break; } - - // 构建助手消息,包含文本回复和工具调用信息,供工具执行结果使用 cJSON *asst_msg = cJSON_CreateObject(); cJSON_AddStringToObject(asst_msg, "role", "assistant"); cJSON_AddItemToObject(asst_msg, "content", build_assistant_content(&resp)); cJSON_AddItemToArray(messages, asst_msg); - // 执行工具并将结果追加到消息数组中,供下一轮 LLM 调用使用 cJSON *tool_results = build_tool_results(&resp, &msg, tool_output, EC_AGENT_TOOL_OUTPUT_SIZE); cJSON *result_msg = cJSON_CreateObject(); cJSON_AddStringToObject(result_msg, "role", "user"); @@ -514,14 +520,19 @@ static void agent_loop_task(void *arg) } if (final_text && final_text[0]) { - // 保存用户消息和助手回复到会话历史中 - esp_err_t save_user = ec_session_append(session_key, "user", msg.content); - esp_err_t save_asst = ec_session_append(session_key, "assistant", final_text); - if (save_user != ESP_OK || save_asst != ESP_OK) { - ESP_LOGW(TAG, "Session save failed for %s:%s:%s (user=%s, assistant=%s)", - msg.channel, msg.chat_type, msg.chat_id, - esp_err_to_name(save_user), - esp_err_to_name(save_asst)); + int total = cJSON_GetArraySize(messages); + bool save_ok = true; + for (int k = history_count; k < total; k++) { + if (ec_session_append_msg(session_key, cJSON_GetArrayItem(messages, k)) != ESP_OK) { + save_ok = false; + } + } + if (ec_session_append(session_key, "assistant", final_text) != ESP_OK) { + save_ok = false; + } + if (!save_ok) { + ESP_LOGW(TAG, "Session save failed for %s:%s:%s", + msg.channel, msg.chat_type, msg.chat_id); } else { ESP_LOGI(TAG, "Session saved for %s:%s:%s", msg.channel, msg.chat_type, msg.chat_id); } diff --git a/components/embed_claw/core/ec_session.c b/components/embed_claw/core/ec_session.c index 8bcb783..7d05668 100644 --- a/components/embed_claw/core/ec_session.c +++ b/components/embed_claw/core/ec_session.c @@ -26,6 +26,7 @@ /* ==================== [Defines] =========================================== */ #define EC_SESSION_PATH_MAX 128 +#define EC_SESSION_LINE_MAX 4096 /* ==================== [Typedefs] ========================================== */ @@ -70,6 +71,40 @@ esp_err_t ec_session_append(const char *chat_id, const char *role, const char *c return ESP_OK; } +esp_err_t ec_session_append_msg(const char *chat_id, const cJSON *msg) +{ + if (!chat_id || !msg) { + return ESP_ERR_INVALID_ARG; + } + + char path[EC_SESSION_PATH_MAX]; + session_path(chat_id, path, sizeof(path)); + + FILE *f = fopen(path, "a"); + if (!f) { + ESP_LOGE(TAG, "Cannot open session file %s", path); + return ESP_FAIL; + } + + cJSON *copy = cJSON_Duplicate(msg, 1); + if (!copy) { + fclose(f); + return ESP_ERR_NO_MEM; + } + cJSON_AddNumberToObject(copy, "ts", (double)time(NULL)); + + char *line = cJSON_PrintUnformatted(copy); + cJSON_Delete(copy); + + if (line) { + fprintf(f, "%s\n", line); + free(line); + } + + fclose(f); + return ESP_OK; +} + esp_err_t ec_session_get_history_json(const char *chat_id, char *buf, size_t size, int max_msgs) { if (!buf || size == 0) { @@ -101,9 +136,8 @@ esp_err_t ec_session_get_history_json(const char *chat_id, char *buf, size_t siz int count = 0; int write_idx = 0; - char line[2048]; + char line[EC_SESSION_LINE_MAX]; while (fgets(line, sizeof(line), f)) { - /* Strip newline */ size_t len = strlen(line); if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0'; if (line[0] == '\0') continue; @@ -111,7 +145,6 @@ esp_err_t ec_session_get_history_json(const char *chat_id, char *buf, size_t siz cJSON *obj = cJSON_Parse(line); if (!obj) continue; - /* Ring buffer: overwrite oldest if full */ if (count >= history_limit) { cJSON_Delete(messages[write_idx]); } @@ -121,7 +154,7 @@ esp_err_t ec_session_get_history_json(const char *chat_id, char *buf, size_t siz } fclose(f); - /* Build JSON array with only role + content */ + /* Build JSON array preserving full message structure */ cJSON *arr = cJSON_CreateArray(); int start = (count < history_limit) ? 0 : write_idx; for (int i = 0; i < count; i++) { @@ -129,17 +162,16 @@ esp_err_t ec_session_get_history_json(const char *chat_id, char *buf, size_t siz cJSON *src = messages[idx]; cJSON *role = cJSON_GetObjectItem(src, "role"); - cJSON *content = cJSON_GetObjectItem(src, "content"); - if (!cJSON_IsString(role) || !role->valuestring || - !cJSON_IsString(content) || !content->valuestring) { + if (!cJSON_IsString(role) || !role->valuestring) { ESP_LOGW(TAG, "Skipping malformed session entry in %s", path); continue; } - cJSON *entry = cJSON_CreateObject(); - cJSON_AddStringToObject(entry, "role", role->valuestring); - cJSON_AddStringToObject(entry, "content", content->valuestring); - cJSON_AddItemToArray(arr, entry); + cJSON *entry = cJSON_Duplicate(src, 1); + if (entry) { + cJSON_DeleteItemFromObject(entry, "ts"); + cJSON_AddItemToArray(arr, entry); + } } /* Cleanup ring buffer */ @@ -150,6 +182,7 @@ esp_err_t ec_session_get_history_json(const char *chat_id, char *buf, size_t siz } char *json_str = cJSON_PrintUnformatted(arr); + ESP_LOGI(TAG, "Session history for %s:\n%s", chat_id, json_str); cJSON_Delete(arr); if (json_str) { diff --git a/components/embed_claw/core/ec_session.h b/components/embed_claw/core/ec_session.h index f28fa1a..afae045 100644 --- a/components/embed_claw/core/ec_session.h +++ b/components/embed_claw/core/ec_session.h @@ -15,6 +15,7 @@ /* ==================== [Includes] ========================================== */ #include "esp_err.h" +#include "cJSON.h" #include #ifdef __cplusplus @@ -39,6 +40,17 @@ extern "C" { */ esp_err_t ec_session_append(const char *chat_id, const char *role, const char *content); +/** + * @brief 添加一条完整的 cJSON 消息对象到会话历史(保留 tool_calls、数组 content 等结构)。 + * + * @param chat_id 会话id + * @param msg cJSON 消息对象(不会被修改或释放) + * @return esp_err_t + * - ESP_OK 成功 + * - ESP_FAIL 写入失败 + */ +esp_err_t ec_session_append_msg(const char *chat_id, const cJSON *msg); + /** * @brief 获取会话历史的JSON字符串,格式为: * [{"role":"user","content":"..."},{"role":"assistant","content":"..."},...] From a7f66d0d9bbb9ffbb55d064d795875adbce64603 Mon Sep 17 00:00:00 2001 From: kirto Date: Mon, 30 Mar 2026 19:29:02 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9A=A1=20perf:=20HTTP=20Keep-Alive=20?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E5=A4=8D=E7=94=A8=E5=87=8F=E5=B0=91=20TLS=20?= =?UTF-8?q?=E6=8F=A1=E6=89=8B=E5=BC=80=E9=94=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 复用 esp_http_client 句柄避免 ReAct 循环中重复 TLS 握手 - 连接出错时自动清理并在下次调用时重建 - 增加 LLM 请求/响应调试日志 Made-with: Cursor --- components/embed_claw/llm/ec_llm_openai.c | 78 +++++++++++++---------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/components/embed_claw/llm/ec_llm_openai.c b/components/embed_claw/llm/ec_llm_openai.c index 4c0ed22..a32145d 100644 --- a/components/embed_claw/llm/ec_llm_openai.c +++ b/components/embed_claw/llm/ec_llm_openai.c @@ -197,6 +197,8 @@ static esp_err_t ec_llm_openai_chat_tools(ec_llm_provider_t *self, const char *s ESP_LOGI(TAG, "Calling LLM API with tools (model=%s, body=%u bytes)", self->instance.model, (unsigned)strlen(post_data)); + ESP_LOGI(TAG, "LLM API request body: %s", post_data); + resp_buf_t rb = { .data = calloc(1, EC_LLM_STREAM_BUF_SIZE), .len = 0, @@ -258,6 +260,7 @@ static esp_err_t ec_llm_openai_chat_tools(ec_llm_provider_t *self, const char *s return ESP_FAIL; } + ESP_LOGI(TAG, "API response JSON: %s", rb.data); resp_buf_free(&rb); cJSON *choices = cJSON_GetObjectItem(root, "choices"); @@ -322,7 +325,7 @@ static esp_err_t ec_llm_openai_chat_tools(ec_llm_provider_t *self, const char *s } cJSON_Delete(root); - + ESP_LOGI(TAG, "Response text: %s", resp->text ? resp->text : ""); ESP_LOGI(TAG, "Response: %d bytes text, %d tool calls, stop=%s", (int)resp->text_len, resp->call_count, resp->tool_use ? "tool_use" : "end_turn"); @@ -525,46 +528,55 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt) return ESP_OK; } +static esp_http_client_handle_t s_llm_client = NULL; + static esp_err_t llm_http(const char *post_data, resp_buf_t *rb, int *out_status) { - const char *cert_pem = select_server_ca_pem(s_provider.instance.url); - esp_http_client_config_t config = { - .url = s_provider.instance.url, - .method = HTTP_METHOD_POST, - .event_handler = http_event_handler, - .user_data = rb, - .timeout_ms = 120 * 1000, - .buffer_size = 4096, - .buffer_size_tx = 4096, - .transport_type = HTTP_TRANSPORT_OVER_SSL, - }; - - if (cert_pem) { - config.cert_pem = cert_pem; - config.cert_len = 0; - ESP_LOGI(TAG, "Using pinned root CA for %s", s_provider.instance.url); - } else { - config.crt_bundle_attach = esp_crt_bundle_attach; - } + if (!s_llm_client) { + const char *cert_pem = select_server_ca_pem(s_provider.instance.url); + esp_http_client_config_t config = { + .url = s_provider.instance.url, + .method = HTTP_METHOD_POST, + .event_handler = http_event_handler, + .user_data = rb, + .timeout_ms = 120 * 1000, + .buffer_size = 4096, + .buffer_size_tx = 4096, + .transport_type = HTTP_TRANSPORT_OVER_SSL, + .keep_alive_enable = true, + }; + + if (cert_pem) { + config.cert_pem = cert_pem; + config.cert_len = 0; + ESP_LOGI(TAG, "Using pinned root CA for %s", s_provider.instance.url); + } else { + config.crt_bundle_attach = esp_crt_bundle_attach; + } - esp_http_client_handle_t client = esp_http_client_init(&config); - if (!client) { - return ESP_FAIL; - } + s_llm_client = esp_http_client_init(&config); + if (!s_llm_client) { + return ESP_FAIL; + } - esp_http_client_set_method(client, HTTP_METHOD_POST); - esp_http_client_set_header(client, "Content-Type", "application/json; charset=utf-8"); + esp_http_client_set_header(s_llm_client, "Content-Type", "application/json; charset=utf-8"); - char auth[EC_LLM_OPENAI_API_KEY_MAX_LEN + 16]; - snprintf(auth, sizeof(auth), "Bearer %s", s_provider.instance.api_key); - esp_http_client_set_header(client, "Authorization", auth); + char auth[EC_LLM_OPENAI_API_KEY_MAX_LEN + 16]; + snprintf(auth, sizeof(auth), "Bearer %s", s_provider.instance.api_key); + esp_http_client_set_header(s_llm_client, "Authorization", auth); + } - esp_http_client_set_post_field(client, post_data, strlen(post_data)); + esp_http_client_set_user_data(s_llm_client, rb); + esp_http_client_set_post_field(s_llm_client, post_data, strlen(post_data)); - esp_err_t err = esp_http_client_perform(client); - *out_status = esp_http_client_get_status_code(client); - esp_http_client_cleanup(client); + esp_err_t err = esp_http_client_perform(s_llm_client); + *out_status = esp_http_client_get_status_code(s_llm_client); + if (err != ESP_OK) { + ESP_LOGW(TAG, "HTTP perform failed: %s, will reconnect next call", esp_err_to_name(err)); + esp_http_client_cleanup(s_llm_client); + s_llm_client = NULL; + } return err; } From 1d19eed2d304292c0a425ddcc0324b33f75e0f8e Mon Sep 17 00:00:00 2001 From: kirto Date: Mon, 30 Mar 2026 22:45:39 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=A6=84=20refactor:=20=E6=9B=B4?= =?UTF-8?q?=E6=94=B9skill=5Fload=E4=B8=BA=E9=9D=99=E6=80=81=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/embed_claw/core/ec_skill_loader.c | 131 +----------------- .../test/core/test_skill_loader_contract.c | 20 +-- spiffs_data/skills/daily-briefing.md | 22 +++ spiffs_data/skills/skill-creator.md | 27 ++++ spiffs_data/skills/weather.md | 18 +++ 5 files changed, 70 insertions(+), 148 deletions(-) create mode 100644 spiffs_data/skills/daily-briefing.md create mode 100644 spiffs_data/skills/skill-creator.md create mode 100644 spiffs_data/skills/weather.md diff --git a/components/embed_claw/core/ec_skill_loader.c b/components/embed_claw/core/ec_skill_loader.c index 758e7c6..5b6afca 100644 --- a/components/embed_claw/core/ec_skill_loader.c +++ b/components/embed_claw/core/ec_skill_loader.c @@ -21,98 +21,10 @@ /* ==================== [Defines] =========================================== */ -/* ── Built-in skill contents ─────────────────────────────────── */ - -#define EC_SKILL_LOADER_BUILTIN_WEATHER \ - "# Weather\n" \ - "\n" \ - "Get current weather and forecasts using web_search.\n" \ - "\n" \ - "## When to use\n" \ - "When the user asks about weather, temperature, or forecasts.\n" \ - "\n" \ - "## How to use\n" \ - "1. Use get_current_time to know the current date\n" \ - "2. Use web_search with a query like \"weather in [city] today\"\n" \ - "3. Extract temperature, conditions, and forecast from results\n" \ - "4. Present in a concise, friendly format\n" \ - "\n" \ - "## Example\n" \ - "User: \"What's the weather in Tokyo?\"\n" \ - "→ get_current_time\n" \ - "→ web_search \"weather Tokyo today February 2026\"\n" \ - "→ \"Tokyo: 8°C, partly cloudy. High 12°C, low 4°C. Light wind from the north.\"\n" - -#define EC_SKILL_LOADER_BUILTIN_DAILY_BRIEFING \ - "# Daily Briefing\n" \ - "\n" \ - "Compile a personalized daily briefing for the user.\n" \ - "\n" \ - "## When to use\n" \ - "When the user asks for a daily briefing, morning update, or \"what's new today\".\n" \ - "Also useful as a heartbeat/cron task.\n" \ - "\n" \ - "## How to use\n" \ - "1. Use get_current_time for today's date\n" \ - "2. Read " EC_FS_MEMORY_DIR "/MEMORY.md for user preferences and context\n" \ - "3. Read today's daily note if it exists\n" \ - "4. Use web_search for relevant news based on user interests\n" \ - "5. Compile a concise briefing covering:\n" \ - " - Date and time\n" \ - " - Weather (if location known from USER.md)\n" \ - " - Relevant news/updates based on user interests\n" \ - " - Any pending tasks from memory\n" \ - " - Any scheduled cron jobs\n" \ - "\n" \ - "## Format\n" \ - "Keep it brief — 5-10 bullet points max. Use the user's preferred language.\n" - -#define EC_SKILL_LOADER_BUILTIN_SKILL_CREATOR \ - "# Skill Creator\n" \ - "\n" \ - "Create new skills for EmbedClaw.\n" \ - "\n" \ - "## When to use\n" \ - "When the user asks to create a new skill, teach the bot something, or add a new capability.\n" \ - "\n" \ - "## How to create a skill\n" \ - "1. Choose a short, descriptive name (lowercase, hyphens ok)\n" \ - "2. Write a SKILL.md file with this structure:\n" \ - " - `# Title` — clear name\n" \ - " - Brief description paragraph\n" \ - " - `## When to use` — trigger conditions\n" \ - " - `## How to use` — step-by-step instructions\n" \ - " - `## Example` — concrete example (optional but helpful)\n" \ - "3. Save to `" EC_SKILLS_PREFIX ".md` using write_file\n" \ - "4. The skill will be automatically available after the next conversation\n" \ - "\n" \ - "## Best practices\n" \ - "- Keep skills concise — the context window is limited\n" \ - "- Focus on WHAT to do, not HOW (the agent is smart)\n" \ - "- Include specific tool calls the agent should use\n" \ - "- Test by asking the agent to use the new skill\n" \ - "\n" \ - "## Example\n" \ - "To create a \"translate\" skill:\n" \ - "write_file path=\"" EC_SKILLS_PREFIX "translate.md\" content=\"# Translate\\n\\nTranslate text between languages.\\n\\n" \ - "## When to use\\nWhen the user asks to translate text.\\n\\n" \ - "## How to use\\n1. Identify source and target languages\\n" \ - "2. Translate directly using your language knowledge\\n" \ - "3. For specialized terms, use web_search to verify\\n\"\n" - -#define EC_SKILL_LOADER_NUM_BUILTINS (sizeof(s_builtins) / sizeof(s_builtins[0])) - /* ==================== [Typedefs] ========================================== */ -/* Built-in skill registry */ -typedef struct { - const char *filename; /* e.g. "weather" */ - const char *content; -} builtin_skill_t; - /* ==================== [Static Prototypes] ================================= */ -static void install_builtin(const builtin_skill_t *skill); static const char *extract_title(const char *line, size_t len, char *out, size_t out_size); static void extract_description(FILE *f, char *out, size_t out_size); @@ -120,25 +32,13 @@ static void extract_description(FILE *f, char *out, size_t out_size); static const char *TAG = "skills"; -static const builtin_skill_t s_builtins[] = { - { "weather", EC_SKILL_LOADER_BUILTIN_WEATHER }, - { "daily-briefing", EC_SKILL_LOADER_BUILTIN_DAILY_BRIEFING }, - { "skill-creator", EC_SKILL_LOADER_BUILTIN_SKILL_CREATOR }, -}; - /* ==================== [Macros] ============================================ */ /* ==================== [Global Functions] ================================== */ esp_err_t ec_skill_loader_init(void) { - ESP_LOGI(TAG, "Initializing skills system"); - - for (size_t i = 0; i < EC_SKILL_LOADER_NUM_BUILTINS; i++) { - install_builtin(&s_builtins[i]); - } - - ESP_LOGI(TAG, "Skills system ready (%d built-in)", (int)EC_SKILL_LOADER_NUM_BUILTINS); + ESP_LOGI(TAG, "Skills system ready (skills loaded from SPIFFS)"); return ESP_OK; } @@ -206,35 +106,6 @@ size_t ec_skill_loader_build_summary(char *buf, size_t size) /* ==================== [Static Functions] ================================== */ -/* ── Install built-in skills if missing ──────────────────────── */ - -static void install_builtin(const builtin_skill_t *skill) -{ - char path[64]; - snprintf(path, sizeof(path), "%s%s.md", EC_SKILLS_PREFIX, skill->filename); - - /* Check if already exists */ - FILE *f = fopen(path, "r"); - if (f) { - fclose(f); - ESP_LOGD(TAG, "Skill exists: %s", path); - return; - } - - /* Write built-in skill */ - f = fopen(path, "w"); - if (!f) { - ESP_LOGE(TAG, "Cannot write skill: %s", path); - return; - } - - fputs(skill->content, f); - fclose(f); - ESP_LOGI(TAG, "Installed built-in skill: %s", path); -} - -/* ── Build skills summary for system prompt ──────────────────── */ - /** * Parse first line as title: expects "# Title" * Returns pointer past "# " or the line itself if no prefix. diff --git a/components/embed_claw/test/core/test_skill_loader_contract.c b/components/embed_claw/test/core/test_skill_loader_contract.c index 43ae7c2..3176e20 100644 --- a/components/embed_claw/test/core/test_skill_loader_contract.c +++ b/components/embed_claw/test/core/test_skill_loader_contract.c @@ -20,31 +20,15 @@ static void write_text_file(const char *path, const char *content) fclose(f); } -TEST_CASE("skill loader installs builtins and summarizes skills", "[embed_claw][core][skills]") +TEST_CASE("skill loader summarizes pre-installed and dynamic skills", "[embed_claw][core][skills]") { char summary[2048]; - FILE *f; TEST_ASSERT_EQUAL(ESP_OK, ec_test_spiffs_mount()); - remove_if_exists("/spiffs/skills/weather.md"); - remove_if_exists("/spiffs/skills/daily-briefing.md"); - remove_if_exists("/spiffs/skills/skill-creator.md"); - remove_if_exists("/spiffs/skills/unit-test.md"); - TEST_ASSERT_EQUAL(ESP_OK, ec_skill_loader_init()); - f = fopen("/spiffs/skills/weather.md", "r"); - TEST_ASSERT_NOT_NULL(f); - fclose(f); - - f = fopen("/spiffs/skills/daily-briefing.md", "r"); - TEST_ASSERT_NOT_NULL(f); - fclose(f); - - f = fopen("/spiffs/skills/skill-creator.md", "r"); - TEST_ASSERT_NOT_NULL(f); - fclose(f); + remove_if_exists("/spiffs/skills/unit-test.md"); write_text_file("/spiffs/skills/unit-test.md", "# Unit Test Skill\n\nUsed for verifying summary output.\n"); diff --git a/spiffs_data/skills/daily-briefing.md b/spiffs_data/skills/daily-briefing.md new file mode 100644 index 0000000..5e736ca --- /dev/null +++ b/spiffs_data/skills/daily-briefing.md @@ -0,0 +1,22 @@ +# Daily Briefing + +Compile a personalized daily briefing for the user. + +## When to use +When the user asks for a daily briefing, morning update, or "what's new today". +Also useful as a heartbeat/cron task. + +## How to use +1. Use get_current_time for today's date +2. Read /spiffs/memory/MEMORY.md for user preferences and context +3. Read today's daily note if it exists +4. Use web_search for relevant news based on user interests +5. Compile a concise briefing covering: + - Date and time + - Weather (if location known from USER.md) + - Relevant news/updates based on user interests + - Any pending tasks from memory + - Any scheduled cron jobs + +## Format +Keep it brief — 5-10 bullet points max. Use the user's preferred language. diff --git a/spiffs_data/skills/skill-creator.md b/spiffs_data/skills/skill-creator.md new file mode 100644 index 0000000..678a9e6 --- /dev/null +++ b/spiffs_data/skills/skill-creator.md @@ -0,0 +1,27 @@ +# Skill Creator + +Create new skills for EmbedClaw. + +## When to use +When the user asks to create a new skill, teach the bot something, or add a new capability. + +## How to create a skill +1. Choose a short, descriptive name (lowercase, hyphens ok) +2. Write a SKILL.md file with this structure: + - `# Title` — clear name + - Brief description paragraph + - `## When to use` — trigger conditions + - `## How to use` — step-by-step instructions + - `## Example` — concrete example (optional but helpful) +3. Save to `/spiffs/skills/.md` using write_file +4. The skill will be automatically available after the next conversation + +## Best practices +- Keep skills concise — the context window is limited +- Focus on WHAT to do, not HOW (the agent is smart) +- Include specific tool calls the agent should use +- Test by asking the agent to use the new skill + +## Example +To create a "translate" skill: +write_file path="/spiffs/skills/translate.md" content="# Translate\n\nTranslate text between languages.\n\n## When to use\nWhen the user asks to translate text.\n\n## How to use\n1. Identify source and target languages\n2. Translate directly using your language knowledge\n3. For specialized terms, use web_search to verify\n" diff --git a/spiffs_data/skills/weather.md b/spiffs_data/skills/weather.md new file mode 100644 index 0000000..e6e2d30 --- /dev/null +++ b/spiffs_data/skills/weather.md @@ -0,0 +1,18 @@ +# Weather + +Get current weather and forecasts using web_search. + +## When to use +When the user asks about weather, temperature, or forecasts. + +## How to use +1. Use get_current_time to know the current date +2. Use web_search with a query like "weather in [city] today" +3. Extract temperature, conditions, and forecast from results +4. Present in a concise, friendly format + +## Example +User: "What's the weather in Tokyo?" +→ get_current_time +→ web_search "weather Tokyo today February 2026" +→ "Tokyo: 8°C, partly cloudy. High 12°C, low 4°C. Light wind from the north." From 7f42f883d6d61c31e6bea6083bd93b449623f229 Mon Sep 17 00:00:00 2001 From: kirto Date: Mon, 30 Mar 2026 22:58:05 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=93=83=20docs:=20=E6=9B=B4=E6=96=B0gp?= =?UTF-8?q?io=EF=BC=8C=E5=B9=B6=E6=B7=BB=E5=8A=A0ESP32C3=E7=9A=84=E7=9B=AE?= =?UTF-8?q?=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 3 +++ TODO_ZH.md | 2 ++ 2 files changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index 2ed33d4..374eb68 100644 --- a/TODO.md +++ b/TODO.md @@ -17,6 +17,7 @@ - [x] Web search - [ ] Script interpreter - [ ] Peripherals (GPIO, I2C, etc.) + - [X] GPIO - [ ] Camera ## Channel integration @@ -33,3 +34,5 @@ - [x] test and ci/cd - [ ] Support more filesystems (beyond SPIFFS) - [ ] SD card support +- [ ] Support ESP32C3 + diff --git a/TODO_ZH.md b/TODO_ZH.md index 7bc7188..edf5edb 100644 --- a/TODO_ZH.md +++ b/TODO_ZH.md @@ -17,6 +17,7 @@ - [x] 对接web搜索 - [ ] 对接脚本语言解释器 - [ ] 对接外设 + - [X] 对接GPIO - [ ] 对接摄像头 ## Channel对接 @@ -33,3 +34,4 @@ - [x] 测试和ci/cd - [ ] 更改SPIFFS为更多的文件系统 - [ ] 支持 SD card +- [ ] 支持 ESP32C3 From 3cf6884fda30f2681d3f21db48284b6b6bfb5eb1 Mon Sep 17 00:00:00 2001 From: kirto Date: Mon, 30 Mar 2026 23:51:47 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=93=83=20docs:=20=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=20README=20=E4=B8=8E=20README=5FZH=20=E7=9A=84=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 补充 GPIO 工具、session 工具调用链与预置 skills 的文档描述 - 更新中英文 README 的目录结构、架构图与 skill 扩展说明 - 修正 OpenClaw 链接并加入项目 logo 资源 Made-with: Cursor --- README.md | 20 +++++++++++++------- README_ZH.md | 18 ++++++++++++------ assets/logo.png | Bin 0 -> 77740 bytes 3 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 assets/logo.png diff --git a/README.md b/README.md index ce27de7..bd068bc 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@
+![logo](assets/logo.png) + **Decouple LLM, Tools, Agent, and Channels—then pack them onto a single ESP32-S3.** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) ![ESP32-S3](https://img.shields.io/badge/MCU-ESP32--S3-ff6a00) ![ESP-IDF](https://img.shields.io/badge/ESP--IDF-v5.5.2-00979D) ![LLM](https://img.shields.io/badge/LLM-Qwen%20via%20DashScope-0f766e) ![Channel](https://img.shields.io/badge/Channel-Feishu%20%7C%20WebSocket%20%7C%20QQBot-2563eb) ![Search](https://img.shields.io/badge/Search-Tavily-111827) @@ -18,7 +20,7 @@ This project draws on the ideas and direction of: -- [OpenClaw](https://github.com/OpenClawAI/OpenClaw) +- [OpenClaw](https://github.com/openclaw/openclaw) - [MimiClaw](https://github.com/memovai/mimiclaw) EmbedClaw keeps the goal of running a full AI Agent on low-power hardware but focuses the architecture on **decoupling LLM, Tools, Agent, and Channels**. @@ -69,10 +71,10 @@ It’s a working “embedded Agent base” you can extend. | Chat Channel | Feishu, WebSocket, QQBot | Feishu long connection, local WebSocket chat, official QQBot gateway | | Agent | ReAct tool loop | Model can call tools, read results, then continue | | Long-term memory | `/spiffs/memory/MEMORY.md` | User profile, preferences, stable facts | -| Short-term memory | `/spiffs/session/se_.jsonl` | Recent conversation for current session | +| Short-term memory | `/spiffs/session/se_.jsonl` | Conversation history including tool call traces | | Daily notes | `/spiffs/memory/.md` | Recent events and daily context | -| Skills | Built-in + SPIFFS | Task instructions as Markdown | -| Tools | Files, time, search, cron | Exposed to LLM via JSON schema | +| Skills | SPIFFS pre-installed + runtime | Task instructions as Markdown | +| Tools | Files, time, search, cron, GPIO | Exposed to LLM via JSON schema | ### Registered tools @@ -87,10 +89,11 @@ It’s a working “embedded Agent base” you can extend. | `cron_add` | Add periodic or one-shot scheduled tasks | | `cron_list` | List scheduled tasks | | `cron_remove` | Remove scheduled tasks | +| `gpio_control` | Control ESP32 GPIO pins (on, off, set, toggle, get) | -### Built-in skills +### Pre-installed skills -These are installed at startup: +These are pre-installed as Markdown files in `spiffs_data/skills/` and deployed with the SPIFFS image: - `weather` - `daily-briefing` @@ -115,6 +118,7 @@ flowchart LR T --> S2[File Tools] T --> S3[Time Tool] T --> S4[Cron Tool] + T --> S5[GPIO Tool] A --> M1[Session Memory] A --> M2[Long-term Memory] A --> K[Skill Loader] @@ -137,6 +141,9 @@ flowchart LR │ ├── embed_claw.c # System startup entry │ └── ec_config_internal.h # Built-in defaults; local overrides live in main/ec_config.h ├── spiffs_data/ # Default SPIFFS image content +│ ├── config/ # SOUL.md, USER.md +│ ├── memory/ # MEMORY.md +│ └── skills/ # Pre-installed skill files └── scripts/ # WebSocket test script and test-app helpers ``` @@ -540,7 +547,6 @@ Skills are Markdown task descriptions, not code. You can: - Write them at runtime via tools to `/spiffs/skills/.md` - Or put default skills in `spiffs_data/skills/` so they’re in the SPIFFS image -- Or add built-in skills in `ec_skill_loader.c` Suggested format: diff --git a/README_ZH.md b/README_ZH.md index 73f6947..8f57af2 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -4,6 +4,8 @@
+![logo](assets/logo.png) + **把 LLM、Tools、Agent、Channel 彻底拆开,再把它们装进一块 ESP32-S3。** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) ![ESP32-S3](https://img.shields.io/badge/MCU-ESP32--S3-ff6a00) ![ESP-IDF](https://img.shields.io/badge/ESP--IDF-v5.5.2-00979D) ![LLM](https://img.shields.io/badge/LLM-Qwen%20via%20DashScope-0f766e) ![Channel](https://img.shields.io/badge/Channel-Feishu%20%7C%20WebSocket%20%7C%20QQBot-2563eb) ![Search](https://img.shields.io/badge/Search-Tavily-111827) @@ -17,7 +19,7 @@ 本仓库在理念和方向上参考了以下优秀项目: -- [OpenClaw](https://github.com/OpenClawAI/OpenClaw) +- [OpenClaw](https://github.com/openclaw/openclaw) - [MimiClaw](https://github.com/memovai/mimiclaw) EmbedClaw 延续了“在低功耗硬件上运行完整 AI Agent”的思路,但把架构重点放在了 **LLM、Tools、Agent、Channel 的解耦** 上。 @@ -68,9 +70,9 @@ EmbedClaw 延续了“在低功耗硬件上运行完整 AI Agent”的思路, | Chat Channel | Feishu、WebSocket、QQBot | 飞书长连接、本地 WebSocket 对话、官方 QQBot gateway | | Agent | ReAct Tool Loop | 支持模型调用工具、再读工具结果、再继续推理 | | 长期记忆 | `/spiffs/memory/MEMORY.md` | 用户画像、长期偏好、稳定事实 | -| 短期记忆 | `/spiffs/session/se_.jsonl` | 最近对话历史,供当前会话上下文使用 | +| 短期记忆 | `/spiffs/session/se_.jsonl` | 对话历史(含完整工具调用链),供当前会话上下文使用 | | 每日笔记 | `/spiffs/memory/.md` | 记录近期事件与每日上下文 | -| Skills | 内置 + SPIFFS 动态技能 | 任务指令可持久化为 Markdown | +| Skills | SPIFFS 预置 + 运行时动态添加 | 任务指令可持久化为 Markdown | | Tools | 文件、时间、搜索、定时任务 | 通过统一 JSON Schema 暴露给 LLM | ### 已注册 Tools @@ -86,10 +88,11 @@ EmbedClaw 延续了“在低功耗硬件上运行完整 AI Agent”的思路, | `cron_add` | 创建周期任务或单次任务 | | `cron_list` | 查看当前定时任务 | | `cron_remove` | 删除定时任务 | +| `gpio_control` | 控制 ESP32 GPIO 引脚(on/off/set/toggle/get) | -### 已启用 Skills +### 预置 Skills -系统启动时会自动安装内置 Skills: +以下技能以 Markdown 文件形式预置在 `spiffs_data/skills/` 中,随 SPIFFS 镜像一同烧录: - `weather` - `daily-briefing` @@ -114,6 +117,7 @@ flowchart LR T --> S2[File Tools] T --> S3[Time Tool] T --> S4[Cron Tool] + T --> S5[GPIO Tool] A --> M1[Session Memory] A --> M2[Long-term Memory] A --> K[Skill Loader] @@ -136,6 +140,9 @@ flowchart LR │ ├── embed_claw.c # 系统统一启动入口 │ └── ec_config_internal.h # 仓库内置默认配置,项目覆盖放在 main/ec_config.h ├── spiffs_data/ # 默认写入 SPIFFS 的系统文件 +│ ├── config/ # SOUL.md、USER.md +│ ├── memory/ # MEMORY.md +│ └── skills/ # 预置 Skill 文件 └── scripts/ # WebSocket 测试脚本与测试构建辅助脚本 ``` @@ -569,7 +576,6 @@ Skill 是最轻量的扩展方式。它不是代码,而是任务说明书。 - 在运行时通过 Tool 把 Skill 写入 `/spiffs/skills/.md` - 或者把默认 Skill 放进 `spiffs_data/skills/`,随 SPIFFS 镜像一同烧录 -- 或者在 `ec_skill_loader.c` 中增加新的内置 Skill 建议格式: diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..74c1af01f6a4782ab1ae9b2eb25eb219c3747722 GIT binary patch literal 77740 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ξ)0qg!y92#c-mcaA5ghLdM_S;!;;`isF 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 q*#El!hTy*__-`Wof7Svr=y$+4