用了一段时间 Claude Code,被 AI 编码助手的能力震撼,但也有些想法:能否支持更多模型?能否更轻量?能否自己掌控?于是决定从零开始做一个 MeowCode —— 不是要替代 Claude Code,而是深入理解 AI Agent 的设计原理,做一个够用的编码助手。
本文记录项目初始化的完整过程:技术选型、架构设计、核心实现和 API 测试。
一、技术选型:Python + DeepSeek
1.1 编程语言选择
考虑过 Go、Rust,但最终选择 Python:
优势明显:
- AI 生态最成熟(OpenAI SDK、Anthropic SDK 都是 Python 优先)
- 开发速度快(1-2 周就能做出 MVP)
- 标准库强大(文件操作、正则、subprocess 开箱即用)
- 打包方案成熟(PyInstaller / Nuitka)
权衡成本:
- 打包体积大(20-40MB)
- 启动速度慢(1-2秒)
对于 MVP 阶段,开发效率优先于运行效率。
1.2 LLM 提供商:DeepSeek 起步
价格对比(修复 100 个 Bug 的实际成本):
| 模型 | 单次成本 | 100次成本 |
|---|---|---|
| DeepSeek v4-flash | ¥0.0018 | ¥0.18 |
| GLM-4-flash | ¥0.0015 | ¥0.15 |
| Claude Sonnet 4.5 | ¥0.54 | ¥54 |
作为个人开发者,DeepSeek 的性价比无敌 —— 比 Claude 便宜 300 倍!
架构设计支持未来扩展:
- 智谱 GLM(备选方案)
- Anthropic Claude(高端场景)
- OpenAI GPT(兼容性考虑)
1.3 界面:CLI 优先
选择命令行界面而不是 GUI:
- ✅ 简单高效,开发成本低
- ✅ 适合开发者使用场景
- ✅ 易于自动化和集成
二、架构设计:多模型抽象层
2.1 核心循环
Agent 的本质是一个反馈循环:
用户输入任务
↓
┌─────────────────┐
│ 查询 LLM │
└────────┬────────┘
↓
┌─────────────────┐
│ 解析 Action │ → bash / read_file / write_file / exit
└────────┬────────┘
↓
┌─────────────────┐
│ 执行 Action │
└────────┬────────┘
↓
┌─────────────────┐
│ 反馈结果 │
└────────┬────────┘
│
└─→ 循环,直到完成或超时
2.2 多模型架构:抽象工厂模式
使用抽象基类 + 工厂函数实现多模型支持:
# models/base.py - 抽象基类
class BaseLLMProvider(ABC):
@abstractmethod
def query(self, messages: List[Dict]) -> str:
"""查询模型"""
pass
@abstractmethod
def get_model_info(self) -> Dict:
"""获取模型信息(名称、价格、上下文长度)"""
pass
# models/deepseek.py - DeepSeek 实现
class DeepSeekProvider(BaseLLMProvider):
def __init__(self, api_key: str, model: str, base_url: str):
self.client = OpenAI(
api_key=api_key,
base_url=base_url # 硅基流动: https://api.siliconflow.cn/v1
)
self.model = model
def query(self, messages: List[Dict]) -> str:
response = self.client.chat.completions.create(
model=self.model,
messages=messages
)
return response.choices[0].message.content
# models/factory.py - 工厂函数
PROVIDERS = {
"deepseek": DeepSeekProvider,
"zhipu": ZhipuProvider, # 未来支持
"anthropic": AnthropicProvider, # 未来支持
}
def create_provider(provider_name: str, api_key: str, **kwargs):
return PROVIDERS[provider_name](api_key, **kwargs)
好处:
- 新增模型只需实现 3 个方法
- 切换模型只需修改配置
- 统一接口,代码清晰
2.3 Action 解析器
从 LLM 输出中提取结构化命令:
# utils/parser.py
class ActionParser:
@staticmethod
def parse_bash_action(text: str) -> Optional[str]:
"""解析 bash 命令:```bash-action\n<command>\n```"""
matches = re.findall(
r"```bash-action\s*\n(.*?)\n```",
text,
re.DOTALL
)
return matches[0].strip() if matches else None
@staticmethod
def parse_read_file(text: str) -> Optional[str]:
"""解析读取文件:```read-file\n<filepath>\n```"""
matches = re.findall(
r"```read-file\s*\n(.*?)\n```",
text,
re.DOTALL
)
return matches[0].strip() if matches else None
@staticmethod
def parse_write_file(text: str) -> Optional[Dict]:
"""解析写入文件:```write-file\n<filepath>\n---\n<content>\n```"""
matches = re.findall(
r"```write-file\s*\n(.*?)\n---\n(.*?)\n```",
text,
re.DOTALL
)
if matches:
filepath, content = matches[0]
return {"path": filepath.strip(), "content": content}
return None
支持多种 Action 类型:
bash-action- 执行 shell 命令read-file- 读取文件write-file- 写入文件exit- 完成任务
2.4 工具系统
命令执行器(tools/execute.py):
class CommandExecutor:
def execute(self, command: str) -> Dict:
result = subprocess.run(
command,
shell=True,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=30,
env=self.env # 禁用交互式工具
)
return {
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode,
"success": result.returncode == 0
}
文件操作(tools/file_ops.py):
class FileOperations:
@staticmethod
def read_file(filepath: str) -> str:
"""读取文件,带行号"""
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
lines = content.split('\n')
numbered = '\n'.join([f"{i+1:4d} | {line}" for i, line in enumerate(lines)])
return f"📄 {filepath}\n{numbered}"
@staticmethod
def write_file(filepath: str, content: str) -> str:
"""写入文件,自动创建目录"""
directory = os.path.dirname(filepath)
if directory and not os.path.exists(directory):
os.makedirs(directory)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
return f"✅ 已写入 {filepath}"
三、核心实现:300 行代码的 Agent
3.1 主循环
# agent.py
class MeowCodeAgent:
def run(self, user_task: str):
# 初始化对话
self.messages = [
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": user_task}
]
for iteration in range(self.max_iterations):
# 1. 查询 LLM
lm_output = self.provider.query(self.messages)
self.messages.append({"role": "assistant", "content": lm_output})
# 2. 解析 action
action = self.parser.parse_action(lm_output)
if not action:
continue
if action["type"] == "exit":
break
# 3. 执行 action
result = self.execute_action(action)
# 4. 反馈结果
self.messages.append({"role": "user", "content": result})
3.2 配置系统
使用 YAML + 环境变量:
# config/config.yaml
llm:
provider: deepseek
deepseek:
api_key: "" # 或从环境变量 DEEPSEEK_API_KEY 读取
model: deepseek-ai/DeepSeek-V3.2
base_url: https://api.siliconflow.cn/v1
agent:
system_prompt: |
You are a helpful coding assistant.
When you want to run a command, wrap it in ```bash-action
<command>
```
max_iterations: 20
command_timeout: 30
配置加载器自动合并环境变量:
class Config:
def get_llm_config(self) -> Dict:
provider = self.get('llm.provider', 'deepseek')
provider_config = self.get(f'llm.{provider}', {})
# 优先从环境变量读取 API Key
env_key = f"{provider.upper()}_API_KEY"
if env_key in os.environ:
provider_config['api_key'] = os.environ[env_key]
return {"provider": provider, **provider_config}
四、测试与验证
4.1 API 连接测试
硅基流动配置:
- Base URL:
https://api.siliconflow.cn/v1 - 模型:
deepseek-ai/DeepSeek-V3.2 - 兼容 OpenAI SDK
测试代码:
from openai import OpenAI
client = OpenAI(
api_key="sk-xxx",
base_url="https://api.siliconflow.cn/v1"
)
response = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V3.2",
messages=[
{"role": "system", "content": "你是一个有帮助的助手。"},
{"role": "user", "content": "你好,请用一句话介绍你自己。"}
]
)
测试结果:
- ✅ API 调用成功
- ✅ 响应速度快(<2秒)
- ✅ Token 使用:36 tokens(输入 18 + 输出 18)
4.2 Action 解析测试
任务:”列出当前目录的文件”
LLM 响应:
\```bash-action
ls
\```
解析结果:
- ✅ 类型:bash
- ✅ 数据:ls
4.3 完整流程测试
测试场景:列出当前目录文件并生成总结
执行流程:
-
用户输入:”列出当前目录的文件”
- Agent 第 1 轮:
- 生成命令:
ls -la - 执行并返回结果
- 生成命令:
- Agent 第 2 轮:
- 解析文件列表
- 生成结构化总结:
当前目录包含以下文件和子目录: ## 文件: - .env.example - 环境变量配置示例文件 - QUICKSTART.md - 快速开始指南 - agent.py - 主要的智能体脚本 ## 子目录: - config/ - 配置文件目录 - docs/ - 文档目录 - models/ - 模型相关文件目录 - tools/ - 工具函数目录
测试结果:
- ✅ 完整循环正常运行
- ✅ 正确执行 bash 命令
- ✅ 理解命令输出
- ✅ 生成清晰的结构化总结
4.4 性能评估
响应速度:
- 单次 API 调用:~1-2秒
- 完整循环(2轮):~3-4秒
输出质量:
- 理解能力:⭐⭐⭐⭐⭐
- 格式遵守:⭐⭐⭐⭐⭐
- 内容质量:⭐⭐⭐⭐⭐
成本:
- 简单任务(列出文件):~100 tokens
- 估算成本:<¥0.001/次
五、项目管理:Git + GitHub
5.1 仓库初始化
# 初始化本地仓库
git init
git add .
git commit -m "feat: 初始化 MeowCode Agent 项目"
# 创建 GitHub 仓库并推送
gh repo create meowcode --public --source=. --push
仓库地址:https://github.com/AriesOxO/meowcode
5.2 目录结构
meowcode/
├── agent.py # 主入口
├── models/ # LLM 提供商
│ ├── base.py # 抽象基类
│ ├── deepseek.py # DeepSeek 实现
│ └── factory.py # 工厂函数
├── tools/ # 工具系统
│ ├── execute.py # 命令执行
│ └── file_ops.py # 文件操作
├── utils/ # 工具函数
│ ├── parser.py # Action 解析
│ └── config.py # 配置加载
├── config/ # 配置文件
│ └── config.example.yaml
├── docs/ # 文档
│ ├── 设计/ # 架构设计文档
│ ├── 参考文档/ # 学习资料
│ ├── 测试/ # 测试报告
│ └── blog/ # 开发博客
└── requirements.txt # 依赖(只有 2 个!)
5.3 代码统计
Python 文件:12 个
代码行数:~300 行(核心)
总行数:2932 行(含文档)
提交数:3 次
开发时间:1 天
六、遇到的问题与解决
6.1 Windows 编码问题
现象:UnicodeEncodeError: 'gbk' codec can't encode character
原因:Windows 默认使用 GBK 编码,无法显示 emoji
解决:
import sys
import io
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
6.2 依赖管理
初始只需要 openai,后来发现配置系统需要 pyyaml:
openai>=1.0.0
pyyaml>=6.0
极简依赖策略:能用标准库就不加依赖。
总结
关键决策
- Python + DeepSeek:开发速度快,成本极低
- 抽象层设计:为未来多模型支持打好基础
- 极简依赖:只有 2 个外部依赖
- CLI 优先:简单高效,适合开发者
技术亮点
- 300 行核心代码实现完整 Agent 循环
- 工厂模式支持多模型扩展
- 模块化设计,职责清晰
- 测试完备:API/逻辑/流程 三层验证
成本优势
DeepSeek 让个人开发者也能负担 AI Agent:
- 单次调用:<¥0.001
- 100 次修复:¥0.18
- vs Claude:便宜 300 倍
下一步计划
- 添加代码搜索功能
- 实现错误重试机制
- 集成智谱 GLM
- PyInstaller 打包
- 跨平台测试
项目地址:https://github.com/AriesOxO/meowcode
欢迎 Star 和贡献!下一篇将记录代码搜索功能的实现过程。