给 AI agent 造一个“认识你“的后台:Kith / agent-sys 的设计笔记

张开发
2026/4/21 12:58:59 15 分钟阅读
给 AI agent 造一个“认识你“的后台:Kith / agent-sys 的设计笔记
给 AI agent 造一个认识你的后台Kith / agent-sys 的设计笔记不是又一个 chatbot也不是又一个AI 文件管家。是我在回答一个更具体的问题为什么 Cursor、Claude Code 每次都要我从头介绍自己一遍0. 起因最近我越来越频繁地在 Cursor 和 Claude Code 之间切换干活。它们很聪明但我每次开新会话都得重复一遍我在做什么项目我习惯把代码放在哪个目录我是什么样的人我有什么偏好我最近看了哪些论文我上次写的那段 parsing 逻辑在哪个文件别碰~/Downloads/xxx里的东西那是临时的关掉窗口所有这些上下文就蒸发了。它们不是不聪明它们是失忆。但把这些一条条喂给它们也还只解决了被问才回答这半边问题。更往前一步的那半边是能不能基于我在这台设备上的信息主动给我推荐、规划、提醒agent 不应该只有在被调用的时候才存在。它更应该像操作系统的一部分后台观察我在做什么默默变得更懂我让我需要用的时候不必从头说起甚至让我根本没意识到它在帮我。换句话说我要的不只是一个知识助理而是一个常驻的、理解你的环境层让每个 agent 工具自己去记你Cursor 自己做索引Claude 自己做索引下一个 agent 再做一次—— 这是现在正在发生的事但注定重复造轮子、索引打架、数据四散有一个在所有 agent 之外、常驻本地、持续观察你的组件 —— 它是所有 agent 的共同后端也在没人调用它的时候继续长思路 2 才是我觉得长期对的方向。于是有了Kith / agent-sys。1. 这个东西到底是什么一句话一个常驻本地的守护进程它持续扫你选的目录、用 LLM 理解每个文件、把结果存进本地 SQLite然后通过 RPC 暴露给外部 agent。后台跑默认扫~/Documents、~/Desktop首次启动让你改LLM 负责给文件打标签、做摘要、抽取知识点、生成个人画像对外有 Unix Socket HTTP 两种接口19 个 RPC 端点Cursor / Claude Code / Codex / 自己写的脚本都能调数据 100% 留本地除了发给 LLM 的那一小部分内容技术上没有什么特别新颖的组件。特别的是它们拼在一起解决的那个问题。下面挑几个设计里有真正取舍的点讲。2. 为什么是 daemon不是 MCP server这是我做的第一个硬决策。当时有三个候选形态形态优劣MCP server和主流 agent 协议直连、plug and play生命周期由 agent 管agent 关了它就关了想做后台索引很别扭IDE 插件Cursor 扩展UI 集成最深绑死在一个 IDE 上独立 daemon RPCagent 随便谁来都能连后台常驻跨 IDE / 跨 agent 工具自己要处理启动、停止、鉴权MCP 在协议层面已经非常好但 MCP server 的生命周期是由调用方拉起、调用方退出时终止的。而我要做的事慢慢扫 21 万文件、每天跑一次 daily report、watch 文件变化天然需要独立寿命。你 Cursor 关了我还得跑。所以选择 daemon。协议层用最无聊的 HTTP# src/syscall/server.py 节选asyncdef_http_syscall(self,request:web.Request)-web.Response:ifnotself._check_auth(request):returnweb.json_response({error:unauthorized},status401)bodyawaitrequest.json()reqSyscallRequest(**body)resultawaitself._handle(req)returnweb.json_response(asdict(result))外部 agent 要接一条 curl 就够了。任何语言、任何工具只要能发 HTTP 就能用。MCP 那套 stdio 通信、capability negotiation 的复杂度我在本地单用户单机场景下一点都用不上。教训选协议时先问我在解决什么问题不是什么协议最新最潮。3. 用 OS 隐喻做架构约束源码树里你会看到这些目录kernel/、syscall/、scheduler/、memory/。不这不是操作系统。只是一种写代码时给自己立的规矩。借 OS 的抽象不是炫技是用别人已经想清楚的结构逼自己分层kernel/daemon.py只管生命周期启动、停止、子系统初始化syscall/只管对外 RPC绝不写业务scheduler/pool.py只管优先级队列 并发 semaphorememory/store.py只管持久化和查询agents/才是真正的 LLM 业务逻辑这种纪律最大的好处是LLM 业务变化飞快但底层的 daemon / RPC / 调度几乎不需要动。分层让我可以每周塞一个新 agent 进去而不是每周重构整个项目。类比是借来的但抽象是真的。4. Triage如何在 21 万文件里省 token这是最实际、最省钱的一个模块。我硬盘里扫出来 21 万个文件绝大部分是site-packages/、node_modules/、.cursor/extensions/里的第三方代码我一辈子不会看它们。如果把每一个丢给 LLM 问这重要吗光 input token 就能烧掉几十刀。所以 triage 分两阶段Phase 1规则快跳零 LLM 成本config/default.yaml里列一组路径子串命中就直接标skiptriage:skip_path_patterns:-site-packages/-node_modules/-.cursor/extensions/-__pycache__/-.venv/这一步砍掉了 21 万里的 18 万。剩下 3 万才真的值得让 LLM 看一眼。Phase 2加权排序 LLM 语义分诊剩下的 3 万按文件类型权重排序 —— 同样是 token 有限哪些先被 LLM 看triage:file_type_priority:.md:9# 我主要用 markdown 记录.py:8# Python 项目优先.docx:9.txt:2# txt 基本不重要hints:-Downloads 文件夹里的 PDF 多是学习资料值得总结-带 test_ 前缀的 python 文件通常是测试可以降级然后 LLM 按目录批量看不按单文件目录是更自然的语义单位返回四级标签high— 用户原创代码、个人文档、学习笔记medium— 依赖配置、数据文件、项目脚手架low— 通用库代码、标准模板skip— 第三方源码、生成文件、原始数据集关键设计用户的file_type_priority和hints只影响排序和边界判断不是硬规则。一个叫journal.txt的.txt文件仍然可以被 LLM 判为high因为语义优先于类型。这种规则过滤 LLM 排序的组合比纯 LLM 省 95% 以上的 token比纯规则灵活得多。做这类成本敏感的 LLM 应用分层兜底几乎是必修。5. 自适应 cron让 LLM 决定自己的作息传统 cron 是每天 3 点跑一次 summarize硬编码。但 agent-sys 里的 agent 有十几个每个资源消耗不同、紧迫性不同、场景依赖不同。Summarizer 白天跑费电、analyzer 刚跑完一轮没必要重跑、triage 积压到一定阈值就得跑。这类动态判断用固定 cron 表达不了。于是 cron 本身变成一个 LLM 驱动的策略引擎# 简化版的 decide 流程asyncdefdecide(self)-list[ScheduleDecision]:stateawaitself._collect_state()# 当前时间、各 agent 上次运行时间、积压量、daemon 负载decisionawaitself.llm.plan(state)# LLM 读状态 读配置里的宏观目标吐一个 JSON 计划awaitself.memory.save_decision(decision)# 存下来给 dashboard 看returndecision.tasksLLM 吃的 prompt 包括当前时刻、上次各 agent 跑完的时间哪些文件积压在 triage 队列系统负载、是否白天、是否在用户活跃时段配置里声明的宏观目标“每天至少一次 daily report”、“triage 积压超过 500 就跑”返回{tasks:[{agent:triage,reason:积压 1823 个未分诊文件高于阈值},{agent:summarizer,mode:light,reason:白天避免 deep 模式读大文件抢 CPU}]}然后有一套规则兜底LLM 挂了、返回的 JSON 格式错了、配额耗尽了 —— 立刻降级到固定规则。这种设计背后的原则用 LLM 做决策用代码做执行和兜底。LLM 负责什么时候该做什么具体执行靠同一份可靠的AgentTask接口LLM 错了大不了就是一次判断失误不影响系统正确性。6. MemoryLRU SQLite暂时不上向量库我知道很多人第一反应是知识系统上 Chroma/Qdrant 啊。我刻意不上。理由是我做的是个人知识系统。文件总量21 万triage 过后需要索引的3 万以内用户的查询延迟要求亚秒级用户同时并发查询数通常 1SQLite 在这个量级上快得离谱。我不需要 recall100我需要的是按 tag、按时间、按类型、按 triage 标签的组合过滤这是 SQL 的主场。语义检索不是不要但现阶段被 LLM 的 summary keyword 足够顶住。分两层LRU 热缓存内存中最近读过的 5000 个 file recordSQLite 冷存储所有 metadata、summary、knowledge entry# 一个典型查询rowsawaitmemory.query_files(file_types[.md,.py],triage_labels[high,medium],modified_afterdatetime.now()-timedelta(days7),limit50,)什么时候上向量库当用户的跨文件语义搜索成为高频操作、SQLite LIKE summary keyword 不够用时。这是 v0.8 以后的事现在不做。教训默认选项往往够用。上重武器之前先量化问题。7. Skills比 MCP 更轻的 agent 接入方式Daemon 本身不知道Cursor 什么时候该调我。这部分靠 skills。skill 就是一个带 YAML frontmatter 的 markdown--- name: agent-sys-user-context description: Use when the user asks based on what you know about me, what have I been working on, do you know me yet. --- You have access to a local daemon. To fetch the users current context: bash TOKEN$(cat ~/.agent_sys/auth_token) curl -X POST http://127.0.0.1:7437/syscall \ -H X-Agent-Token: $TOKEN \ -d {call_type: report.brief, params: {}, caller: cursor}Return a one-paragraph synthesis grounded only in the brief.Anthropic [最近推出 Skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills) 作为 Claude 的扩展机制Cursor 也支持。对我来说这是比 MCP 更合适的集成点 - **声明式触发**skill 通过 description 字段告诉 agent什么时候用我不需要显式调用 - **零协议开销**就是 markdownagent 自己决定怎么调 - **跨工具通用**同一份 SKILL.md 丢进 Cursor 的 ~/.cursor/skills/、Claude Code 的 ~/.claude/skills/、Codex 的 AGENTS.md都能工作 我在仓库里写了三个agent-sys-user-context查自己的上下文、agent-sys-file-search搜文件、agent-sys-admin管理 daemon。加起来不到 300 行。 **skill 是接入面daemon 是能力源**。这种分层让我不用为每个新 agent 工具单独开发集成 —— skill 写一次到处能用。 ## 8. 鉴权最简单的那种 HTTP 开在 127.0.0.1:7437。因为是本地 only我用了最朴素的鉴权 python # 启动时生成一个 token写到 ~/.agent_sys/auth_token权限 0600 token_path Path.home() / .agent_sys / auth_token if not token_path.exists(): token secrets.token_hex(32) token_path.write_text(token) token_path.chmod(0o600) # 每次 /syscall 调用都校验 X-Agent-Token header def _check_auth(self, request): return request.headers.get(X-Agent-Token) self._token/health和/status不鉴权监控和诊断方便/syscall和/reload必须带 token。威胁模型非常清楚能读你本机~/.agent_sys/auth_token0600的进程已经是你自己。不需要 OAuth不需要 mTLS。合适的安全等级不是最高的安全等级。9. 下一步这套东西对 agent 工具提供的不只是读还应该能被写。一个 agent 可以通过 syscall 告诉 daemon“这个文件很重要升级成 high”、“帮我记住 X 是 Y 的代号”。单向消费 → 双向对话。结尾整个项目的 thesis 压成一句话agent 不应该每次被召唤才出现也不该每次都要你重新自我介绍。它该是 OS 的一部分 —— 后台默默看着你、理解你你需要时顺手上来就能用你不需要它也在变得更懂你。这个方向说大不大但说小也真不小。它意味着 agent 不再是一次次独立的对话而是一种持续存在的背景智能—— 类似 Spotlight 之于文件、Time Machine 之于历史但面对的是你这个人这件事本身。Kith / agent-sys 是我对这个问题目前的答案。它还粗糙API 会继续变但核心结构我越用越觉得是对的 —— 尤其是随着 agent 工具越来越多、每一个都想单独理解你一次的时候。代码、skill、完整架构图在 GitHubhttps://github.com/MarkfuGod/KithAgent项目还在积极开发中欢迎提 issue、吐槽、PR。如果你也在为每次对 agent 都要重新自我介绍挠头或者在做类似的本地 agent 基础设施欢迎交流。

更多文章