Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/mimiclaw/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ set(APP_SRCS
${APP_PATH}/tools/tool_registry.c
${APP_PATH}/tools/tool_cron.c
${APP_PATH}/tools/tool_web_search.c
${APP_PATH}/tools/tool_web_search_baidu.c
${APP_PATH}/tools/tool_get_time.c
${APP_PATH}/tools/tool_files.c
${APP_PATH}/tools/tool_led.c
${APP_PATH}/skills/skill_loader.c
)

Expand Down
24 changes: 24 additions & 0 deletions apps/mimiclaw/README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,30 @@ cp apps/tuya_mimiclaw/mimi_secrets.h.example apps/tuya_mimiclaw/mimi_secrets.h
5. **也可以直接复制 `apps/tuy_mimiclaw/mimi_secrets.h.example` 并重命名为 `mimi_secrets.h` 修改文件上的配置,并重新编译**
(本仓库中目录名为 `apps/tuya_mimiclaw`,请以实际路径为准)

6. **存储切换**

在 `apps/tuya_mimiclaw/mimi_config.h` 中

```
#if MIMI_USE_SDCARD
#define MIMI_FS_BASE "/spiffs"
#else
#define MIMI_FS_BASE "/spiffs"
#endif
```c

7. **搜索引擎切换**

在 `apps/tuya_mimiclaw/mimi_config.h` 中

```
/* Search Engine Selection: 0 = Brave, 1 = Baidu */
#ifndef MIMI_USE_BAIDU_SEARCH
#define MIMI_USE_BAIDU_SEARCH 0
#endif
```c


### 6. 配置优先级规则

配置优先级(从高到低)如下:
Expand Down
43 changes: 37 additions & 6 deletions apps/mimiclaw/agent/agent_loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,11 @@ static char *patch_tool_input_with_context(const llm_tool_call_t *call, const mi
changed = true;
}

