Achai (AI Agent in C)
Achai 是一个基于 C 语言构建的高性能、轻量级 AI Agent 运行时环境。它专为嵌入式设备(如树莓派、IoT 网关)设计,支持 ReAct 认知架构、工具调用(Function Calling)、技能动态加载、本地和远程MCP服务以及 MQTT 远程控制。
✨ 主要特性
- 轻量级运行时: 纯 C 语言编写,低内存占用,适合嵌入式环境。
- ReAct 架构: 实现 "思考-行动-观察" (Think-Act-Observe) 循环,支持复杂任务拆解。
- 多模态交互: 支持命令行交互 (REPL) 和 MQTT 远程指令控制。
- 工具与技能:
* 内置工具: Shell 命令执行、文件读写、定时器、闹钟等。 * 动态技能: 支持通过 skills/ 目录动态加载外部技能配置。 * IoT 控制: 内置 IoT 管理模块,支持通过配置文件 (iot_config.json) 定义设备,并进行读写操作。 * Web & MCP: 集成 MCP 协议,支持连接本地或远程的 MCP 服务。
- 异步架构: 基于 Reactor 模式的事件驱动设计,集成 Paho MQTT,实现非阻塞 I/O。
- 长短期记忆:
* 短期记忆: JSON 文件实时存储最近对话。 * 长期记忆: 基于 SQLite + Vector Search 的归档机制,自动总结并向量化存储重要信息。
- 模块化设计: 支持多种 LLM 后端(目前支持火山引擎 Volcengine,架构上支持扩展)。
🛠️ 编译指南 (Build)
前置依赖
请确保您的系统已安装以下工具和库:
- CMake (>= 3.10)
- GCC 或 Clang 编译器
- Make
- libcurl (用于 HTTP 请求)
- pthread (线程支持)
- OpenSSL (用于 HTTPS/MQTT)
- zlib (用于数据压缩)
编译步骤
1. 克隆项目
git clone
cd achai2. 本地编译 (x86_64)
使用提供的脚本进行一键编译:
chmod +x build.sh
./build.sh编译成功后,可执行文件 Achai 将生成在 build/ 目录下。
3. 交叉编译 (ARM)
如果您需要为 ARM 设备(如树莓派、IoT 网关)编译,请使用专门的交叉编译脚本。 该脚本会自动下载并静态编译 curl, openssl, zlib 等依赖库,生成无需外部依赖的静态二进制文件。
chmod +x build_arm.sh
./build_arm.sh编译成功后,可执行文件 Achai 将生成在 build_arm/ 目录下。
部署到 ARM 设备: 请将以下文件复制到设备:
build_arm/Achai(可执行文件).env(配置文件)cacert.pem(CA 证书,用于 HTTPS)skills/目录 (可选)
运行前请设置证书环境变量:
export CURL_CA_BUNDLE=/path/to/cacert.pem
./Achai调试模式 (可选)
如果需要开启调试符号及内存检查 (AddressSanitizer):
# 修改 build.sh 或 build_arm.sh 中的 CMAKE_BUILD_TYPE
cmake -DCMAKE_BUILD_TYPE=Debug ..
make🧪 测试 (Testing)
本项目包含单元测试和集成测试,使用 CTest 进行管理。
运行所有测试
在 build 目录下运行:
ctest --output-on-failure或者:
make test运行特定测试
您可以根据测试名称运行特定的测试套件:
# 运行单元测试 (Unit Tests)
ctest -R unit_tests
# 运行网络模块测试 (Network Tests)
ctest -R network_tests
# 运行 MQTT 集成测试 (Integration Tests - 需要 MQTT Broker)
ctest -R integration_mqtt测试说明
- unit_tests: 核心逻辑的单元测试,涵盖 Utils, Core, Agent, Skills 等模块。使用 Mock 模拟网络和 LLM 调用,无需外部依赖。
- network_tests: 网络客户端的逻辑测试,使用 Mock 驱动模拟 LLM 响应。
- integration_mqtt: MQTT 模块的集成测试。注意:此测试需要本地或配置的 MQTT Broker 处于运行状态,否则会失败。
⚙️ 配置说明 (Configuration)
Achai 使用 .env 文件进行配置。程序启动时会自动在当前目录、父目录或祖父目录查找 .env 文件。
请在项目根目录创建 .env 文件,并参考以下配置:
# --- 基础配置 ---
# 工作区目录 (默认: workspace)
ACHAI_WORKSPACE=workspace
# 技能目录 (默认: skills)
ACHAI_SKILLS_DIR=skills
# 日志级别 (0:User, 1:Process, 2:Debug, 3:Error)
ACHAI_LOGGER=2
# 是否输出日志到控制台 (1:开启, 0:关闭)
ACHAI_LOG_TO_CONSOLE=1
# --- LLM 模型配置 ---
# LLM 提供商 (目前支持: volcengine)
LLM_PROVIDER=volcengine
# 模型接入点 ID (例如火山引擎的 Endpoint ID)
LLM_MODEL_ID=ep-20240604055538-2q2h9
# API Key (格式:
_API_KEY,大写)
VOLCENGINE_API_KEY=your_api_key_here
# --- 记忆与归档配置 ---
# 触发归档的对话条数 (默认: 25)
ACHAI_MEMORY_ARCHIVE_THRESHOLD=25
# 归档后保留的最近条数 (默认: 5)
ACHAI_MEMORY_ARCHIVE_KEEP=5
# 归档时重叠的条数 (默认: 5)
ACHAI_MEMORY_ARCHIVE_OVERLAP=5
# --- Embedding 配置 ---
# Embedding 模型路径 (GGUF 格式)
ACHAI_EMBEDDING_MODEL=models/embedding/bge-small-zh-v1.5-f32.gguf
# 数据库文件路径 (默认: memory/achai.db)
ACHAI_MEMORY_PATH=memory/achai.db
# --- MQTT 配置 (IoT 集成) ---
# MQTT Broker 地址
MQTT_HOST=localhost
# MQTT 端口
MQTT_PORT=1883
# 客户端 ID
MQTT_CLIENT_ID=achai_device_01
# 订阅的主题 (接收指令)
MQTT_TOPIC_IN=achai/chat/in
# 发布的主题 (发送回复)
MQTT_TOPIC_OUT=achai/chat/out
# (可选) MQTT 用户名和密码
# MQTT_USERNAME=user
# MQTT_PASSWORD=pass
# --- IoT 配置 (IoT) ---
# IoT 配置文件路径 (默认: config/iot_config.json)
ACHAI_IOT_CONFIG=config/iot_config.json
# IoT 数据库路径 (默认: iot.db)
ACHAI_IOT_DB=iot.db
IoT 配置文件 (iot_config.json)
Achai 使用 config/iot_config.json 定义 IoT 设备。支持 只读 (read) 和 只写 (write) 两种权限。
配置示例:
{
"devices": [
{
"name": "living_room_temp",
"topic": "achai/sensor/temp",
"type": "float",
"unit": "°C",
"access": "read"
},
{
"name": "kitchen_light_control",
"topic": "achai/kitchen/light/set",
"type": "string",
"unit": "mode",
"access": "write"
}
]
}- access="read": 设备为只读(如传感器)。系统会自动订阅
topic,接收到的消息会存入本地数据库iot.db。 - access="write": 设备为只写(如控制器)。Agent 可以使用
set_iot工具向topic发送指令,但不会订阅该主题,也不会将指令存入数据库。 - type: 支持
int,float,bool,string。系统会自动进行类型转换和校验。
MCP 服务配置 (mcp_config.json)
Achai 支持通过 mcp_config.json 文件配置多个 MCP 服务器。支持 Stdio (本地进程) 和 HTTP/SSE (远程服务) 两种模式。
文件路径优先级:
- 环境变量
ACHAI_MCP_CONFIG指定的路径 - 当前目录下的
mcp_config.json - 父目录或祖父目录下的
mcp_config.json
配置示例:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/files"],
"env": {
"NODE_ENV": "production"
}
},
"remote-agent": {
"url": "http://localhost:8000/sse",
"api_key": "sk-..."
}
}
}配置字段说明:
- Stdio 模式 (通过命令行启动本地服务):
* command: (必填) 可执行命令或脚本路径。 * args: (可选) 命令行参数数组。 * env: (可选) 环境变量键值对对象。
- HTTP 模式 (连接远程 SSE 服务):
* url: (必填) 远程服务的 SSE 端点地址。 * api_key: (可选) 用于认证的 Bearer Token。
🚀 使用说明 (Usage)
1. 启动 Agent
确保已配置好 .env 文件,然后在 build 目录下运行:
./Achai或者通过命令行参数指定模型:
# 用法: ./Achai [provider] [model_id]
./Achai volcengine ep-2024xxxx2. 交互模式
启动后,您可以直接在终端输入自然语言指令与 Agent 交互:
User: 帮我设置一个 5 分钟后的闹钟
Achai: 好的,正在为您设置...
[调用 set_timer 工具]
Achai: 已为您设置好 5 分钟后的闹钟。3. MQTT 远程控制
Achai 启动后会自动连接配置的 MQTT Broker。
- 发送指令: 向
achai/chat/in主题发布消息。 - 接收回复: 订阅
achai/chat/out主题获取 Agent 的响应。
4. 目录结构
src/: 源代码tools/: 工具定义与注册
* definitions/: 工具的具体实现代码 * tool_list.c: 工具注册列表
skills/: 技能定义文件 (Markdown 格式)workspace/: Agent 运行时产生的文件存储位置memory/: 长期记忆数据库存放位置config/: 配置文件存放位置 (如 iot_config.json)prompt/: 系统提示词模板存放位置logs/: 运行时日志存放位置models/: 本地模型存放位置 (需自行下载模型文件)AGENTS.md: 详细的开发与架构文档
🛠️ 如何添加新工具 (How to Add Tools)
Achai 支持扩展自定义工具。工具源代码位于项目根目录的 tools/ 文件夹下。
步骤 1: 创建工具实现文件
在 tools/definitions/ 目录下创建一个新的 .c 文件(例如 my_tool.c),并使用 DEFINE_TOOL 宏实现工具逻辑。
#include
#include
#include "utils/cJSON.h"
#include "tools.h" // 引入基础工具定义
// 使用 DEFINE_TOOL 宏定义工具函数
// 自动生成函数签名: char* tool_my_add(cJSON* args)
DEFINE_TOOL(my_add) {
// 1. 解析参数 (假设 args 是 [1, 2])
// GET_ARG_INT(index, variable_name)
GET_ARG_INT(0, num1);
GET_ARG_INT(1, num2);
// 2. 执行逻辑
// ...
// 3. 返回结果 (自动处理内存分配)
char buffer[128];
snprintf(buffer, sizeof(buffer), "%d + %d = %d", num1, num2, num1 + num2);
RETURN_STRING(buffer);
}步骤 2: 注册工具
打开 tools/tool_list.c 文件,在 tools_register_all() 函数中注册您的新工具。
- 声明外部函数(或者在头文件中声明):
// Forward declaration
char* tool_hello_world(cJSON* args);- 调用
tools_register:
void tools_register_all(void) {
// ... 其他工具 ...
// 参数: 名称, 描述, 参数格式(JSON schema 字符串), 函数指针
tools_register(
"hello_world",
"Say hello to someone",
"[\"name\"]",
tool_hello_world
);
}常用宏说明
- DEFINE_TOOL(name): 定义工具函数
tool_name。 - GET_ARG_STR(idx, var): 获取字符串参数,存入
const char* var。如果类型不匹配会自动返回错误。 - GET_ARG_INT(idx, var): 获取整数参数,存入
int var。 - RETURN_STRING(str): 返回字符串结果。
- RETURN_INT(val): 返回整数结果(自动转字符串)。
- RETURN_ERROR(msg): 返回错误信息。
步骤 3: 重新编译
cd build
make重启 Achai 后,Agent 即可感知并使用该工具。
🔌 如何添加本地 MCP 服务 (How to Add Local MCP Server)
除了内置的 C 语言工具,Achai 还支持通过 Model Context Protocol (MCP) 集成外部工具服务。您可以编写 Python、Node.js 等语言的 MCP Server,并通过标准输入输出 (stdio) 与 Achai 通信。
步骤 1: 创建 MCP Server 脚本
在项目根目录下创建一个新目录(例如 MCP_local),并在其中编写 Server 脚本(例如 server.py)。
该脚本需要实现基本的 MCP 协议 JSON-RPC 交互,主要处理以下方法:
initialize: 初始化握手tools/list: 返回工具列表tools/call: 执行工具调用
以下是一个 Python 实现示例:
import sys
import json
def main():
while True:
try:
# 读取标准输入
line = sys.stdin.readline()
if not line:
break
req = json.loads(line)
method = req.get("method")
req_id = req.get("id")
# 1. 处理初始化请求
if method == "initialize":
resp = {
"jsonrpc": "2.0",
"id": req_id,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"serverInfo": {"name": "MyLocalServer", "version": "1.0"}
}
}
print(json.dumps(resp))
sys.stdout.flush()
# 2. 处理初始化通知 (无响应)
elif method == "notifications/initialized":
pass
# 3. 返回工具列表
elif method == "tools/list":
resp = {
"jsonrpc": "2.0",
"id": req_id,
"result": {
"tools": [
{
"name": "weather_query",
"description": "查询指定地点的天气",
"inputSchema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "城市名称"}
},
"required": ["location"]
}
}
]
}
}
print(json.dumps(resp))
sys.stdout.flush()
# 4. 处理工具调用
elif method == "tools/call":
params = req.get("params", {})
name = params.get("name")
args = params.get("arguments", {})
# 兼容性处理:如果 args 是列表,取第一个元素
if isinstance(args, list) and len(args) > 0 and isinstance(args[0], dict):
args = args[0]
content = ""
if name == "weather_query":
loc = args.get("location", "未知地点")
content = f"{loc} 的天气是:晴朗,25°C"
else:
content = f"未知工具: {name}"
resp = {
"jsonrpc": "2.0",
"id": req_id,
"result": {
"content": [{"type": "text", "text": content}]
}
}
print(json.dumps(resp))
sys.stdout.flush()
# 5. 心跳检测
elif method == "ping":
resp = {"jsonrpc": "2.0", "id": req_id, "result": {}}
print(json.dumps(resp))
sys.stdout.flush()
except json.JSONDecodeError:
continue
except Exception as e:
continue
if __name__ == "__main__":
main()步骤 2: 配置 mcp_config.json
在项目根目录下创建或编辑 mcp_config.json 文件,注册您的本地 Server。
{
"mcpServers": {
"local_mcp": {
"command": "python3",
"args": ["-u", "server.py"],
"env": {
"PYTHONPATH": "/absolute/path/to/achai/MCP_local",
"LOG_LEVEL": "INFO"
}
}
}
}注意:cwd(工作目录) 建议使用绝对路径,确保 Achai 能正确找到脚本位置。args中的-u参数对于 Python 很重要,它强制标准输出不缓存,保证通信实时性。
步骤 3: 启动 Achai
配置完成后,正常启动 Achai:
./AchaiAchai 启动时会自动读取 mcp_config.json,启动配置的子进程,并加载其中定义的工具(如 weather_query)。您可以在对话中直接使用这些工具。
☁️ 如何连接远程 MCP 服务 (How to Connect Remote MCP Server)
除了本地子进程,Achai 还支持通过 HTTP (SSE - Server-Sent Events) 协议连接远程运行的 MCP 服务器。这对于将重型计算任务卸载到云端或连接共享的 Agent 服务非常有用。
步骤 1: 部署远程 MCP Server
远程 Server 需要支持 MCP 的 SSE 传输规范。您可以使用官方 SDK 或兼容框架部署。
例如,一个简单的 Node.js SSE Server 可能运行在 http://myserver.com:8000/sse。
步骤 2: 配置 mcp_config.json
在 mcp_config.json 中添加 url 字段即可:
{
"mcpServers": {
"remote_math_solver": {
"url": "http://192.168.1.100:8000/sse",
"api_key": "optional-secret-token"
}
}
}url: 远程服务的 SSE 端点。Achai 会先连接此端点接收事件,并通过 POST 请求发送指令。api_key: (可选) 如果远程服务需要鉴权,Achai 会将其放入 HTTP 请求头的Authorization: Bearer中。
步骤 3: 启动 Achai
启动后,Achai 会自动尝试连接该 URL。连接成功后,远程 Server 暴露的工具将自动注册到 Achai 的工具列表中。
📝 开发手册
更多关于项目架构、代码结构和开发规范的详细信息,请参阅 AGENTS.md。
📄 许可证 (License)
本项目基于 MIT 许可证开源。详情请参阅 LICENSE 文件。
Copyright (c) 2024-2025 Achai
