# 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
**轻量安卓 AI Agent —— 手机里内置一个 Alpine Linux,装上就能执行命令、装软件、跑 JVM**
APK 只做协调者:对话 / 规划 / 工具调用 / 展示。执行层三档可选——
**内置 Linux**(APK 自带 busybox + proot/Alpine,装上即用,`apk` 可装软件)、
**Termux 增强**(pkg 生态、共享存储)、**普通**(纯工作区,无 shell)。
默认内置模式:零配置、零权限、零外部依赖。







---
| 首页 | 对话 + 流式 | 工具卡片 | 设置分层 |
|:---:|:---:|:---:|:---:|
|  |  |  |  |
## ✨ 特性
**执行通道(三档显式选择,无自动魔法)**
- 🐳 **内置 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 🐋