if (channel && strcmp(channel, MIMI_CHAN_TELEGRAM) == 0 && strcmp(msg->channel, MIMI_CHAN_TELEGRAM) == 0 &&
msg->chat_id[0] != '\0') {
/* Patch chat_id for Telegram and Feishu if AI used invalid 'cron' placeholder */
bool same_channel = channel && msg->channel[0] != '\0' && strcmp(channel, msg->channel) == 0;
bool needs_patch =
same_channel && (strcmp(channel, MIMI_CHAN_TELEGRAM) == 0 || strcmp(channel, MIMI_CHAN_FEISHU) == 0);
if (needs_patch && msg->chat_id[0] != '\0') {
cJSON *chat_item = cJSON_GetObjectItem(root, "chat_id");
const char *chat_id = cJSON_IsString(chat_item) ? chat_item->valuestring : NULL;
if (!chat_id || chat_id[0] == '\0' || strcmp(chat_id, "cron") == 0) {
Expand Down Expand Up @@ -160,13 +163,20 @@ static cJSON *build_tool_results(const llm_response_t *resp, const mimi_msg_t *m

tool_output[0] = '\0';

MIMI_LOGI(TAG, ">>> TOOL EXEC: name=%s id=%s input=%s", call->name, call->id,
tool_input ? tool_input : "(null)");
OPERATE_RET rt = tool_registry_execute(call->name, tool_input, tool_output, tool_output_size);
if (rt != OPRT_OK && tool_output[0] == '\0') {
snprintf(tool_output, tool_output_size, "Tool execute failed: %d", rt);
}
cJSON_free(patched_input);

MIMI_LOGI(TAG, "tool=%s result_bytes=%u", call->name, (unsigned)strlen(tool_output));
MIMI_LOGI(TAG, "<<< TOOL RESULT: name=%s rt=%d result_bytes=%u", call->name, rt, (unsigned)strlen(tool_output));
if (strlen(tool_output) <= 512) {
MIMI_LOGI(TAG, " tool output: %s", tool_output);
} else {
MIMI_LOGI(TAG, " tool output (first 512): %.512s ...[truncated]", tool_output);
}

cJSON *result_block = cJSON_CreateObject();
if (!result_block) {
Expand Down Expand Up @@ -212,7 +222,9 @@ static void agent_loop_task(void *arg)
continue;
}

MIMI_LOGI(TAG, "processing msg %s:%s", in_msg.channel, in_msg.chat_id);
MIMI_LOGI(TAG, "================ AGENT TURN START ================");
MIMI_LOGI(TAG, "processing msg channel=%s chat_id=%s", in_msg.channel, in_msg.chat_id);
MIMI_LOGI(TAG, "user input: %s", in_msg.content ? in_msg.content : "(null)");

if (context_build_system_prompt(system_prompt, MIMI_CONTEXT_BUF_SIZE) != OPRT_OK) {
free(in_msg.content);
Expand Down Expand Up @@ -261,22 +273,35 @@ static void agent_loop_task(void *arg)
}
#endif

MIMI_LOGI(TAG, "--- LLM call iteration=%d ---", iteration + 1);
bool force_tool = (iteration == 0); /* first iteration: force tool use */
llm_response_t resp;
OPERATE_RET rt = llm_chat_tools(system_prompt, messages, tools_json, &resp);
OPERATE_RET rt = llm_chat_tools_ex(system_prompt, messages, tools_json, force_tool, &resp);
if (rt != OPRT_OK) {
MIMI_LOGE(TAG, "llm_chat_tools failed: %d", rt);
MIMI_LOGE(TAG, "llm_chat_tools failed: rt=%d iteration=%d", rt, iteration + 1);
break;
}

MIMI_LOGI(TAG, "LLM response: tool_use=%s text_len=%u call_count=%d", resp.tool_use ? "true" : "false",
(unsigned)(resp.text ? strlen(resp.text) : 0), resp.call_count);

if (!resp.tool_use) {
if (resp.text && resp.text[0] != '\0') {
final_text = strdup(resp.text);
MIMI_LOGI(TAG, "final text (len=%u): %.512s%s", (unsigned)strlen(resp.text), resp.text,
strlen(resp.text) > 512 ? "...[truncated]" : "");
} else {
MIMI_LOGW(TAG, "LLM returned no text and no tool calls");
}
llm_response_free(&resp);
break;
}

MIMI_LOGI(TAG, "tool iteration=%d call_count=%d", iteration + 1, resp.call_count);
for (int tc_i = 0; tc_i < resp.call_count; tc_i++) {
MIMI_LOGI(TAG, " tool_call[%d]: name=%s id=%s input=%s", tc_i, resp.calls[tc_i].name,
resp.calls[tc_i].id, resp.calls[tc_i].input ? resp.calls[tc_i].input : "(null)");
}

cJSON *asst_msg = cJSON_CreateObject();
cJSON_AddStringToObject(asst_msg, "role", "assistant");
Expand All @@ -295,7 +320,12 @@ static void agent_loop_task(void *arg)

cJSON_Delete(messages);

MIMI_LOGI(TAG, "agent loop completed: iterations=%d final_text=%s", iteration,
(final_text && final_text[0] != '\0') ? "present" : "empty");

if (final_text && final_text[0] != '\0') {
MIMI_LOGI(TAG, "sending final response (len=%u): %.512s%s", (unsigned)strlen(final_text), final_text,
strlen(final_text) > 512 ? "...[truncated]" : "");
OPERATE_RET save_user_rt = session_append(in_msg.chat_id, "user", user_text);
OPERATE_RET save_asst_rt = session_append(in_msg.chat_id, "assistant", final_text);
if (save_user_rt != OPRT_OK || save_asst_rt != OPRT_OK) {
Expand Down Expand Up @@ -329,6 +359,7 @@ static void agent_loop_task(void *arg)
free(final_text);
free(in_msg.content);
MIMI_LOGI(TAG, "free heap=%d", tal_system_get_free_heap_size());
MIMI_LOGI(TAG, "================ AGENT TURN END ================");
}
}

Expand Down
22 changes: 15 additions & 7 deletions apps/mimiclaw/agent/context_builder.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,36 @@
#include "skills/skill_loader.h"

#include "cJSON.h"
#include "tal_fs.h"
// #include "tal_fs.h"

static const char *TAG = "context";
/* Reused scratch buffer for memory/skills blocks to keep stack usage low. */
#define CONTEXT_TMP_BUF_SIZE 4096

static size_t append_file(char *buf, size_t size, size_t offset, const char *path, const char *header)
{
TUYA_FILE f = tal_fopen(path, "r");
TUYA_FILE f = mimi_fopen(path, "r");
if (!f || !buf || size == 0 || offset >= size - 1) {
if (f) {
tal_fclose(f);
mimi_fclose(f);
}
return offset;
}

if (header) {
offset += snprintf(buf + offset, size - offset, "\n## %s\n\n", header);
if (offset >= size - 1) {
tal_fclose(f);
mimi_fclose(f);
return size - 1;
}
}

int n = tal_fread(buf + offset, (int)(size - offset - 1), f);
int n = mimi_fread(buf + offset, (int)(size - offset - 1), f);
if (n > 0) {
offset += (size_t)n;
}
buf[offset] = '\0';
tal_fclose(f);
mimi_fclose(f);
return offset;
}

Expand All @@ -55,7 +55,8 @@ OPERATE_RET context_build_system_prompt(char *buf, size_t size)
"- web_search: Search the web for current information. "
"Use this when you need up-to-date facts, news, weather, or anything beyond your training data.\n"
"- get_current_time: Get the current date and time. "
"You do NOT have an internal clock - always use this tool when you need to know the time or date.\n"
"You do NOT have an internal clock - always use this tool when you need to know the time or date. "
"NEVER guess or reuse time from previous messages.\n"
"- read_file: Read a file from SPIFFS (path must start with /spiffs/).\n"
"- write_file: Write/overwrite a file on SPIFFS.\n"
"- edit_file: Find-and-replace edit a file on SPIFFS.\n"
Expand All @@ -66,6 +67,13 @@ OPERATE_RET context_build_system_prompt(char *buf, size_t size)
"- cron_remove: Remove a scheduled cron job by ID.\n\n"
"When using cron_add for Telegram delivery, always set channel='telegram' and a valid numeric chat_id.\n\n"
"Use tools when needed. Provide your final answer as text after using tools.\n\n"
"## CRITICAL RULES\n"
"1. When the user asks about time, date, or anything time-dependent, you MUST call get_current_time. "
"The [Current time] in the user message is approximate - for precise answers, always use the tool.\n"
"2. When the user asks to set reminders or timed tasks, you MUST call cron_add. NEVER pretend a task was set "
"without actually calling the tool.\n"
"3. NEVER fabricate tool results. If you need information, call the appropriate tool.\n"
"4. When answering about weather, news, or real-time info, you MUST call web_search.\n\n"
"## Memory\n"
"You have persistent memory stored on local flash:\n"
"- Long-term memory: /spiffs/memory/MEMORY.md\n"
Expand Down
2 changes: 1 addition & 1 deletion apps/mimiclaw/app_default.config
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ CONFIG_ENABLE_CUSTOM_CONFIG=y
CONFIG_ENABLE_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y
CONFIG_ENABLE_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=y
CONFIG_ENABLE_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y
CONFIG_ENABLE_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y
CONFIG_ENABLE_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y
Loading