SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches
H2 — safety gate ของ claw
variant ที่ใช้ได้
mtb-mpy เท่านั้น (ไม่มีการลิงก์ libbento_mpy.a เมื่อ BENTO_HAS_MPY=0)

ไม่รวมอยู่ในชุดเอกสารของ mtb-only (ที่นั่นไม่ได้ลิงก์ libbento_mpy.a)

เป้าหมายของหัวข้อนี้

ท่องลำดับของ gate (ด่านกั้นเชิงความปลอดภัย) ให้ได้ — circuit breaker → trust → rate check → rate record — และรู้ว่าเหตุใดลำดับนี้จึงตายตัว จับคู่ claw_cb_success() กับ claw_cb_failure() ให้ถูกต้องรอบการ raise ที่ไม่คืนค่ากลับ ยกระดับ trust ก่อนการ connect และคืนค่าเดิมในทุกเส้นทางที่ล้มเหลว ตรวจสอบแต่ละ gate จาก REPL ด้วยสตริง RuntimeError ที่มันโยนออกมา

ลำดับการทำงานจริงของเฟิร์มแวร์

การ init เป็น idempotent และมีทางเข้าเดียว:

static void bentoclaw_ensure_init(void) {
if (s_module_inited) return;
/* Set per-tool rate limits from registry */
for (size_t i = 0; i < TOOL_COUNT; i++) {
if (s_tools[i].rate_per_min > 0) {
claw_rate_set(s_tools[i].name, s_tools[i].rate_per_min);
}
}
s_module_inited = true;
}

ลำดับของ gate — snippet ที่รับน้ำหนักมากที่สุดในโมดูลนี้:

/* ...context: inside the tool-call handler; 'found' is the resolved tool entry ... */
/* Safety check: circuit breaker */
if (!claw_cb_allow()) {
s_error_count++;
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("circuit breaker open — cooldown active"));
}
/* Safety check: transport trust level */
if (!claw_trust_allows(found->risk)) {
s_error_count++;
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("tool risk exceeds transport trust level"));
}
/* Safety check: rate limiter */
if (!claw_rate_check(found->name)) {
s_error_count++;
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("rate limit exceeded for this tool"));
}
/* Record rate limit hit */
claw_rate_record(found->name);

การตรวจ ไม่กินโควตา — claw_rate_check() และ claw_rate_record() เป็นการเรียกคนละครั้งบน found->name เดียวกัน และจะไปถึง record ก็ต่อเมื่อผ่าน gate ครบทั้งสามแล้วเท่านั้น s_error_count++ มาก่อนการ raise ทุกครั้ง

การจับคู่สำเร็จ/ล้มเหลว ต้องเรียก claw_cb_failure() ก่อน mp_raise_msg ที่ไม่คืนค่ากลับทั้งสองฟังก์ชันเป็น void:

/* ...context: inside bentoclaw_ask(), after claw_https_post() ... */
if (resp_len < 0) {
s_agent_state = CLAW_STATE_ERROR;
s_error_count++;
claw_ipc_send_ui(CLAW_UI_ERROR, "Backend error", 13);
snprintf(s_last_result, sizeof(s_last_result), "error");
claw_ipc_send_ui(CLAW_UI_LAST_RESULT, s_last_result, strlen(s_last_result));
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("HTTPS request failed"));
}
/* ...context: inside bentoclaw_ask(), success path ... */
s_agent_state = CLAW_STATE_IDLE;

การยกระดับ trust และการคืนค่าเดิม trust ยกขึ้นเป็น CLAW_TRUST_HTTPS ก่อนการพยายาม connect และรีเซ็ตกลับเป็น CLAW_TRUST_USB ภายใน else เพื่อไม่ให้การ connect ที่ล้มเหลวทิ้งเครื่องมือความเสี่ยงสูงไว้ในสถานะเปิด:

/* ...context: inside bentoclaw_connect() ... */
claw_ipc_init();
claw_ipc_send_ui(CLAW_UI_STATUS_TEXT, "Connecting...", 13);
bool ok = claw_https_connect(host, port, api_key);
if (ok) {
claw_ipc_send_ui(CLAW_UI_STATUS_TEXT, "Online (HTTPS)", 14);
claw_session_add(CLAW_ROLE_SYSTEM, "Connected to TESAIoT backend");
} else {
claw_ipc_send_ui(CLAW_UI_STATUS_TEXT, "Connect failed", 14);
}
static mp_obj_t bentoclaw_disconnect(void) {
if (claw_ipc_initialized) {
claw_ipc_send_ui(CLAW_UI_STATUS_TEXT, "Offline", 7);
}
claw_session_add(CLAW_ROLE_SYSTEM, "Disconnected from backend");
return mp_const_none;
}

ส่วนที่เรียกดูสถานะclaw_cb_state(), claw_trust_get(), claw_cb_cooldown_remaining() รวมอยู่ในฟังก์ชันเดียวที่จบในตัว:

