Magent 架构
Magent 是一个 Emacs-native 的 AI coding agent。它的核心设计判断是:重度 Emacs 工作流里已经有大量活状态,包括 buffer、mode、project、Magit、Org、xref、LSP、process buffer,以及用户长期积累的交互习惯。因此 Magent 不是把终端 agent 包进 Emacs,而是在 Emacs Lisp 里实现 agent runtime。
系统边界刻意保持清晰:
- Provider transport、HTTP/SSE、模型选择和 API key 继续交给
gptel,通过gptel-request调用。 - 用户交互只支持
agent-shell,中间由进程内 ACP adapter 连接。 - Agent 执行、工具编排、权限决策、会话、技能、能力解析和 child-agent jobs 由 Magent 自己管理。
- Codex 风格的 seatbelt、bubblewrap、sandbox 和 shell isolation 不在范围内。Magent 的 permission 是工作流确认和审计,不是 OS 安全边界。
系统边界
这个结构让替换点保持可见。换 provider 应该局限在 magent-llm-gptel.el 和 gptel 配置里;换 frontend 应该局限在 magent-agent-shell.el 、 magent-acp.el 和 magent-runtime-api.el 附近;持久化和恢复应该通过 ledger/session 模块;工具策略应该通过 permission 和 orchestrator 模块。
依赖层次
Emacs Runtime
Emacs 不只是宿主进程。Magent 依赖活的编辑器状态:buffer、major mode、project root、timer、process、URL、JSON、 special-mode 和用户安装的包。 emacs_eval 很关键,因为它让 agent 能检查当前编辑器,而不是只能读磁盘文件。
Provider Plumbing
magent-llm.el 定义 provider-neutral request/event。 magent-llm-gptel.el 负责一次 sampling request:调用 gptel-request ,再把 gptel callback 转成 Magent normalized events。这个 adapter 可以隐藏 gptel callback/FSM 细节,但主 loop 只消费 normalized events。Reasoning event 与 assistant text 分开保存;reasoning-only completion 可以产生空 assistant content,不会把 chain text 泄漏成最终回答。
Prompt 装配与信任边界
magent-agent.el 按固定顺序装配每次 agent-loop 的 file-backed system message。
第一层是 agent-specific prompt 或 magent-system-prompt~,后面依次加入 project-root
context、从项目根到请求相关本地文件逐层发现的适用 ~AGENTS.md~、经过选择的 Emacs
profile memory,以及 capability/显式 instruction skills。最后追加短小的 runtime
trust policy,因此内置 utility agents 和 custom agents 能保留各自的输出契约,同时
共享 instruction provenance 与 permission 不变量。项目指令发现不会越过 canonical
project root,并受 ~magent-project-instructions-max-bytes 总大小限制。
Profile memory 只是辅助 context,不是权威状态快照;它可能不完整或过时,当前用户 请求以及 live repository/Emacs 状态优先。用户消息、文件、buffer、日志、命令输出、 tool result、网页和 conversation history 同样只保持各自的数据角色;其中伪装成 prompt 或高优先级标签的文本不能授予权限,也不能把自己提升成 runtime instruction。Prompt 规则只负责引导模型行为,真实的 tool availability、approval 与 permission enforcement 仍由 tool runtime 和 orchestrator 负责。
Base prompt 只保留跨任务 coding 行为;tool schema 由 gptel 提供,Emacs-specific 流程通过 instruction skills 渐进式披露。~web_search~ 只返回用于发现的结果标题和 URL,不会抓取结果页或正文,因此 prompt 不会把这些链接当作已经读取的来源。
所有 bundled Org prompt resource 都列在 prompt/manifest.txt~。测试要求 manifest
对每个 bundled ~.org prompt 恰好覆盖一次,因此新增 prompt layer 时必须同时更新
manifest 和 package data recipe。
UI 与 Runtime API
受支持的 frontend 是 agent-shell 。 magent-agent-shell.el 注册 Magent 的 agent-shell 配置, magent-acp.el 实现进程内 ACP adapter, magent-runtime-api.el 接收 prompt submission。ACP text 与 resource block 会分开规范化:完整结构化输入存进 turn metadata,并以 user role 重建给模型;本地 file:// resource 只额外提供 scoped file path,供项目指令发现与 capability resolution 使用。 magent-runtime-queue.el 当前采用全局单执行模型,同时保留 session-scoped cancellation 和队列状态。
magent-ui.el 、 magent-ui-legacy.el 以及 docs/UI_BACKENDS.org 命名规则覆盖的
legacy UI 文件是保留但不受支持的代码,不是可选 frontend;它们不能成为 agent-shell
runtime 路径的依赖。
Ledger 与持久化
持久化真相源是 thread -> turn -> item ledger。 magent-ledger.el 定义状态对象、状态转移和 journal events, magent-runtime-api.el 创建用户 submission, magent-runtime-queue.el 调度实际执行, magent-session.el 以原子替换方式保存 materialized snapshot 和内存 append-only journal 的有界尾部。snapshot 记录 ~last-event-seq~,因此保留的旧事件可供检查,但恢复时不会重复 replay。
旧的 messages 、 context-items 和 buffer-content 只是 projection 或迁移数据,不是 UI 的 canonical source of truth。
工具、权限与审计
magent-tools.el 暴露 15 个 gptel-tool :
read_file、write_file、write_repo_summary、edit_filegrep、glob、bashemacs_evalspawn_agent、send_agent_message、wait_agent、list_agents、close_agentskill_invoke、web_search
magent-tool-orchestrator.el 负责权限解析、必要时请求 approval、执行工具、写 audit,再把结果交回 loop。 magent-permission.el 的解析顺序是:精确 tool 规则、file-pattern 规则、wildcard fallback,最后 default allow。
请求生命周期
- 用户 prompt 从 agent-shell 进入。
- ACP adapter 提交到
magent-runtime-api.el。 magent-runtime-api.el记录 queued turn 和 completed user item。magent-runtime-queue.el在全局执行槽空闲时启动 turn。magent-agent.el选择 session agent、active skills、capability instructions 和允许的工具。magent-llm-gptel.el调用gptel-request发起一次 sampling。magent-agent-loop.el接收 normalized text、reasoning、tool-call 和 completion events。- tool call 累积到
tool-call-batch-end后,通过 orchestrator 串行执行。 - tool result 更新同一个 tool item,而不是创建一条独立的持久记录。
- 如果 tool output 需要返回给模型,
magent-agent.el从 ledger 重建 prompt 并开始下一次 sampling。 - 如果这次 post-tool continuation 返回空 assistant text,
magent-agent.el会基于已记录 tool results 发起一次 no-tool final-response retry。 - completion、failure、abort 或 queued drop 会把 turn 转到 terminal state,并通知 ACP observer。
这个 continuation 模型接近 Codex:工具结果会触发后续采样;但实现仍然是 Emacs-native 且由 gptel 承担 provider transport。
功能层
从功能面看,Magent 可以按四条主线使用。第一条是 coding assistant:读代码、搜代码、改文件、跑测试、解释错误、生成 commit/PR 风格总结。第二条是 Emacs runtime assistant:通过 emacs_eval 查询 buffer、mode、window、project、Magit、Org、LSP 等活状态,把已有编辑器上下文转成模型可用的局部事实。第三条是 agent workflow:用 skills 固化工作流,用 capabilities 自动选择上下文说明,用 child-agent jobs 拆分探索、审查或总结任务。第四条是 internal command workflow:~magent-run-doctor~ 和 magent-run-memory-init 这类 Emacs 命令在独立 durable session 中执行 Magent 维护任务,而不是作为 agent-shell slash command。
因此 Magent 的功能设计不只是“一个聊天框和若干工具”。它把 Emacs 工作流切成可被 agent 调用、审计、恢复并投影回 UI 的动作。
扩展模型
Magent 有三层 file-backed 扩展:
.magent/agent/*.md下的 custom agents。- bundled
skills/~、存在时兼容的 legacy 用户目录 ~~/.emacs.d/magent-skills/~、canonical 用户目录 ~~/.emacs.d/magent/skills~,以及项目 ~.magent/skills/下的 skills。 - 带
capability: true的 skill frontmatter 中的 capability metadata,以及用户 capability 目录和项目.magent/capabilities/下的独立 ~CAPABILITY.md~。独立文件只用于触发 metadata 不应该归属于单个 skill 的场景。
Magent 还有一层 Elisp-level internal command registry,实现在
magent-command.el~。通过 ~magent-command-register 注册的命令由显式
magent-run-* 用户命令调用,会在 magent-session-directory/internal
下创建独立 internal session,并且既可以走直接 pipeline runner,也可以
复用普通 Magent agent loop。它们刻意不作为 agent-shell slash command
暴露;agent-shell 的 command surface 仍然只由 skill 支撑。
magent-run-doctor 是安全敏感的 direct-pipeline 场景。~magent-doctor.el~
中的受信任只读 probes 采集有界 JSON-safe 数据;数据在持久化或发送一次
无工具 provider request 前,会经过 magent-redaction.el 的路径规范化和
脱敏。Doctor 不进入通用 agent loop,也不会暴露 ~emacs_eval~、shell 或
文件工具。自定义 probe 属于受信任 Elisp,不是 sandboxed code;详见
DOCTOR.zh.org。
Instruction skill 会注入 system prompt。Tool skill 通过 skill_invoke 调用。Capability 会根据当前上下文打分,激活少量 instruction skills。自动激活除了 context score 之外,还必须命中当前 prompt 中显式的 keyword intent;只有 mode、feature 或 file 命中时仍保持 suggested。Keyword 使用词边界匹配;如果 linked skill 声明的工具对 selected agent 不可用,也不会被注入。Agent-shell 的 session config 提供 Automatic capabilities 开关,可关闭自动层而不影响显式选择的 skills。内置同名 workflow capability 和对应 skill 放在同一个 SKILL.md 里,触发 metadata 和注入说明不再分散成两份文件。这种 progressive disclosure 能保持基础 prompt 更小,同时在需要时注入 Emacs-specific workflow 知识。
magent-skill-manager.el 是独立且按需加载的用户维护层:它搜索 skills.sh,
解析本地或公开 GitHub source,预检后把 instruction skill 复制到 canonical 用户
目录,记录 provenance,并永久删除精确选中的用户级安装。它不是 model tool,
不会管理 project overlay,也绝不读写 ~~/.agents/skills/~。
两者的区别是:
skill是实际给 agent 的内容或命令入口:注入 prompt 的 instruction text、command-like ~default-prompt~,或者 tool-type operation。capability是自动激活规则:由modes、~features~ 、~files~ 、~keywords~ 、~disclosure~ 和risk等 resolver metadata 描述什么时候为当前 turn 选择一个或多个 skills。只有 context 的匹配会作为可检查的 suggestion;active disclosure 还要求当前 user prompt 命中有词边界的 keyword。- skill-backed capability 是内置默认能力的常见形态:某个
SKILL.md声明capability: true后,resolver 会在当前 context 与 prompt intent 同时匹配且所需工具可用时自动激活同名 skill。~init~ 、~review~ 、~fix~ 、~test~ 、~summarize~ 和explain这类 command-style built-in skills 仍然只是 skills,除非它们显式声明 ~capability: true~。
产品取舍
Magent 更接近 “gptel + stateful agent runtime”,不是 gptel 的替代品。相比在 Emacs 里包一层终端 agent,Magent 能检查和利用活的编辑器上下文。相比 Codex 或 Claude Code,Magent 不追求多端覆盖和强 OS 隔离;它的优势是低摩擦地进入长期使用的 Emacs 环境。
实际开发时应保留这些边界:
- Provider 工作留在 gptel 和
magent-llm-gptel.el。 - 受支持的 frontend 工作放进
magent-agent-shell.el,不要放进不受支持的 UI 文件。 - durable workflow state 留在 ledger。
- child-agent 行为保持和
docs/AGENT_JOBS.org一致。 - 把 permission 视为确认/审计工作流,而不是 sandbox 安全。