1483 words
7 minutes
Android app开发导览
写给后端开发者:把一个”个人 AI 助理”从代码层面拆开看——它不是网站,而是一个 7×24 常驻的事件驱动守护进程。本文用一个真实项目(mini-agent)讲清楚它如何运转,并逐条对比它和传统 Web 开发的本质差异。
一、这个 App 到底是什么
它不是一个”网站”,而是一个 7×24 常驻后台的守护进程(daemon),以 systemd user service 运行。核心入口是 agent.py 的 async def main(),跑一个 asyncio 事件循环,直到收到 SIGTERM/SIGINT 才退出。
一句话概括它做的事:“很多入口把消息塞进来 → 中枢分发 → LLM/子进程/SQLite 处理 → 多个出口把结果送出去”。
真实数据流(来自代码)
┌── Telegram 轮询 (channels/telegram.py)入口 (channels) ─┼── Web API aiohttp (channels/web.py) ──┐ ├── inotify 文件监控 (BlogFileHandler) │ └── APScheduler 定时器 (core/scheduler.py) │ ▼ ┌─────────────────────────────────────┐ │ EventBus (core/event_bus.py) │ │ Event(type, data) 发布/订阅 │ │ publish() → 同步调 handler │ └─────────────────────────────────────┘ │ │ │ "message_received" "cron_tick" "file_changed" ▼ ▼ ▼ MessageHandler CronDispatcher BlogSyncTask │ │ │ ▼ ▼ ▼ IntentRouter/ TodoReminder git add→commit→ ConversationMgr ScheduledMsg pull --rebase→push │ ▼ CodeTaskExecutor (tasks/code_task.py) │ ┌──────┴──────┐ ▼ ▼ ConversationMgr PiRpcClient (LangGraph + (起一个 pi 子进程, SQLite 检查点) JSONL 协议 stdin/stdout, cwd 隔离在 pi-workspace)几个关键组件(一句话版)
| 组件 | 文件 | 它是什么 |
|---|---|---|
| EventBus | core/event_bus.py | 中枢神经。subscribe(type, handler) + publish(Event)。handler 是同步的,异步活儿用 asyncio.ensure_future 派发。异常隔离(一个 handler 挂了不影响别人)。 |
| ConversationManager | core/conversation.py | 用 LangGraph StateGraph + AsyncSqliteSaver 做多轮对话的持久化检查点;还有”记忆压缩”——超过 token 阈值就用 LLM 把旧消息总结成摘要。 |
| CodeTaskExecutor / PiRpcClient | tasks/code_task.py / tasks/pi_rpc.py | 真正干”代码任务”的地方:起一个 pi --mode rpc --no-session 子进程,通过 stdin 发 JSONL 命令、stdout 读响应(64KB 手动切分避免行缓冲上限)。 |
| CronDispatcher | tasks/cron_dispatcher.py | 把 cron_tick 事件路由给注册的 TaskActor(todo 提醒、定时消息、记忆压缩、AI 日报、双向同步)。 |
| WebChannel | channels/web.py | 内嵌 aiohttp server。Bearer token 认证(24h TTL)、SSE 流式、本地↔云双向同步的 /sync/* 端点。 |
| TelegramChannel | channels/telegram.py | 另一个入口,long polling,有 watchdog 自动重启死掉的轮询。 |
一个完整请求的生命周期(以 /query 为例)
- 用户在 Telegram 发
/query 怎么写一个快速排序 TelegramChannel收到 →event_bus.publish("query_command", {...})MessageHandler订阅了这个事件 → 调_handle_query()- 从
ConversationManager.get_context_string()取上下文 → 拼到 prompt 里 CodeTaskExecutor._run_pi()→PiRpcClient.prompt_stream()→ 起子进程、流式读text_delta事件- 结果用
log_result("[query]", text)落到data/logs/result-YYYY-MM-DD.log _chunk_message()切成 ≤4096 字符块(Telegram 限制)回发- (非 topic 模式下)
asyncio.ensure_future(blog_actor.handle_single(...))异步判断要不要生成博客
整个链路里,没有一个”HTTP 请求→响应”的同步框架在主导,是事件驱动的。
二、它和网站开发的核心区别
作为后端开发,你最熟悉的是”请求-响应”模型。这个 app 几乎在每个轴上都不同:
| 维度 | 传统网站(你熟悉的) | 这个 AI 助理 |
|---|---|---|
| 运行模型 | 进程按请求拉起(gunicorn/uwsgi worker),请求完即走。或常驻但无状态。 | 单进程常驻,asyncio 事件循环永不退出,靠 systemd 守护。状态全在内存里(会话、连接、offset)。 |
| 主导范式 | HTTP 请求-响应(同步、短生命周期) | 事件驱动 / pub-sub(EventBus)。HTTP 只是众多入口之一,且 WebChannel 本身也是嵌在同一个 event loop 里的 aiohttp server。 |
| “后端”的边界 | 后端 = 你的业务逻辑进程。外部依赖是 DB/缓存。 | 后端本身要反过来调用 LLM 和起子进程(pi RPC)。你的进程既是 server 又是 client。 |
| 延迟特征 | 毫秒~秒级。一个请求等一个响应。 | LLM 调用动辄几十秒~几分钟。所以大量流式(SSE)+ fire-and-forget(asyncio.ensure_future)。不能阻塞 event loop。 |
| 状态管理 | Redis / DB,进程无状态好横向扩展 | SQLite + LangGraph checkpoint + 内存 dict,单机单实例,状态就在进程里。_sessions、_active_threads、_topic_log_offsets 都是内存结构。 |
| 入口多样性 | 几乎只有 HTTP | HTTP(Web) + Telegram long-polling + inotify 文件事件 + cron 定时器。同一段处理逻辑被四种入口触发。 |
| 关注点 | 并发、事务、幂等、缓存、水平扩展 | ① 别阻塞 loop ② 子进程隔离(cwd 隔离防误删源码)③ 长任务超时(pi 20 分钟)④ 跨实例状态同步(本地↔云 last-write-wins)⑤ 消息分片(4096 限制) |
| 部署形态 | 容器编排、多副本、负载均衡 | systemd user service 跑在一个 Linux 用户下;加密凭据用 LoadCredentialEncrypted=;本地+云双实例靠 cron 互相同步。 |
| 错误模型 | 500 给用户看,重试靠客户端 | handler 异常被 EventBus 吞掉只记日志(不影响其他订阅者);轮询死了有 watchdog 自愈。容错优先于事务正确性。 |
| 安全模型 | 认证授权、SQL 注入、CSRF | 仍是这些,但多了:子进程能 cd 任何地方(软隔离不是硬隔离)、凭据用 systemd encrypted credentials、云上 nginx + loopback + 444 隐身模式。 |
一句话总结这个差异
网站 = “一个进程处理很多并发请求,每个请求独立且短暂”。 这个助理 = “一个常驻进程是主体本身,外界事件只是喂给它的输入;它内部以事件总线解耦多个异步生产者-消费者,并主动调用 LLM/子进程产生输出。”
换句话说,做网站你是在写”被调用的服务”;做这种助理,你是在写一个”主动的、有状态的、多入口的数字员工”。思维方式从 request handler 转到 event loop + actor。
三、想动手做类似的,先吃透三个概念
- asyncio 事件循环 +
asyncio.ensure_future:怎么在不阻塞的前提下并发跑多个慢任务。 - pub-sub 事件总线:为什么不用直接函数调用而要解耦——因为多入口 + 可插拔。
- LLM 作为后端依赖的延迟处理:流式 SSE、超时、上下文压缩——这是 AI 应用区别于传统后端最实质的工程难点。
Android app开发导览
https://sgjki547.top/posts/android-app开发导览/