static mp_obj_t bentoclaw_safety(void) {
bentoclaw_ensure_init();
mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(4));
static const char *cb_names[] = { "closed", "open", "half-open" };
const char *cb_str = (cb < 3) ? cb_names[cb] : "unknown";
static const char *trust_names[] = { "none", "https", "usb" };
const char *trust_str = (trust < 3) ? trust_names[trust] : "unknown";
mp_obj_dict_store(MP_OBJ_FROM_PTR(d),
MP_OBJ_NEW_QSTR(MP_QSTR_circuit_breaker),
mp_obj_new_str(cb_str, strlen(cb_str)));
mp_obj_dict_store(MP_OBJ_FROM_PTR(d),
MP_OBJ_NEW_QSTR(MP_QSTR_cooldown_ms),
mp_obj_new_int(claw_cb_cooldown_remaining()));

claw_memory_set() เป็น binding แบบ bool ธรรมดา:

static mp_obj_t bentoclaw_remember(mp_obj_t key_obj, mp_obj_t val_obj) {
bentoclaw_ensure_init();
const char *key = mp_obj_str_get_str(key_obj);
const char *val = mp_obj_str_get_str(val_obj);
bool ok = claw_memory_set(key, val);
return mp_obj_new_bool(ok);
}

ทีละขั้น

ขั้นที่ 1 — อ่านสถานะความปลอดภัยตั้งต้น

>>> import bentoclaw
>>> bentoclaw.safety()

สิ่งที่ควรสังเกต dict ที่มี cb = "closed", trust = "usb" (หรือ "none") และ cooldown_ms = 0 ตารางชื่อมีตัวกันขอบเขต (คืน "unknown" สำหรับค่าที่อยู่นอกช่วง)

ขั้นที่ 2 — ทำให้ trust gate ทำงาน

เรียกเครื่องมือที่ประกาศระดับความเสี่ยงไว้สูงกว่าระดับ trust ปัจจุบัน ในขณะที่ยังไม่ได้เชื่อมต่อ

สิ่งที่ควรสังเกต RuntimeError: tool risk exceeds transport trust level และ safety()['trust'] ไม่เปลี่ยน — trust gate ปฏิเสธเท่านั้น ไม่ไปแก้ค่า

ขั้นที่ 3 — ทำให้ rate gate ทำงาน

เรียกเครื่องมือที่จำกัดอัตราซ้ำ ๆ ภายในหนึ่งนาที

สิ่งที่ควรสังเกต RuntimeError: rate limit exceeded ในการเรียกครั้งแรกที่เกิน rate_per_min ของเครื่องมือนั้น ค่าจำกัดมาจาก registry ตอน init (ลูป claw_rate_set เฉพาะเมื่อ rate_per_min > 0)

ขั้นที่ 4 — เปิด breaker แล้วเฝ้าดู cooldown

ทำให้เกิดความล้มเหลวติดต่อกัน (ปลายทาง HTTPS ที่ตัดการเชื่อมต่อไว้ก็เพียงพอ)

สิ่งที่ควรสังเกต RuntimeError: circuit breaker open — cooldown active และ safety()['cb'] = "open" โดย cooldown_ms นับถอยหลัง ความล้มเหลวแต่ละครั้งผ่าน claw_cb_failure() ก่อนการ raise ของมัน ส่วนความสำเร็จครั้งถัดมาผ่าน claw_cb_success() และปิด breaker ผ่านสถานะ "half-open"

กับดัก

Warning
ลำดับนี้ต่อรองไม่ได้ breaker ถูกตรวจก่อน trust เพื่อไม่ให้ transport ที่มี trust สูงเลี่ยง breaker ที่เปิดอยู่ได้ และ rate บันทึกเป็นลำดับสุดท้ายเพื่อไม่ให้การเรียกที่ถูกปฏิเสธกินโควตา
การ raise โดยไม่เรียก claw_cb_failure() ก่อน ทำให้ breaker มองไม่เห็นความล้มเหลวนั้น — mp_raise_msg ไม่คืนค่ากลับ
CLAW_TRUST_HTTPS ทุกจุดต้องมี CLAW_TRUST_USB เป็นคู่ของมัน บนเส้นทางที่ล้มเหลวและเส้นทางตัดการเชื่อมต่อ (modbentoclaw.c:487, :2369) claw_https_disconnect() ทำงานโดยไม่มีเงื่อนไขและต้องตามด้วยการรีเซ็ต trust
claw_memory_get, claw_session_dirty, claw_session_flush ส่งออกมาโดยไม่มีผู้เรียกเลยตัวอย่างของทั้งสามเป็นตัวอย่างที่เขียนขึ้นเองและระบุไว้เช่นนั้น

กล่อง variant

mtb-mpy เท่านั้น บน mtb-only ไม่มีส่วนที่เรียกใช้ได้ของ BentoClaw