# Dfy **Repository Path**: cc123hh/Dfy ## Basic Information - **Project Name**: Dfy - **Description**: 轻量安卓 AI Agent APK:APK 只做协调者(对话/规划/工具调用/展示),bash 能力全部借手机上的 Termux 执行 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-11-01 - **Last Updated**: 2026-11-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
Dfy 图标 # Dfy **轻量安卓 AI Agent —— 手机里内置一个 Alpine Linux,装上就能执行命令、装软件、跑 JVM** APK 只做协调者:对话 / 规划 / 工具调用 / 展示。执行层三档可选—— **内置 Linux**(APK 自带 busybox + proot/Alpine,装上即用,`apk` 可装软件)、 **Termux 增强**(pkg 生态、共享存储)、**普通**(纯工作区,无 shell)。 默认内置模式:零配置、零权限、零外部依赖。 ![release](https://img.shields.io/badge/release-v0.10.14-3b82f6) ![apk](https://img.shields.io/badge/APK-12%20MB-22c55e) ![kotlin](https://img.shields.io/badge/Kotlin-2.0.21-7f52ff) ![compose](https://img.shields.io/badge/UI-Jetpack%20Compose-4285f4) ![minSdk](https://img.shields.io/badge/minSdk-26-9ca3af) ![platform](https://img.shields.io/badge/平台-Android%208.0%2B-34a853) ![license](https://img.shields.io/badge/license-MIT-f59e0b)
--- | 首页 | 对话 + 流式 | 工具卡片 | 设置分层 | |:---:|:---:|:---:|:---:| | ![首页](docs/screenshots/25-home.png) | ![对话](docs/screenshots/26-chat-stream.png) | ![工具卡](docs/screenshots/27-tool-cards.png) | ![设置](docs/screenshots/28-settings-hierarchy.png) | ## ✨ 特性 **执行通道(三档显式选择,无自动魔法)** - 🐳 **内置 Linux(测试版,默认)**:APK 自带静态 busybox + proot + Alpine 3.22 rootfs—— 装上即有完整命令行,`linux_exec` 工具可 `apk add` 装软件(python3 / jq / openjdk 全生态)。 DNS 与阿里云镜像已预置,时区已配北京时间,开箱即用 - 🐚 **Termux 增强**:装了 Termux 并授权后可选此档,走 RUN_COMMAND 下发 bash, pkg 生态 + 共享存储,能力最全 - 🔌 **OpenAI 兼容**:任何提供 `/chat/completions` + function calling 的服务都能接(DeepSeek / OpenAI / Moonshot / 本地 vLLM / Ollama) **Agent 能力** - 🛠️ **十二件套工具**:`shell_exec` / `linux_exec`(内置模式)/ `env_check` / `fs_list` / `fs_read` / `fs_write` / `view_image` / `memory_save` / `memory_delete` / `export_file` / `web_search`(可选) / `generate_image`(可选) - ☕ **跑 JVM 程序**:内置环境锁定 OpenJDK 17(`apk add openjdk17-jre-headless`),Spring Boot / Maven 构建实测可用 - 🖼️ **附件上传 + 看图**:SAF 多选进 `~/uploads/`;看不看图是模型的决策——干活直接处理,理解内容才 `view_image` - 🎨 **AI 生图(可选)**:模型自主调 `generate_image` 画图,产物落 `~/images/` 并出预览卡片;OpenAI 兼容接口 - 🧠 **长期记忆**:key-value 存本机,常驻系统提示词,模型用工具自己增删改;抽屉里有管理界面 - 🕰️ **时间感知**:系统提示词每轮实时注入设备时钟 - 📦 **交付卡片**:模型产出文件后一键导出到「下载」目录,可直接打开 / 分享 - 🌐 **联网搜索(可选)**:Kimi / DeepSeek / Tavily 三选一,关闭时模型看不到该工具 - ✂️ **上下文自动压缩**:估算到达阈值自动摘要压缩,阈值与力度可调,长对话不爆上下文 - 💳 **秘钥账户制**:多组厂家配置一键切换,DeepSeek 可查余额;模型列表从接口拉取 **体验与安全** - 🌗 **三档主题**:跟随系统深色模式 / 深色 / 浅色,全页面适配 - 💬 **多会话**:40 个会话落盘,界面消息与模型上下文双份存,改名 / 置顶 / 防抖保存 - 📝 **自研 Markdown 渲染**:代码块带语言标签 + 一键复制,不引第三方库 - 🗂️ **Unix 风格工具卡片**:每条命令一张卡,工具名 / 参数 / 五种状态分色,展开看输出 - 🛡️ **三层命令守卫**:硬拦截 / 二次确认 / 路径白名单,四档策略可选——内置模式的命令同样过守卫 - 🔍 **应用内更新**:检查更新 → 下载 → 安装,全在 App 内闭环 ## 🔧 工作原理 ``` ┌─────────────────────────────────────┐ │ Dfy Agent APK │ │ Compose UI / AgentEngine 规划循环 │ │ ┌───────────────────────────────┐ │ │ │ AgentToolset(12 工具) │ │ │ │ ↕ CommandGuard 三层守卫 │ │ │ │ BackendRouter 按档位分流 │ │ │ └──────┬──────────┬─────────────┘ │ └──────────┼──────────┼───────────────┘ │ │ ┌────────▼───┐ ┌───▼──────────────┐ │ 内置模式 │ │ Termux(可选) │ │ busybox │ │ RUN_COMMAND Intent│ │ proot + │ │ app-shell 执行器 │ │ Alpine │ │ bash / pkg │ │ apk 生态 │ │ │ └────────────┘ └──────────────────┘ ``` 内置模式的实现要点:可执行文件以伪 `.so` 形式随 APK 分发(落在系统唯一放行的 `nativeLibraryDir`),proot + 用户态 loader 绕过 Android 对数据目录的 W^X 限制—— JVM(OpenJDK 17)实测可跑 Spring Boot。详见 [.wiki/decisions.md](.wiki/decisions.md) 与 [EmbeddedLab 实验记录](https://gitee.com/cc123hh/EmbeddedLab)。 ## 🚀 快速开始 ### 内置模式(默认,零配置) 1. 安装 APK([Releases](https://gitee.com/cc123hh/Dfy/releases) 或应用内「检查更新」) 2. 设置里填模型(Base URL / API Key / 模型名,任何 OpenAI 兼容接口) 3. 开聊——「跑个命令看看」「装个 python3」「搭个 Spring Boot hello world」 ### 可选:Termux 增强 需要 pkg 生态或共享存储时再配置(装 Termux → `allow-external-apps` → 授权 RUN_COMMAND → 关电池优化),详见设置页引导。不用 Termux 也完全可以,内置模式覆盖绝大多数任务。 **联网搜索、AI 生图是独立的可选能力**,在设置里单独开启并填各自的 Key。 ## 🛡️ 安全模型 三层,都在 `CommandGuard` 里,内置模式的命令同样过守卫: 1. **硬拦截**:`rm -rf /`、`mkfs`、`dd of=/dev/block/*`、fork bomb、`pm uninstall com.termux`… 2. **二次确认**:`rm -r`、`pkg/pip/npm/apk install`、`curl | sh`、`git push --force`、`su`、`pm/am/settings`、`kill`… 3. **路径白名单**:写入禁止落到 `/system`、`/vendor`、`/data/adb`、`/proc` 等 四档策略(设置页可切):**平衡**(默认)/ **严格**(非只读全确认)/ **信任**(只拦致命命令)/ **完全信任**(跳过所有拦截与确认,切换需二次确认,只给完全可控的环境用)。 > 说清楚:这是防手滑的护栏,不是沙箱。命令跑在内置 Linux(App 沙箱内)或用户自己的 Termux 权限里。 ## 🧠 关键设计决策
内置 Linux 如何绕过 Android 的 W^X(点击展开) targetSdk ≥ 29 后,Android 禁止执行 App 数据目录里的文件(SELinux neverallow execute / `execute_no_trans`)。Dfy 的做法(UserLAnd 同款、全部上机实证): 1. 可执行文件(busybox / proot / loader)以**伪 `.so`** 打进 jniLibs,PM 解压到 `nativeLibraryDir`——系统唯一放行的 exec 位置(需 `useLegacyPackaging = true`) 2. proot(termux 构建)+ 静态 loader:guest 内**每一次 execve 都被改写为执行 loader**, loader 把真正的程序当数据 mmap 进内存——内核从不执行数据目录文件,`apk` 装来的软件也能跑 3. 工作区绑定到 guest 的 `/root`,`~/` 语义与文件工具互通 踩过的坑(AGP 丢 `.so.2` 文件、argv 多传参数、模拟器 musl 装载崩溃、JDK21 崩 JDK17 正常) 全部记录在 [.wiki/decisions.md](.wiki/decisions.md)。
结果回传用广播,不用 Service(点击展开) 官方 wiki 的示例是 `PendingIntent.getService(...)` + `IntentService`。但 Android 8+ 有后台服务启动限制, App 在后台时这条路径可能被拦。本项目改成 **`PendingIntent.getBroadcast(...)` + manifest 声明的 `BroadcastReceiver`**:广播不受该限制,进程被拉起也能收到结果;无人等待时结果落盘 + 发通知,不丢。
脚本走 stdin,不走 argv(点击展开) `RUN_COMMAND` 的 `EXTRA_ARGUMENTS` 有约 128 KB 上限,还要自己做引号转义。本项目改为: 脚本喂给 bash 的 stdin(`RUN_COMMAND_STDIN`)。既绕开长度限制,也彻底避免引号地狱。
上下文不变量与自愈(点击展开) OpenAI 协议要求 `assistant.tool_calls` 里每个 id 都有一条对应 `role=tool` 消息,破坏了就 400。 执行循环全程维护这个不变量;会话从磁盘载入时 `HistoryRepair` 兜底自愈,中断的工具卡标 「结果未知」,不编结果。
## 🏗️ 构建 ```bash # debug 包 ./gradlew :app:assembleDebug # release 包(构建 → zipalign → apksigner → dist/Dfy-<版本>-<时间>.apk → 自动 bump 版本) ./scripts/release.sh # 静态检查 + 单测(CI 同款) ./gradlew ktlintCheck :app:testDebugUnitTest ``` - JDK 17+,Android SDK(platforms 35、build-tools 35.0.0);无 NDK,纯 Kotlin - 技术栈:AGP 8.7.3 / Gradle 8.9 / Kotlin 2.0.21 / Compose BOM 2024.12.01 / minSdk 26 / targetSdk 35 - `keystore.properties` 存在时 release 构建期自动正式签名,否则回退工程 debug 密钥手动签(脚本有 ⚠️ 提示) - CI:`.github/workflows/ci.yml`(ktlint → lint → 单测 → assembleDebug) ## 📁 目录结构 ``` app/src/main/java/com/dfy/agent/ ├── data/ 设置(DataStore)+ 系统提示词 + 秘钥账户 ├── embedded/ 内置 Linux:busybox/proot 执行器 + rootfs 管理(测试版) ├── termux/ RUN_COMMAND 协议层:常量 / 执行器 / 结果接收 / 自检 / 平台入口 ├── llm/ OpenAI 兼容客户端 + SSE 解析(可单测)+ 生图客户端 + 消息模型 ├── agent/ Agent 循环 + 工具集 + 记忆 / 附件 / 交付导出 + 上下文自愈 ├── security/ 命令守卫(三层) ├── debug/ 内存环形日志 + 诊断包导出 ├── search/ 联网搜索客户端(Kimi / DeepSeek / Tavily,可选) ├── update/ 检查更新 + 应用内下载安装 └── ui/ Compose 页面(首页/对话/抽屉/设置/日志/记忆/交付)+ components/ + ChatViewModel(Core + 3 Controller)+ SessionStore(多会话落盘)+ markdown/ ``` ## 📚 文档 全部资料在 [docs/](docs/)(设计/协议/截图,索引见 [docs/README.md](docs/README.md)) 与 [.wiki/](.wiki/)(踩坑 QA、决策归档、专题笔记): | 类别 | 文档 | |---|---| | 设计 | [UI 改造](docs/UI_REDESIGN.md) · [多会话](docs/SESSIONS.md) · [Markdown](docs/MARKDOWN.md) · [工具卡](docs/TOOL_CARDS.md) · [联网搜索](docs/WEB_SEARCH.md) | | 协议 | [Termux 通道](docs/TERMUX_PROTOCOL.md) · [决策归档](.wiki/decisions.md) | | 资料 | [外部 API 笔记](docs/reference/README.md)(Kimi / DeepSeek / Tavily 搜索接口等) | | 截图 | [docs/screenshots/](docs/screenshots/) | ## ⚠️ 已知限制 - **会话存本机私有目录**:不参与备份、不含 API Key;不做跨设备同步 - **内置模式为测试版**:模拟器(x86_64)上 Alpine 的部分动态程序有限制,手机(arm64)不受影响; openjdk21 启动崩溃(用 17),无 gcc 等本地编译工具链 - **常驻服务**:linux_exec 内的后台进程会让调用等待其退出,长驻服务用「启动→验证→关闭」在同一条命令内完成 - **命令串行**:一次只跑一条命令(`Mutex` 保护),避免 Agent 并发乱跑 - **无交互式 TTY**:`top`、`vim` 这类需要终端的程序用不了 - **看图能力看服务商**:tool 消息回灌多模态图片,各服务商支持不一——不支持的会 400,错误信息会回给模型向用户转述 ## 🛣️ Roadmap - [ ] 内置模式转正:rootfs 体积优化(按需组件)、更多 JDK 版本验证 - [ ] Shizuku:接 `pm` / `uiautomator` 等系统级能力,作为独立 executor - [ ] 代码块语法高亮、会话导出/分享 - [ ] 多机型实测(已覆盖:Redmi manet Android 14 / 红米 K50 Pro Android 13 / MI 8 Lite Android 10 / AVD) ## 📄 许可证 本项目以 [MIT](LICENSE) 许可开源 —— 可自由使用、修改、分发,保留版权声明即可。 Dfy 与 [Termux](https://github.com/termux/termux-app) 是独立应用,仅通过其公开的 RUN_COMMAND Intent 接口协作,使用 Dfy 不要求你的项目采用特定许可证。 ---
**Dfy** · 蓝鲸肚子里揣着一台 Linux 🐋