1. 项目概述:轻量化Android原生AI Agent运行时
在移动AI领域,我们正面临一个尴尬的现实:大多数所谓的"AI应用"本质上只是云端API的调用器。它们既不能自主思考,也无法直接操作设备,更谈不上真正的智能代理(Agent)。传统解决方案如Termux+Python组合,不仅体积庞大(动辄上百MB),启动缓慢,还严重依赖第三方环境,完全违背了移动端开发的原生性原则。
我开发的so-claude-code项目彻底改变了这一局面。这是一个用纯C语言编写的Android原生AI Agent运行时,编译后仅200KB左右,无需Root权限、不依赖Termux或Python环境,直接以.so动态库形式运行在任何Android设备上。它实现了完整的ReAct推理循环、文件操作、Shell执行等核心能力,特别值得一提的是其创新的动态技能热加载系统——允许在运行时加载.so插件扩展Agent功能。
2. 架构设计与核心特性
2.1 整体架构解析
项目的架构设计遵循Android原生开发的最佳实践:
code复制┌─────────────────────────────────────────┐
│ Android App (Kotlin + Jetpack Compose) │
│ │ JNI 桥接 │
├──────────────┼───────────────────────────┤
│ libcclaude.so(核心Agent Runtime) │
│ ┌──────────┐ ┌──────────────────────┐ │
│ │ ReAct循环│ │ 工具注册表 & 技能系统 │ │
│ │(推理+执行)│ │ read/write/shell/skill│ │
│ └────┬─────┘ └──────────────────────┘ │
│ │ │
│ ┌────┴────────────────────────────┐ │
│ │ 记忆系统 | 人格设定 | 会话管理 │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
2.2 关键设计原则
-
零全局状态:所有上下文封装在
cclaude_ctx_t结构体中,完美支持多实例并行运行,避免传统AI框架常见的状态污染问题。 -
纯回调驱动:C层完全不涉及UI操作,所有交互通过预设的回调函数抛给Kotlin层处理,这种设计使得核心运行时可以完全独立于UI框架。
-
最小依赖原则:仅依赖cJSON处理JSON数据,网络请求通过JNI回调给Android端的OkHttp实现,保持核心库的极致轻量化。
-
安全沙箱机制:所有工具调用都经过风险分级和权限检查,危险操作必须通过用户审批,技能插件采用权限隔离设计。
3. 核心模块实现细节
3.1 上下文管理系统
上下文管理是整个运行时的基础,主要数据结构如下:
c复制struct cclaude_ctx {
char* api_key;
char* model;
char* data_dir;
// 回调函数集
cclaude_token_cb token_cb;
cclaude_approval_cb approval_cb;
cclaude_http_cb http_cb;
// 会话状态
cJSON* messages; // 消息历史
cJSON* memory; // 长期记忆
cclaude_approval_mode_t approval_mode;
char last_error[256];
};
这种设计使得每个Agent实例都拥有完全独立的运行环境,特别适合多任务场景。上下文创建和销毁的接口非常简单:
c复制// 创建新上下文
cclaude_ctx_t* ctx = cclaude_create("api-key", "claude-3-haiku", "/data/data/com.agent/files");
// 销毁上下文
cclaude_destroy(ctx);
3.2 内置工具系统
运行时内置了三类基础工具,覆盖了大部分本地操作需求:
- 文件读写工具:
c复制static char* tool_readfile(const cJSON* args, void* ud) {
const char* path = cJSON_GetStringValue(cJSON_GetObjectItem(args, "path"));
FILE* f = fopen(path, "r");
// ...读取文件内容并返回JSON格式结果
}
static char* tool_writefile(const cJSON* args, void* ud) {
const char* path = cJSON_GetStringValue(cJSON_GetObjectItem(args, "path"));
const char* content = cJSON_GetStringValue(cJSON_GetObjectItem(args, "content"));
// ...写入文件并返回操作结果
}
- Shell命令工具:
c复制static char* tool_shell(const cJSON* args, void* ud) {
const char* cmd = cJSON_GetStringValue(cJSON_GetObjectItem(args, "command"));
FILE* fp = popen(cmd, "r");
// ...捕获命令输出并返回
}
- 工具注册机制:
c复制void cclaude_register_tool(cclaude_ctx_t* ctx, const char* name,
cclaude_risk_t risk_level,
cclaude_tool_handler handler);
每个工具都关联了风险等级(SAFE/MODERATE/DANGEROUS),这是安全审批机制的基础。
3.3 ReAct推理循环
ReAct(Reasoning + Acting)是当前AI Agent的核心范式,我们的实现主要包括以下步骤:
- 接收用户输入:通过
cclaude_send()接口将用户请求传入系统 - 生成推理计划:分析任务需求,拆解为工具调用序列
- 执行工具调用:根据风险等级触发审批流程
- 处理工具结果:将结果反馈给推理引擎
- 生成最终响应:综合所有中间结果形成完整回答
简化版的主循环实现如下:
c复制int react_loop(cclaude_ctx_t* ctx) {
// 1. 任务分析阶段
if(ctx->token_cb) ctx->token_cb("🤖 开始分析任务...\n", ctx->token_ud);
// 2. 工具调用阶段
cJSON* args = cJSON_CreateObject();
cJSON_AddStringToObject(args, "command", "ls /sdcard/Documents");
char* res = run_tool(ctx, "shell", args);
// 3. 结果处理阶段
if(ctx->token_cb) ctx->token_cb("\n✅ 任务完成", ctx->token_ud);
return 0;
}
3.4 技能热加载系统
这是本项目最具创新性的功能之一,允许动态加载.so插件来扩展Agent能力:
c复制int cclaude_skill_load(cclaude_ctx_t* ctx, const char* so_path) {
void* handle = dlopen(so_path, RTLD_NOW);
if(!handle) return -1;
// 每个技能插件必须导出初始化函数
int (*init_func)(cclaude_ctx_t*) = dlsym(handle, "cclaude_skill_init");
if(!init_func || init_func(ctx) != 0) {
dlclose(handle);
return -2;
}
// 注册成功,保存handle
g_skill.sk[g_skill.cnt++].handle = handle;
return 0;
}
技能插件示例(创建Flask项目):
c复制#include "cclaude.h"
static char* skill_create_flask(const cJSON* args, void* ud) {
// 实现创建Flask项目的具体逻辑
return strdup("{\"status\":\"flask项目创建成功\"}");
}
int cclaude_skill_init(cclaude_ctx_t* ctx) {
// 注册插件提供的工具
cclaude_register_tool(ctx, "create_flask", CCLAUDE_MODERATE, skill_create_flask);
return 0;
}
4. Android集成与性能优化
4.1 JNI桥接层
为了让C核心与Kotlin/Java层无缝协作,我们设计了精简的JNI接口:
c复制JNIEXPORT jlong JNICALL
Java_com_agent_CClaude_nativeCreate(JNIEnv* env, jobject thiz,
jstring api_key,
jstring model,
jstring data_dir) {
const char* key = (*env)->GetStringUTFChars(env, api_key, 0);
const char* mod = (*env)->GetStringUTFChars(env, model, 0);
const char* dir = (*env)->GetStringUTFChars(env, data_dir, 0);
cclaude_ctx_t* ctx = cclaude_create(key, mod, dir);
// ...初始化回调等
(*env)->ReleaseStringUTFChars(env, api_key, key);
(*env)->ReleaseStringUTFChars(env, model, mod);
(*env)->ReleaseStringUTFChars(env, data_dir, dir);
return (jlong)ctx;
}
4.2 Kotlin封装类
Android端使用Kotlin提供了更友好的接口:
kotlin复制class CClaude(apiKey: String, model: String, dir: String) {
private val ptr = nativeCreate(apiKey, model, dir)
fun send(msg: String) = nativeSend(ptr, msg)
fun installSkill(path: String): Int = nativeInstallSkill(ptr, path)
companion object {
init { System.loadLibrary("cclaude") }
private external fun nativeCreate(k: String, m: String, d: String): Long
private external fun nativeSend(p: Long, msg: String)
private external fun nativeInstallSkill(p: Long, path: String): Int
}
}
4.3 性能优化技巧
-
内存管理:所有内存分配都有明确的归属,C层分配的内存由C层释放,通过
cclaude_free_str()统一接口。 -
JSON处理优化:使用cJSON而不用更复杂的解析库,在保证功能的前提下最小化依赖。
-
线程模型:建议将Agent运行在后台线程,通过回调与UI线程交互,避免阻塞主线程。
-
流式输出:token回调机制支持流式输出,可以实现打字机效果的用户体验。
5. 安全机制详解
5.1 三级风险管控
- SAFE(安全):只读操作,如读取文件、搜索内容
- MODERATE(中等):可能影响系统状态的操作,如写入文件
- DANGEROUS(危险):可能造成严重影响的操作,如执行Shell命令
5.2 审批模式配置
c复制typedef enum {
CCLAUDE_APPROVAL_AUTO = 0, // 仅危险操作需审批
CCLAUDE_APPROVAL_CAUTIOUS = 1,// 中等+危险操作需审批
CCLAUDE_APPROVAL_STRICT = 2, // 所有工具调用需审批
CCLAUDE_APPROVAL_YOLO = 3 // 全部自动放行(不推荐)
} cclaude_approval_mode_t;
审批回调示例:
c复制static int approval_cb(const char* tool, const char* args, void* ud) {
// 这里应该显示Android弹窗让用户确认
// 返回1表示批准,0表示拒绝
return 1;
}
5.3 技能权限隔离
每个技能插件在初始化时需要声明其需要的权限:
c复制typedef enum {
SKILL_PERM_FILE = 1 << 0, // 文件操作权限
SKILL_PERM_SHELL = 1 << 1, // Shell命令权限
SKILL_PERM_NETWORK = 1 << 2, // 网络访问权限
SKILL_PERM_MEMORY = 1 << 3 // 内存访问权限
} cclaude_skill_perm_t;
6. 实际应用案例
6.1 自动化文档处理
用户请求:"帮我找出/sdcard/Documents中所有包含'TODO'的Python文件,并生成汇总报告"
Agent执行流程:
- 调用
glob工具列出所有.py文件 - 对每个文件调用
readfile读取内容 - 使用内置的文本分析功能查找TODO标记
- 调用
writefile生成汇总报告
6.2 开发环境搭建
用户请求:"在/sdcard/projects下创建一个新的Flask项目"
Agent执行流程:
- 检查是否已安装Flask创建技能
- 若未安装,提示用户下载并安装技能插件
- 调用
create_flask工具生成项目骨架 - 验证项目结构并返回创建结果
6.3 系统维护任务
用户请求:"检查我的存储空间使用情况"
Agent执行流程:
- 调用
shell工具执行df -h命令 - 解析命令输出
- 以可视化格式呈现结果
7. 开发实践与经验分享
7.1 跨语言交互要点
- 字符串处理:JNI中的字符串转换要注意及时释放资源,避免内存泄漏
- 类型映射:C与Java/Kotlin之间的类型系统差异需要仔细处理
- 异常处理:C层错误需要通过适当机制反馈到Java层
7.2 常见问题排查
- dlopen失败:检查.so文件的路径和权限,确保ABI兼容
- 内存泄漏:使用Android Profiler监控native内存使用情况
- 回调失效:确保上下文对象生命周期管理正确
- 权限问题:AndroidManifest.xml中声明必要的存储和网络权限
7.3 性能调优经验
- 避免在C/JNI边界频繁传递大量数据
- 对频繁调用的工具进行性能优化
- 使用内存池技术管理频繁分配释放的小内存块
- 考虑引入LRU缓存机制缓存常用工具调用结果
8. 项目构建与部署
8.1 编译环境准备
- 安装Android NDK(建议版本r25c+)
- 配置CMake(3.18+)
- 准备cJSON源码(1.7.15+)
8.2 CMake配置要点
cmake复制cmake_minimum_required(VERSION 3.18)
project(cclaude)
# 设置C标准
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
# 添加源文件
add_library(cclaude SHARED
cclaude.c
agent.c
tools.c
skill.c
jni_cclaude.c
cjson/cJSON.c
)
# 包含目录
include_directories(. cjson/)
# 链接库
target_link_libraries(cclaude log android)
8.3 集成到Android项目
- 将C源码放入
app/src/main/cpp/目录 - 在
build.gradle中配置NDK选项:
groovy复制android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags ""
arguments "-DANDROID_STL=c++_shared"
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.22.1"
}
}
}
9. 技术决策与设计思考
9.1 为什么选择纯C实现?
- 极致轻量化:C语言生成的二进制体积最小,没有运行时开销
- 广泛兼容性:C是Android NDK最基础的支持语言,兼容性最好
- 性能优势:对于底层系统操作,C语言可以提供最佳性能
- 依赖最小化:避免引入复杂的C++标准库依赖
9.2 不采用现有AI框架的原因
- 体积问题:TensorFlow Lite等框架体积过大
- 灵活性:现有框架难以支持动态技能加载
- 控制力:自主实现可以精确控制内存和线程模型
- 依赖管理:避免带入不必要的依赖项
9.3 安全与能力的平衡
在设计工具系统时,我们特别注重安全性与功能性的平衡:
- 最小权限原则:每个技能只能访问其声明需要的权限
- 用户知情权:危险操作必须经过用户明确确认
- 沙箱隔离:不同技能实例运行在隔离的上下文中
- 审计日志:所有工具调用都可以记录日志供审查
10. 未来扩展方向
10.1 技能市场生态
可以建立一个技能插件市场,开发者可以发布各种功能插件:
- 数据库操作技能
- 网络请求技能
- 图像处理技能
- 机器学习推理技能
10.2 多模态扩展
当前版本主要处理文本信息,未来可以扩展:
- 图像识别和处理能力
- 语音输入输出支持
- 传感器数据接入
10.3 分布式协作
多个设备上的Agent实例可以协作完成任务:
- 任务分片与合并
- 结果同步机制
- 设备能力发现
在移动设备上实现真正的本地化AI Agent是一个充满挑战但极具价值的领域。so-claude-code项目证明,通过精心的设计和C语言的强大能力,我们可以在200KB左右的体积内实现功能完整的Agent运行时。这个项目为移动端AI应用开辟了新的可能性,使智能���理真正成为设备的一部分,而不是云端服务的简单终端。
