Spark Agent — 架构文档¶
本文档描述 Spark 的整体架构与核心流程。
概览¶
Spark 是一个本地自托管的 AI 编程 Agent,采用 Codex 式 Agent Loop 设计,支持多种 LLM Provider,内置丰富工具集,具备长会话记忆与检查点回滚能力。
用户输入 → AgentLoop(回合引擎)→ 模型推理 → 工具执行 → 结果反馈 → 下一轮
↑ ↓
上下文压缩 ← 记忆检索 ← AGENTS.md ←─── SQLite 持久化
↓
SSE 流式渲染 → Web UI
核心组件¶
1. AgentLoop (src/spark/core/loop.py)¶
AgentLoop 是核心引擎,负责管理整个对话回合:
单次 turn 流程:
用户输入
│
▼
[上下文检查] ──≥85%──→ 触发 5 段式压缩(Objective / Important Details / Work State / Next Move / Relevant Files)
│
▼
[记忆检索] → 补充到 messages
│
▼
[构建 prompt] = system + AGENTS.md + history + memory
│
▼
[调用 Provider] → 流式接收 token
│
├─ 有 tool_call ──→ 执行工具 → 收集结果 → 回到模型推理
│ │
│ ▼
│ sandbox 检查(是否允许此操作)
│ │
│ ▼
│ 审批策略(suggest/full-auto)
│
└─ 无 tool_call ──→ 输出最终答案
│
▼
落库(history + store)
│
▼
自动检查点(write/shell 类回合)
│
▼
turn_end → 推送前端
2. 上下文引擎 (src/spark/core/context.py)¶
管理 Agent 的"视野":
| 模块 | 作用 |
|---|---|
| AGENTS.md | 注入项目级说明,定义工作流与约定 |
| Token 估算 | token_count_estimate(text) → words × 1.3 |
| 自动压缩 | 85% 阈值触发,保留最近 8 条消息 + 分段摘要 |
| 记忆检索 | 关键词匹配 → LLM 抽取 → 语义检索(降级关键词) |
3. Provider 层 (src/spark/providers/)¶
统一抽象,支持多种 LLM 后端:
class BaseProvider(ABC):
@abstractmethod
async def stream(self, messages, schemas) -> AsyncIterator[Delta]: ...
class OpenAICompatProvider(BaseProvider): # OpenAI 兼容接口
class OllamaProvider(BaseProvider): # 本地 Ollama 服务
class MockProvider(BaseProvider): # 测试用 mock
4. 工具注册表 (src/spark/tools/registry.py)¶
所有工具通过 schema 注册,统一审批与沙箱检查:
ToolRegistry
├── 文件工具:read_file, write_file, apply_patch
├── 搜索工具:grep, glob, list_dir
├── 执行工具:run_shell, bg_start, bg_output, bg_kill(跨重启持久化)
├── Git 工具:git_status, git_diff, git_log, git_branch, git_add, git_commit
├── 数据工具:read_notebook, notebook_edit
├── 联网工具:web_search, web_fetch
├── 规划工具:update_plan
├── 扩展工具:task(子 Agent 委派,最多 4 个并行)
└── MCP 工具:McpBridge 桥接外部协议
5. 沙箱系统 (src/spark/sandbox.py)¶
四档权限,逐级放行:
sandbox-only: list_dir / read_file / grep / glob
↓
workspace: + write_file / apply_patch / shell
↓
full-access: + background / MCP / readonly tools
↓
unrestricted: 全部放开(仅用户自定义保护路径)
安全防护:
- Shell 命令白名单扫描
- 路径穿越检测(绝对路径 → workdir 校验)
- 保护路径清单(config.toml、sessions.db 等)
- 文件写入前 check_write_path() 校验
6. 持久化 (src/spark/store.py)¶
SQLite 存储:
| 表 | 内容 |
|---|---|
sessions |
会话元信息(id, title, model, updated_at) |
messages |
对话消息(role, content, timestamp) |
checkpoints |
检查点记录(id, session_id, label, created_at) |
tool_events |
工具调用审计(name, args, duration, ok) |
bg_jobs |
后台任务跨重启状态 |
7. 记忆系统 (src/spark/memory/)¶
每轮结束
│
▼
自动抽取(LLM 调用)
│
▼
入库(SQLite + 关键词索引)
│
▼
检索时:top_k 最近 + 语义相关性
降级策略:嵌入模型不可用时,自动降级为关键词匹配。
8. 检查点 (src/spark/core/checkpoints.py)¶
每次关键操作后自动快照:
触发条件:write_file / apply_patch / run_shell / notebook_edit
│
▼
tar.gz 快照(排除 .git / node_modules / __pycache__ 等)
│
▼
存储路径:~/.spark/checkpoints/<session_id>/<id>.tar.gz
│
▼
回滚时:还原文件 + 截断对话历史 + 重载历史记录
前端架构¶
React 19 + Vite + Tailwind 4
│
├── App.tsx # 主布局与状态管理
│ ├── SessionSidebar # 会话列表(CRUD + 关键词)
│ ├── ChatMessage # 消息渲染(markdown + 工具块)
│ ├── InputBox # 输入框(图片附件 + 快捷按钮)
│ ├── SettingsPanel # 设置面板(多 Tab)
│ └── Toast # 通知系统
│
├── api.ts # API 客户端(fetch + SSE)
├── types.ts # TypeScript 类型定义
└── components/
├── Markdown.tsx # mdx 渲染(rehype + highlight.js)
├── ThinkingBlock # 推理过程展示
├── PlanCard # 任务计划卡片
└── ContextMeter # 上下文水位仪表
通信协议:
- 实时消息:SSE(Server-Sent Events)
- CRUD 操作:REST JSON
- 事件类型:text_delta / reasoning_delta / tool_start / tool_end / plan / approval_needed / compaction / context / turn_end / turn_error / done
CLI 架构¶
spark # 默认启动 TUI
spark exec "prompt" # 无头执行
spark web # 启动 Web UI
spark sessions # 列出会话
spark resume <id> # 恢复会话
spark test --model xxx # 连通性测试
MCP 集成¶
通过 McpBridge 桥接外部 MCP 服务器:
[[mcp_servers]]
name = "filesystem"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/data"]
readonly_tools = ["ReadFile", "ReadDir", "Glob"]
安全策略:
- MCP 工具默认走审批门
- 可通过 readonly_tools 白名单声明只读(免审批)
- 服务端工具集变化时通过 schema revision 检测并剔除失效调用
- 配置在 [[mcp_servers]] 段中(stdio / sse / streamable_http 三种传输)
文件结构¶
spark/
├── src/spark/
│ ├── __init__.py
│ ├── cli.py # CLI 入口(typer)
│ ├── config.py # 配置加载(pydantic + toml)
│ ├── core/
│ │ ├── loop.py # AgentLoop 外围职责(历史/存储/记忆/压缩/计账/hooks/子代理)
│ │ ├── graph.py # LangGraph 状态机——ReAct 控制流唯一真相源(llm→tools→finalize)
│ │ ├── context.py # 上下文引擎
│ │ ├── compact.py # 对话压缩(LlamaIndex + 结构化摘要)
│ │ ├── tokens.py # Token 估算
│ │ └── checkpoints.py # 检查点管理
│ ├── providers/
│ │ ├── base.py # Provider 抽象基类
│ │ ├── factory.py # 工厂函数
│ │ ├── openai_compat.py # LiteLLM 统一实现(openai_compat + ollama 两个类)
│ │ ├── mock.py # Mock 实现
│ │ └── probe.py # 连通性探测
│ ├── tools/
│ │ ├── registry.py # 工具注册表
│ │ ├── fs.py # 文件系统工具
│ │ ├── shell.py # Shell 执行
│ │ ├── search.py # grep/glob
│ │ ├── web.py # 联网检索
│ │ ├── notebook.py # Jupyter 操作
│ │ ├── bg.py # 后台任务
│ │ └── mcp_bridge.py # MCP 桥接
│ ├── memory/
│ │ ├── service.py # 记忆服务
│ │ ├── extractor.py # LLM 抽取
│ │ ├── llm.py # 记忆 LLM 调用
│ │ └── store.py # 记忆存储
│ ├── sandbox.py # 沙箱策略
│ ├── policy.py # 工具权限策略
│ ├── store.py # SQLite 持久化
│ ├── tui/ # 终端 UI(textual)
│ └── web/ # Web 服务器(stdlib http.server)
├── tests/
│ ├── test_sandbox.py # 沙箱测试
│ ├── test_policy.py # 策略测试
│ ├── test_loop.py # 循环逻辑测试
│ ├── test_store.py # 持久化测试
│ └── test_p1_capabilities.py # P1 功能 E2E
├── web/
│ ├── src/ # React 源码
│ ├── public/ # 静态资源
│ └── package.json
├── brand/ # 品牌资产
│ ├── favicon.svg
│ ├── logo.svg
│ ├── logo-dark.svg
│ └── hero.svg
├── docs/ # 文档
│ ├── ARCHITECTURE.md
│ ├── API.md # HTTP API 参考
│ ├── CONFIG.md # 配置参考
│ ├── INSTALL.md # 安装与打包
│ └── assets/
│ ├── architecture.svg # 整体架构图
│ └── turn-flow.svg # 回合流程图
├── README.md
├── CONTRIBUTING.md
├── LICENSE
├── pyproject.toml
└── .env.example
扩展指南¶
添加新工具¶
- 在
src/spark/tools/下创建实现 - 在
registry.py的schemas()方法中注册_schema(name, description, properties, required) - 在
policy.py中归类(READONLY_TOOLS 或 SHELL_TOOLS) - 在
tools/__init__.py中导出执行函数
添加新 Provider¶
- 继承
BaseProvider - 实现
stream()异步生成器 - 在
providers/factory.py的create_provider()中添加分支 - 在
config.py的ProviderNameLiteral 中添加类型
声明式子代理(.claude/agents/,P1-4)¶
「文件即 subagent」:在仓库 .claude/agents/*.md(项目级)或
~/.claude/agents/*.md(用户级)放置一个 Markdown 文件,即声明一个可委派的专家子代理。
加载器为 src/spark/agents.py(frontmatter 语法与 skills.py 对齐,零 YAML 依赖):
---
name: reader # 必填,spawn 句柄
description: 只读代码探索 # 一行摘要,注入 <available_subagents>
when_to_use: 探索陌生代码时 # 可选,委派时机提示
tools: read_file, grep # 可选;缺省 = 全部工具
---
这里是子代理的 system prompt:角色、规则、输出格式。
行为约定:
- 发现:
discover_declared_agents(workdir)扫描两个目录,项目级同名文件优先; 非法文件(缺 frontmatter / 缺 name / 空 body)在发现时跳过并告警,按名 spawn 时严格拒绝并报错。 - 注入:系统 prompt 追加
<available_subagents>列表(name/description/when_to_use)。 - spawn:
task工具传{"agent": "reader", "prompt": "..."};并行模式传tasks: [{"agent": "reader", "prompt": "..."}, ...](最多 4 个)。子代理 body 经subagent_system_prompt()包裹后写入子回路自己的 system 槽(AgentLoop._extra_system_prompt),tools字段经ToolRegistry.tool_allowlist收敛其工具 schema。 - 复用:不新建执行器——声明式子代理走的就是既有并行 task 执行器
AgentLoop.spawn_subtask/spawn_subtasks_parallel(src/spark/core/loop.py), 仅多传system_extra/tools_allowlist两个可选参数。
仓库内置示例:.claude/agents/reader.md(只读代码探索)、.claude/agents/tester.md(跑测试汇总结论)。
设计原则¶
- 本地优先:所有数据存储本地,不依赖云服务
- 可扩展:Provider、工具、MCP 均可插件化
- 安全:沙箱四档、审批流、路径保护
- 可观测:Token 计数、上下文水位、事件流可视化
- 容错:记忆降级、模型探测、异常兜底