跳到主要内容

AI 工具调用

AI 的限制

AI 在嵌入式系统中有许多用途,但输出可能包含不准确、偏见或安全问题。

本指南假设您已经查看了我们的嵌入向量指南和 AI 文本助手指南。

工具调用允许 AI 模型的输出调用外部函数或 API。工具调用的典型用途是从基于云的 LLM 访问云 API;但在嵌入式系统中,我们也可以使用它与系统状态和外设进行交互。这有许多用途,包括嵌入式系统的语音用户界面 (VUI) 控制。

让我们扩展基于问答的 AI 助手,使其在看到特殊标记时调用外部工具。 这使您的 AI 能够:

  • 通过网络摄像头查看({vision}
  • 打开/关闭 LED({light_on}{light_off}
  • 运行 shell 命令({time}

信息

本快速指南适用于所有 SL16xx 开发板。虽然推理性能可能有所不同,但所有 Astra SL 系列处理器的步骤都相同。

硬件设置

  1. Synaptics Astra SL1680 开发板
  2. USB 网络摄像头(可选)
    • vision.py 访问,它使用 NPU 加速在帧上运行图像分类
    • 返回文本描述,例如:"我看到一只猫。"
  3. GPIO 引脚上的 LED(可选)
    • 连接到 GPIO[36](开发板上的物理引脚 33)高电平({light_on})或低电平({light_off}
    • 通过类似"开灯"或"关灯"的短语进行语音控制
用例图片

运行演示

  1. 运行助手
    python3 -m assistant.toolcall
  2. 询问"你能看到什么?"
    • 系统在 qa_pairs.json 中识别匹配项("我看到 {vision}
    • 它发现 {vision},运行 python3 path/to/vision.py,并用脚本输出替换 {vision}
  3. 询问"打开灯"
    • 匹配问答 "打开灯" -> "{light_on}"`
    • 您的代码执行 GPIO 命令将 LED 引脚设置为高电平。
  4. 询问"现在几点了?"
    • 返回 {date} => 调用 date => 输出系统时间。

工作原理

  1. 嵌入向量和相似度:我们将每个"问题+答案"对嵌入到向量空间中。当用户提问时,我们计算问题的嵌入向量,将其与存储的嵌入向量进行比较,并找到最高的余弦相似度。
  2. 响应构建:我们从 qa_pairs.json 中检索最佳匹配的"答案"。
  3. 标记替换:在打印最终答案之前,我们查找任何占位符,如 {light_on}。如果存在,我们从 tools.json 执行相应的命令,并用命令的输出替换标记。

数据文件

1. data/qa_pairs.json

我们不仅在 answer 字段中存储静态文本,还嵌入"工具标记"。例如:

[
{
"question": "现在几点了?",
"answer": "{time}"
},
{
"question": "你能看到什么?",
"answer": "我看到 {vision}"
},
{
"question": "打开灯",
"answer": "{light_on}"
},
{
"question": "关闭灯",
"answer": "{light_off}"
},
]

这里,"{vision}" 代表对网络摄像头图像运行 NPU 加速的图像分类,而 "{light_on}" 调用 GPIO 脚本来打开 LED。

2. data/tools.json

此文件将每个标记映射到我们希望系统运行的命令:

[
{
"token": "{time}",
"command": "date"
},
{
"token": "{vision}",
"command": "python3 assistant/tools/vision.py"
},
{
"token": "{light_on}",
"command": "echo 'light on' & python assistant/tools/gpio.py 484 out 1"
},
{
"token": "{light_off}",
"command": "echo 'light off' & python assistant/tools/gpio.py 484 out 0"
}
]

请随意自定义路径和参数以匹配您的开发板设置。

关键代码概念

以下是在选择的答案中出现标记时如何调用工具的基本代码片段:

import json
import subprocess

def run_command(command):
"""运行 shell 命令并返回其输出作为字符串。"""
try:
return subprocess.check_output(command, shell=True).decode().strip()
except Exception as e:
return f"[错误: {e}]"

def replace_tool_tokens(answer, tools):
"""用外部命令输出替换答案中的占位符。"""
for tool in tools:
if tool["token"] in answer:
# 运行相应的命令并注入结果
result = run_command(tool["command"])
answer = answer.replace(tool["token"], result)
return answer

在您的主助手循环中:

# 加载问答对并预计算嵌入向量等(未完全显示)
with open("qa_pairs.json") as f:
qa_pairs = json.load(f)

with open("tools.json") as f:
tools = json.load(f)

while True:
query = input("问我任何问题(或输入 'exit'):")
if query.lower() == "exit":
break

# 1. 通过余弦相似度找到最佳匹配答案(伪代码)
best_answer = find_best_match(query, qa_pairs)

# 2. 用命令输出替换任何工具标记
final_answer = replace_tool_tokens(best_answer, tools)

print(f"答案:{final_answer}\n")

结果是一个可以与物理环境交互(打开/关闭灯)并动态收集信息(运行视觉分析、打印当前时间)的助手,所有这些都通过在其文本答案中返回简短的标记来实现。

自定义工具和命令

要添加新功能,只需在 qa_pairs.json 中添加更多标记,并在 tools.json 中定义如何处理它们。例如,如果您有红外发射器或伺服电机,创建相应的标记(如 {raise servo})并将其链接到您的硬件脚本。

结论

通过将工具调用基于嵌入向量的问题匹配配对,我们现在有了一个实用的 AI 助手,它可以:

  • 回答来自精选知识集的问题,
  • 动态运行系统命令或脚本以从外部系统获取答案,
  • 与真实世界的硬件(如网络摄像头和 GPIO 驱动的 LED)集成。

这种架构是模块化的,让您能够以最少的代码更改插入新的硬件工具、脚本或 API。