很多人第一次看到 EmbedClaw 會有一種錯覺:

然而事實是:
不能直接操作,但可以通過 Tool 去操作。
這也是 EmbedClaw 很有意思的一點。它不是把大模型硬塞進 ESP32 里當(dāng)聊天機器人,而是把 LLM、Agent、Tools、Channel 拆成了清晰的幾層。
模型負責(zé)“理解意圖和決策”,真正執(zhí)行硬件動作的是你自己寫的 Tool。
今天這篇文章,我們就不講空話,直接拿項目里已經(jīng)跑通的 gpio_control 作為例子,帶你了解:

01
先想明白:大模型為什么不能直接控制 GPIO
因為大模型本質(zhì)上只是一個“對于用戶輸入的內(nèi)容,會推理、會生成文本”的程序。
它擅長的是:
理解用戶說了什么
判斷該做什么
決定該調(diào)用哪個能力
根據(jù)結(jié)果繼續(xù)推理或回復(fù)
所以說,大模型并無法操作硬件。而需要把真實能力封裝成 Tool,把“能做什么、需要什么參數(shù)、執(zhí)行后返回什么結(jié)果”都定義清楚,再把這個 Tool 暴露給大模型。
這樣一來:
模型負責(zé)“調(diào)用”
代碼負責(zé)“執(zhí)行”
你負責(zé)“定義邊界”

這就是 EmbedClaw 的 Tool 機制。
02
先別急著寫代碼:Skill 和 Tool 不是一回事
很多人第一次接觸 EmbedClaw,最容易混淆的不是代碼細節(jié),而是Skill 和 Tool。 看起來Skill 和 Tool都像是在“教大模型做事”,但它們根本不是一層?xùn)|西。
如果只用一句話概括兩者關(guān)系,我更推薦下面這個版本:

1
Skill:負責(zé)教模型“怎么理解任務(wù)”
Skill 通常是一段 Markdown。
它的核心作用不是執(zhí)行,而是指導(dǎo)。
它解決的是這類問題:

比如一個天氣 Skill,可能會告訴模型:
用戶問天氣時可以使用 web_search
最好先獲取當(dāng)前日期
最后把結(jié)果整理成簡潔的自然語言
所以Skill 本質(zhì)上是策略層。
它告訴模型:“這類問題通常該怎么處理?!?/p>
但Skill 本身并不能真正執(zhí)行任何動作。
它不會直接去讀 GPIO,不會直接操作文件,也不會直接觸發(fā)硬件。
2
Tool:負責(zé)把事情真正執(zhí)行
Tool 則完全不同,Tool 是一段真正可執(zhí)行的能力接口。
它不是在“建議模型做什么”,而是在告訴模型:
“如果你要做這件事,可以調(diào)用我,我會真的去執(zhí)行。”
比如 gpio_control 這個 Tool:
模型可以決定要不要調(diào)用它
模型可以按 schema 組織參數(shù)
但真正去執(zhí)行 GPIO 配置、設(shè)置電平、返回結(jié)果的,是 execute 對應(yīng)的 C 代碼
所以 Tool 本質(zhì)上是執(zhí)行層。
3
那 Skill 還有什么意義?
很多人到這里疑惑:
“既然真正執(zhí)行的是 Tool,那 Skill 還有必要存在嗎?”
有,而且非常有必要。
原因就在于:
Skill 可以由大模型自己生成。
這也是 Skill 最有價值的地方之一。
因為 Tool 通常需要你自己寫 C 代碼、定義 schema、注冊進系統(tǒng),屬于工程能力擴展;
但 Skill 只是 Markdown,它天然適合承載那些“可以被總結(jié)、可以被抽象、可以被持續(xù)補充”的經(jīng)驗。

03
在 EmbedClaw 里,一個 Tool 是怎么跑起來的?
如果你把流程拆開看,其實非常清晰:

在當(dāng)前項目里,這條鏈路主要落在 4 個地方:
components/embed_claw/tools/tools_gpio.c
這里定義具體 Tool,需要用戶根據(jù)具體的tool在tools文件夾下執(zhí)行。
components/embed_claw/tools/ec_tools_reg.inc
這里把 Tool 注冊進系統(tǒng)。
components/embed_claw/core/ec_tools.c / ec_tools.h
這里維護 Tool 注冊表,并把 Tool 轉(zhuǎn)成模型能理解的 JSON 描述。新增 tool 不需要改動它。
components/embed_claw/llm/ec_llm_openai.c
這里把 Tool 描述轉(zhuǎn)成 OpenAI-Compatible 的 tools 字段,發(fā)送給大模型。新增 tool 不需要改動它。
也就是說,一個 Tool 之所以能被模型調(diào)用,不是因為模型“認識了你的 C 函數(shù)”,而是因為 EmbedClaw 先把它翻譯成了一份結(jié)構(gòu)化能力說明。
新增一個tool僅僅需要改動 tools_xxx.c 和在 ec_tools_reg.inc 中添加 tool 的注冊。


