第 8 章:实战 -- 项目架构设计
前七章我们一块一块地学了 SDK 的各个零件。从这一章开始,我们要把这些零件组装成一台真正的机器 -- MiniClaw,一个迷你版 CLI Agent 助手。
MiniClaw 是什么
你听说过 NanoClaw 吗?它是一个轻量级的开源 AI 助手,用 TypeScript 写的,大约 3900 行代码。它能接入 WhatsApp、支持容器隔离、有记忆系统,功能还挺齐全的。
MiniClaw 就是我们自己造的"Python 版迷你 NanoClaw"。但我们不走大而全的路线,我们走小而完整的路线:
| 对比项 | NanoClaw | MiniClaw |
|---|---|---|
| 语言 | TypeScript | Python |
| 交互方式 | WhatsApp + Web | CLI(命令行) |
| 底层 | 直接调 API | Claude Agent SDK |
| 代码量 | ~3900 行 | ~500 行(目标) |
| 定位 | 可部署的产品 | 可学习的教程项目 |
MiniClaw 的设计理念是:小到能完全理解。
想象你去宜家买了个书架,打开包装,发现零件有 200 个,说明书有 40 页 -- 你大概会先叹口气。但如果零件只有 20 个,说明书 2 页 -- 你一杯咖啡的功夫就能装好,而且完全明白每个零件是干什么的。
MiniClaw 就是那个 20 个零件的书架。它虽然小,但五脏俱全:
- 多轮对话(第 9 章)
- 记忆系统(第 9 章)
- 可扩展的工具生态(第 10 章)
- 安全防护(第 10 章)
- 定时任务(第 11 章)
- 多 Agent 协作(第 11 章)
本章我们先搭骨架 -- 把项目结构设计好,把认证和配置这两个"基础设施"先跑起来。
架构设计
MiniClaw 采用经典的分层架构。如果你写过 Web 应用,这套路你应该很熟悉:上层调下层,每层只管自己的事。
┌─────────────────────────────────┐
│ CLI 交互层 (cli.py) │ <-- 用户看到的界面:输入、输出、美化
├─────────────────────────────────┤
│ 对话引擎 (engine.py) │ <-- 核心:ClaudeSDKClient 封装
├─────────────────────────────────┤
│ 工具层 (tools/) │ <-- SDK MCP 自定义工具
├─────────────────────────────────┤
│ 存储层 (memory + config) │ <-- SQLite 记忆 + JSON 配置
└─────────────────────────────────┘
打个比方:MiniClaw 就像一家小餐馆。
- CLI 交互层是前厅 -- 服务员接待客人、传菜、收银。客人(用户)只跟这层打交道。
- 对话引擎是后厨 -- 厨师(Claude)在这里真正干活。前厅把点单传过来,后厨做好菜传回去。
- 工具层是厨房里的各种厨具 -- 刀、锅、烤箱。厨师需要什么工具,就从这里拿。
- 存储层是仓库 -- 食材(记忆数据)和菜单(配置)都存在这里。
每层的职责:
| 层 | 文件 | 职责 |
|---|---|---|
| CLI 交互层 | cli.py |
读取用户输入、格式化输出、命令解析 |
| 对话引擎 | engine.py |
管理 ClaudeSDKClient 生命周期、处理多轮对话 |
| 工具层 | tools/ |
注册和管理 MCP 工具、内置工具实现 |
| 存储层 | memory.py + config.py |
SQLite 记忆存储、JSON 配置读写 |
为什么要分层?因为你可以单独替换任何一层。比如:
- 想把 CLI 换成 Web 界面?只改
cli.py,其他不动。 - 想把记忆从 SQLite 换成 Redis?只改
memory.py。 - 想加个新工具?在
tools/里加个文件就行。
项目目录结构
miniclaw/
├── __init__.py # 版本信息
├── __main__.py # 入口: python -m miniclaw
├── auth.py # 认证: API Key / 订阅自动检测
├── config.py # 配置: ~/.miniclaw/config.json
├── cli.py # CLI 交互层 (第 9 章实现)
├── engine.py # 对话引擎 (第 9 章实现)
├── memory.py # 记忆系统 (第 9 章实现)
├── tools/ # 工具目录 (第 10 章实现)
│ ├── __init__.py
│ ├── builtin.py # 内置工具:文件摘要、代码分析等
│ └── registry.py # 工具注册中心
├── scheduler.py # 定时任务 (第 11 章实现)
└── agents.py # 多 Agent 协作 (第 11 章实现)
你可能会问:为什么不一开始就把所有文件都写好?
因为软件开发有个原则叫 YAGNI -- "You Aren't Gonna Need It"(你现在用不到它)。我们一章一章往里填代码,每一章都只加当前需要的东西。这样你在任何一个时间点看到的代码,都是完整可运行的,而不是一堆写了一半的空壳。
本章我们只实现三个文件:
| 文件 | 本章实现 | 说明 |
|---|---|---|
__init__.py |
完整 | 版本信息,一行搞定 |
__main__.py |
完整 | 项目入口,加载配置 + 检测认证 |
auth.py |
完整 | 双模式认证检测 |
config.py |
完整 | 配置管理 |
双模式认证
还记得第 3 章讲的两种认证方式吗?API Key 模式和订阅模式。MiniClaw 需要两种都支持,而且要自动检测,不用用户手动选。
检测逻辑就像你进一家酒店:
- 先看你有没有带房卡(API Key) -- 有就直接进。
- 没房卡?看你有没有在前台登记过(
claude login) -- 登记过也能进。 - 都没有?那对不起,请先办理入住。
用流程图表示:
开始
│
▼
检查 ANTHROPIC_API_KEY 环境变量
│
├── 有值 ──→ API Key 模式 ✓
│
└── 没有
│
▼
检查 claude 命令是否可用
│
├── 可用 ──→ 订阅模式 ✓
│
└── 不可用 ──→ 报错,提示用户配置 ✗
这个逻辑实现在 auth.py 中。核心代码很短:
from miniclaw.auth import detect_auth_mode
auth = detect_auth_mode()
print(auth.mode) # "api_key" 或 "subscription" 或 "none"
print(auth.display_key) # "sk-ant-api03-xxxx..." (脱敏显示)
几个设计要点:
1. API Key 脱敏显示
永远不要把完整的 API Key 打印到屏幕上。MiniClaw 只显示前 12 个字符加省略号。这是安全基本功。
2. 优雅降级
检测失败不抛异常,而是返回 mode="none" 和一条错误信息。让调用方决定怎么处理。这比直接 raise 更灵活 -- 比如你可能想在 UI 上显示一个友好的提示,而不是一段冷冰冰的堆栈跟踪。
3. CLI 路径记录
订阅模式下,我们记录 claude 命令的完整路径。这在调试的时候很有用 -- 用户可能装了多个版本的 CLI,知道具体用的哪个能帮你快速定位问题。
完整代码见本章的 miniclaw/auth.py。
配置管理
每个像样的 CLI 工具都需要配置文件。MiniClaw 的配置存在 ~/.miniclaw/config.json:
{
"model": "sonnet",
"system_prompt": "你是 MiniClaw,一个友好的 AI 助手。用中文回答问题。",
"max_turns": 30,
"max_budget_usd": 1.0,
"tools_enabled": true,
"memory_enabled": true,
"verbose": false
}
配置管理的设计思路是默认配置 + 用户覆盖:
- 代码里写好一套默认配置,保证即使没有配置文件也能正常运行。
- 如果用户创建了配置文件,就用用户的值覆盖默认值。
- 用户只需要写自己想改的项,不需要把所有配置都写一遍。
就像你新买了一台手机 -- 出厂设置已经能用了,你只需要改你在意的那几项(比如壁纸、铃声)。
from miniclaw.config import load_config
config = load_config()
print(config.model) # "sonnet" (默认值)
print(config.max_budget_usd) # 1.0 (默认值)
print(config.config_path) # "~/.miniclaw/config.json"
可配置项一览
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
model |
str |
"sonnet" |
使用的模型 |
system_prompt |
str |
见默认值 | Claude 的角色设定 |
max_turns |
int |
30 |
最大对话轮数 |
max_budget_usd |
float |
1.0 |
单次对话预算上限(美元) |
tools_enabled |
bool |
True |
是否启用工具 |
memory_enabled |
bool |
True |
是否启用记忆系统 |
verbose |
bool |
False |
是否显示详细日志 |
首次运行自动创建
如果 ~/.miniclaw/ 目录和配置文件不存在,load_config() 会自动创建它们,并写入默认配置。这样用户第一次运行就有一个可编辑的配置文件模板,不用自己从零写起。
完整代码见本章的 miniclaw/config.py。
跑起来看看
本章的代码已经可以运行了。在 miniclaw/ 目录的上级目录执行:
python -m miniclaw
如果你设置了 API Key,会看到类似这样的输出:
==================================================
MiniClaw v0.1.0 - 迷你 AI Agent 助手
==================================================
配置文件: /Users/你的用户名/.miniclaw/config.json
认证模式: api_key
API Key: sk-ant-api03-...
项目骨架已就绪!后续章节将添加完整功能。
- 第 9 章: 对话引擎 + 记忆系统
- 第 10 章: 工具生态
- 第 11 章: 高级特性
如果你用的是订阅模式(claude login 过了),会看到:
认证模式: subscription
CLI 路径: /usr/local/bin/claude
如果两个都没有,会看到错误提示并退出。
本章架构与 SDK 的对应关系
你可能会好奇:我们设计的这些层,跟之前学的 SDK API 是怎么对应的?
MiniClaw 层 对应的 SDK 概念 之前学过的章节
─────────────────────────────────────────────────────────────
CLI 交互层 receive_messages() 第 4 章
对话引擎 ClaudeSDKClient 第 4 章
工具层 create_sdk_mcp_server() / @tool 第 5 章
安全防护 hooks / can_use_tool 第 6、7 章
配置管理 ClaudeAgentOptions 第 3 章
认证检测 env / cli_path 第 3 章
前七章的知识点,在接下来的实战中会全部用到。如果某个概念记不清了,随时翻回去看。
小结
这一章我们做了四件事:
- 明确了 MiniClaw 的定位 -- 纯 Python、CLI 交互、小而完整的 AI Agent 助手
- 设计了分层架构 -- CLI 层、引擎层、工具层、存储层,各司其职
- 实现了双模式认证 -- API Key 和订阅模式自动检测,优雅降级
- 实现了配置管理 -- 默认配置 + 用户覆盖,首次运行自动创建
目前 MiniClaw 还只是个"骨架" -- 能跑起来,能检测认证,能读配置,但还不能聊天。别急,下一章我们就给它装上"大脑"。
下一章: 第 9 章 - 实战:核心引擎与记忆 -- 用 ClaudeSDKClient 实现对话引擎,用 SQLite 实现记忆系统。MiniClaw 将真正能和你聊天,而且能记住之前的对话。
本章文件清单
08-实战-项目架构设计/
README.md # 你正在读的这个文件
miniclaw/
__init__.py # 版本信息
__main__.py # 项目入口
auth.py # 双模式认证检测
config.py # 配置管理