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

ไม่รวมอยู่ในชุดเอกสารของ mtb-only

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

สร้าง context ของคำขอจากเซสชันตามระเบียบวินัยที่ผู้เรียกเป็นเจ้าของบัฟเฟอร์ เดินชุด 3 ขั้นของ HTTPS connect → post → disconnect พร้อมการตรวจที่ถูกต้อง และ bring up backend ของ bento_link ตามลำดับที่กำหนดไว้ ตรวจสอบผลด้วยการเดินทางไป-กลับหนึ่งรอบของ ask()

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

การเพิ่มข้อความในเซสชัน เนื้อหาปิดท้ายด้วย NUL บทบาทที่พบในโค้ดที่ส่งมอบจริงคือ USER, ASSISTANT, SYSTEM และ TOOL ส่วนรอบของ assistant จะต่อท้ายเข้าไปหลัง claw_cb_success() เท่านั้น:

/* ...context: inside bentoclaw_ask() ... */
/* Add user message to session */
/* ...context: inside bentoclaw_ask(), success path ... */
s_agent_state = CLAW_STATE_IDLE;
snprintf(s_last_result, sizeof(s_last_result), "ok (%d bytes)", resp_len);
claw_ipc_send_ui(CLAW_UI_LAST_RESULT, s_last_result, strlen(s_last_result));
claw_ipc_send_ui(CLAW_UI_STATUS_TEXT, "Online (HTTPS)", 14);
/* Add assistant response to session (s_work_small holds response) */
return mp_obj_new_str(s_work_small, (size_t)resp_len);

การสร้าง context ผู้เรียกเป็นผู้จัดหาบัฟเฟอร์เอง (s_work_large[3072] / s_work_small[512], modbentoclaw.c:237-238) โดยเผื่อพื้นที่ไว้ 256 ไบต์อย่างจงใจ ผลลัพธ์ ไม่ผ่านการ escape แบบ JSON และต้องผ่าน json_escape ก่อนเข้าสู่ body ของคำขอ บัฟเฟอร์ทั้งสองห้ามซ้อนทับกัน:

/* ...context: inside bentoclaw_ask() ... */
/* Build context → escape both → build JSON → POST.
* Escape prompt+context into s_work_small halves, then build JSON
* in s_work_large (no overlap). */
/* 1) Build context string in s_work_large */
size_t ctx_len = claw_session_build_context(s_work_large, sizeof(s_work_large) - 256);
/* 2) Escape prompt into s_work_small first half */
char *esc_p = s_work_small;
size_t ep_max = sizeof(s_work_small) / 2;
size_t ep_len = json_escape(esc_p, ep_max, prompt,
prompt_len > 200 ? 200 : prompt_len);
/* 3) Escape context into s_work_small second half */
char *esc_c = s_work_small + ep_max;
size_t ec_max = sizeof(s_work_small) / 2;
size_t ec_len = json_escape(esc_c, ec_max,
s_work_large, ctx_len > 200 ? 200 : ctx_len);
/* ...context: inside bentoclaw_ask() ... */
/* 4) Build JSON in s_work_large (no overlap with s_work_small) */
int json_len = snprintf(s_work_large, sizeof(s_work_large),
"{\"prompt\":\"%.*s\",\"context\":\"%.*s\"}",
(int)ep_len, esc_p, (int)ec_len, esc_c);

ชุดสามขั้นของ transport ตรวจการเชื่อมต่อ ก่อน circuit breaker ที่จุดเรียก ask ทุกจุด:

static mp_obj_t bentoclaw_ask(mp_obj_t prompt_obj) {
bentoclaw_ensure_init();
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("not connected — call bentoclaw.connect() first"));
}
if (!claw_cb_allow()) {
mp_raise_msg(&mp_type_RuntimeError,
MP_ERROR_TEXT("circuit breaker open"));
}

claw_https_post() คืนจำนวนไบต์ที่เขียนลง resp หรือ ค่าติดลบเมื่อล้มเหลว บัฟเฟอร์คำตอบห้ามชี้ทับบัฟเฟอร์คำขอ:

/* ...context: inside bentoclaw_ask() ... */
/* 4) Build JSON in s_work_large (no overlap with s_work_small) */
int json_len = snprintf(s_work_large, sizeof(s_work_large),
"{\"prompt\":\"%.*s\",\"context\":\"%.*s\"}",
(int)ep_len, esc_p, (int)ec_len, esc_c);
/* 5) POST to backend — response overwrites s_work_small */
int resp_len = claw_https_post("/ai/v1/chat", s_work_large, (size_t)json_len,
s_work_small, sizeof(s_work_small));
/* ...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"));
}

การ connect และ disconnect พร้อมการจับคู่ trust อยู่ใน H2 — safety gate ของ claw

สถานะเซสชันและการล้าง claw_session_count() ป้อนค่าให้ dict สถานะ ส่วน claw_session_clear() จับคู่กับการล้างฝั่ง UI:

/* ...context: inside bentoclaw_status() ... */
mp_obj_dict_store(MP_OBJ_FROM_PTR(d),
MP_OBJ_NEW_QSTR(MP_QSTR_session_msgs),
mp_obj_new_int(claw_session_count()));
static mp_obj_t bentoclaw_session_clear(void) {
bentoclaw_ensure_init();
if (claw_ipc_initialized) {
claw_ipc_send_ui(CLAW_UI_CLEAR, "", 0);
}
return mp_const_none;
}