04
先看結(jié)果:gpio_control 這個 Tool 到底長什么樣?
首先,我們要在embed_claw/tools下創(chuàng)建自己要添加的tool的.c源文件
例如,在 tools_gpio.c 里,核心定義其實非常直接:
staticconst 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,};
這里面最關(guān)鍵的其實就 4 個字段:
name
description
input_schema_json
execute
接下來我們逐個解析。
05
第一步:給 Tool 起一個模型能理解的名字
.name="gpio_control",
這個名字不是寫給 C 編譯器看的,而是寫給大模型看的。
也就是說,后面模型發(fā)起調(diào)用時,大模型發(fā)過來的是:
{ "name":"gpio_control", "arguments":{ "pin":21, "action":"on" }}
所以命名建議很簡單:
用英文
語義清晰
一個名字只表達一種能力
比如:
gpio_control
relay_switch
sensor_read
buzzer_play

不要搞成以上這種模糊名字。名字越清楚,模型越容易選對!
06
第二步:寫好 description,告訴模型“什么時候該用我”
.description="Control an ESP32 output GPIO pin by pin number. Supports on, off, set, toggle, and get.\nIMPORTANT!!!: 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.",
這一段非常重要。
因為對模型來說,description 基本就是你寫給它的“使用說明書”。
模型會據(jù)此判斷:
這個工具是干嘛的
什么時候該調(diào)用它
哪些場景不應(yīng)該跳過它
以 gpio_control 為例,這段描述里實際上做了兩件事:
先定義能力范圍
它能控制 ESP32 輸出引腳,支持 on / off / set / toggle / get
再強調(diào)調(diào)用約束
只要是 GPIO 操作請求,就必須通過這個 Tool 執(zhí)行,不能直接文本編造結(jié)果
這也是你在設(shè)計自定義 Tool 時最值得花心思的地方。
一個好描述,通常應(yīng)該回答這三個問題:
比如你以后要做一個繼電器 Tool,描述就可以寫成:
Controlarelay connectedtothe board.Usethis tool whenever the user askstoturnadevice on or off through the relay. Do not claim the relay state without calling this tool.
07
第三步:用 input_schema_json 限制模型怎么傳參
Tool 之所以可靠,不只是因為模型“知道要調(diào)用它”,還因為你限制了模型“只能按這個格式調(diào)用它”。
gpio_control 的參數(shù)定義是:
"{"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"]}"
翻譯成人話就是:
參數(shù)必須是一個對象
pin 必須是整數(shù)
action 只能是 on/off/set/toggle/get
level 只有在 set 時才需要,而且只能是 0/1
pin 和 action 是必填項
這一步的意義非常大:
你不是“希望模型這么傳”
而是“明確規(guī)定模型必須這么傳”
對于大模型來說,這其實就像你給了它一個函數(shù)簽名。
所以如果你以后要做自己的 Tool,Schema 一定盡量寫嚴(yán)格。
舉幾個例子:
如果參數(shù)只能是整數(shù),就別寫成 string
如果只有幾個合法動作,就用 enum
如果某個字段必須要有,就放進 required
如果某個值范圍有限,就在 schema 里限制死
08
第四步:在 execute 里寫真正執(zhí)行邏輯
前面三步本質(zhì)上都還是“告訴模型怎么調(diào)用”,真正讓硬件動起來的,是 execute。
在 gpio_control 里,對應(yīng)的是:
.execute=ec_tool_gpio_control_execute,
然后具體執(zhí)行函數(shù)長這樣:
staticesp_err_tec_tool_gpio_control_execute(constchar *input_json,char*output,size_toutput_size)
這個函數(shù)做的事情可以概括成 4 步:
解析輸入 JSON
校驗參數(shù)是否合法
執(zhí)行對應(yīng)動作
把結(jié)果寫回 output
以 gpio_control 為例,它內(nèi)部先解析:
pin
action
level
然后根據(jù) action 進入不同分支:
get
on
off
set
toggle
比如 on 分支,大致就是:
err = prepare_pin_for_output(gpio_num);err = write_level(gpio_num, 1);snprintf(output, output_size,"OK: gpio %d action=on level=1", pin);
Tool 返回給 LLM 的,不是 C 語言里的返回值,而是 output 字符串。
也就是說,模型真正能看到的是類似:
OK: gpio21action=onlevel=1
因此 Tool 的輸出建議做到:
穩(wěn)定
簡潔
可讀
盡量結(jié)構(gòu)化
這樣后續(xù)模型再根據(jù)工具結(jié)果組織自然語言回復(fù)時,會更穩(wěn)。
09
第五步:把 Tool 注冊進系統(tǒng)
只寫了 tools_gpio.c 還不夠,系統(tǒng)還得知道有這么個 Tool。
esp_err_tec_tools_gpio_control(void){ ec_tools_register(&s_gpio_control); returnESP_OK;}
這里通過定義ec_tools_gpio_control()進行 tool 的注冊,而這里的函數(shù)不需要你調(diào)用。
EmbedClaw 這調(diào)用注冊函數(shù)是通過 ec_tools_reg.inc 來做的。
你會看到現(xiàn)在里面有一行:
EC_TOOLS_REG(gpio_control)
這一行別看簡單,它實際完成了三件事:
生成枚舉項,目標(biāo)是為了統(tǒng)計總共注冊了多少工具,對應(yīng)的注冊數(shù)組就會多大。
生成注冊函數(shù)聲明
在 ec_tools_register_all() 里自動調(diào)用 ec_tools_gpio_control()
這個機制背后靠的是 ec_tools_reg_rule.h 里的宏展開。
完成這一步之后,它就會自動進到 Tool 注冊表里。
10
EmbedClaw 是怎么把 Tool 暴露給大模型的?
這一塊很多人第一次看 Agent 框架都會忽略,但其實它才是“模型能調(diào)用 Tool”的關(guān)鍵。
在 ec_tools.c 里,EmbedClaw 會把所有已注冊 Tool 組織成一個 JSON 數(shù)組:
cJSON_AddStringToObject(tool,"name", s_tools[i]->name);cJSON_AddStringToObject(tool,"description", s_tools[i]->description);cJSON *schema =cJSON_Parse(s_tools[i]->input_schema_json);cJSON_AddItemToObject(tool,"input_schema", schema);
也就是說,系統(tǒng)內(nèi)部會生成一份類似這樣的描述:
{"name":"gpio_control","description":"Control an ESP32 output GPIO pin by pin number...","input_schema":{"type":"object","properties":{"pin":{"type":"integer"},"action":{"type":"string","enum":["on","off","set","toggle","get"]}},"required":["pin","action"]}}
隨后在 ec_llm_openai.c 里,這份內(nèi)部描述又會被轉(zhuǎn)成 OpenAI-Compatible 的 tools 字段發(fā)給模型。
也就是說,對模型來說,它看到的是:
這個 Tool 叫什么
它是干什么的
它需要什么參數(shù)
參數(shù)格式是什么
所以你可以把整個過程理解成:
你寫的是 C 代碼,但 EmbedClaw 會自動把它翻譯成“大模型能理解的函數(shù)說明書”。
11
總結(jié)
到這里,其實你已經(jīng)可以總結(jié)出一個通用方法了。
以后你不管是做:
繼電器控制
舵機控制
溫濕度傳感器讀取
蜂鳴器播放
本地業(yè)務(wù)接口觸發(fā)
基本都可以照這個模板走。
1
新建 tools_xxx.c
文件位置:
components/embed_claw/tools/tools_xxx.c
2
定義一個 ec_tools_t
最小骨架:
staticesp_err_tec_tool_demo_execute(constchar*input_json, char*output, size_t output_size);staticconstec_tools_t s_demo={ .name="demo_tool", .description="Describe what this tool does.", .input_schema_json="{"type":"object","properties":{},"required":[]}", .execute=ec_tool_demo_execute,};esp_err_tec_tools_demo(void){ ec_tools_register(&s_demo);returnESP_OK;}
3
第三步:寫執(zhí)行邏輯
你的執(zhí)行函數(shù)里重點做三件事:
解析參數(shù)
校驗參數(shù)
寫入執(zhí)行結(jié)果
4
注冊
在 components/embed_claw/tools/ec_tools_reg.inc 里補一行:
EC_TOOLS_REG(demo)
5
測試
你最好至少驗證這些場景:
合法參數(shù)能正常執(zhí)行
缺少必填參數(shù)時返回明確錯誤
非法參數(shù)不會誤操作硬件
Tool 輸出格式穩(wěn)定
這樣你這個 Tool 才算真的能給模型用。
這樣,你就能自己DIY定義各種各樣的功能并通過大模型進行調(diào)用了。
-
硬件
+關(guān)注
關(guān)注
12文章
3611瀏覽量
69127 -
AI
+關(guān)注
關(guān)注
91文章
40660瀏覽量
302313 -
大模型
+關(guān)注
關(guān)注
2文章
3720瀏覽量
5252
發(fā)布評論請先 登錄
一句話讓你理解線程和進程
準(zhǔn)備入門,誰來用一句話告訴我它和51的區(qū)別以及其應(yīng)用,就是一句話
Linux一句話精彩問答 pdf
一句話點評2012年20大技術(shù)前瞻
手把手教你學(xué)LabVIEW視覺設(shè)計
一句話解決嵌入式開發(fā)ping問題
一句話讓大模型控制硬件:手把手教你給 EmbedClaw 添加自己的 Tool!
評論