backend ลำดับที่กำหนดไว้คือ bento_link_ipc_init()bento_link_get("ipc") → ตรวจ NULL → ต่อเข้ากับ adapter:

static void ensure_inited(void)
{
if (s_inited) {
return;
}
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("model link init failed"));
}
s_adapter.base.send = adapter_send;
s_adapter.base.register_receive_cb = adapter_register_receive;
s_adapter.link = bento_link_get("ipc");
if (s_adapter.link == NULL) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ipc link unavailable"));
}
s_iface.IPC = &s_adapter.base;
model_type_t model = { .va_model = DEEPCRAFT_VA_MODEL };
deepcraft_engine_init(&s_engine, model, &s_iface);
deepcraft_engine_set_event_cb(&s_engine, engine_event_cb);

วนอ่าน backend ที่ลงทะเบียนไว้จนถึง BENTO_LINK_MAX_BACKENDS และหยุดเมื่อพบ NULL:

static mp_obj_t deepcraft_links(void)
{
ensure_inited();
mp_obj_t items[BENTO_LINK_MAX_BACKENDS];
uint32_t n = 0;
for (uint32_t i = 0; i < BENTO_LINK_MAX_BACKENDS; i++) {
if (l == NULL) {
break;
}
items[n++] = mp_obj_new_str(l->name, strlen(l->name));
}
return mp_obj_new_tuple(n, items);
}

ทีละขั้น

ขั้นที่ 1 — เดินทางไป-กลับหนึ่งรอบ

>>> import bentoclaw
>>> bentoclaw.ask("What is on the display?")

สิ่งที่ควรสังเกต สตริงคำตอบ และ bentoclaw.status()['session_msgs'] เพิ่มขึ้นสอง (user + assistant) สตริงผลลัพธ์ล่าสุด ok (d bytes) ถูกดันไปยังหน้า BentoClaw บน CM55 (modbentoclaw.c:471-473) หากลิงก์ล่ม จะได้ RuntimeError จากตัวกันของ ask ก่อนที่ breaker จะบันทึกอะไรลงไป

ขั้นที่ 2 — ล้างแล้วยืนยัน

>>> bentoclaw.session_clear()
>>> bentoclaw.status()['session_msgs']
0

สิ่งที่ควรสังเกต จำนวนเป็น 0 และบทสนทนาบนหน้า CM55 ถูกล้างในการเรียกครั้งเดียวกัน

ขั้นที่ 3 — ตรวจดู backend

>>> import deepcraft

สิ่งที่ควรสังเกต การ import สำเร็จก็ต่อเมื่อ bento_link_get("ipc") คืนค่าที่ไม่ใช่ NULL ตอน init ของโมดูล snippet ด้านบนคือเส้นทางที่ทำงานจริง

ตัวอย่างที่เขียนขึ้นเอง (ไม่มี call site ในของที่ส่งมอบ)

bento_link_ipc_query — ไม่มีการเรียกในเทมเพลตเลย และไม่มีการประกาศส่งมาด้วย (bento_secure_undeclared.h:11) ผู้ใช้ไลบรารีจึงต้องเขียน extern เอง bento_link_register — เป็น API สำหรับผู้เขียน backend สร้างแบบตามการลงทะเบียนตัวเองที่เก็บไว้ใน archive ที่ bento_link_ipc.c:245 ส่วน claw_session_flush, claw_session_dirty, claw_memory_get — ไม่มีผู้เรียกที่ใดเลย ตัวอย่างของทั้งสามในคู่มืออ้างอิง mpy_secure มีป้ายกำกับว่าเขียนขึ้นเอง และไม่มีสิ่งใดในบทนี้ขึ้นกับทั้งสาม

กับดัก

Warning
rlen > 0 เป็นการตรวจที่ผิด สำหรับ claw_https_post() (จุดที่ :2213 ต่างจากการตรวจค่าติดลบที่ถูกต้องที่ :459) ความสำเร็จที่มีความยาวศูนย์เป็นสิ่งที่ถูกต้องตามข้อกำหนด
context ที่ไม่ผ่านการ escape ใน body แบบ JSON ทำให้คำขอพังทันทีที่ผู้ใช้พิมพ์เครื่องหมายคำพูดเป็นครั้งแรก
การเรียก bento_link_get ก่อน bento_link_ipc_init คืนค่า NULL — ตัว init เป็นผู้ลงทะเบียน backend ให้เอง

กล่อง variant

mtb-mpy เท่านั้น