MeowCode AI编码助手开发记录(一)

 

用了一段时间 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 完整流程测试

测试场景:列出当前目录文件并生成总结

执行流程

  1. 用户输入:”列出当前目录的文件”

  2. Agent 第 1 轮
    • 生成命令:ls -la
    • 执行并返回结果
  3. 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

极简依赖策略:能用标准库就不加依赖。


总结

关键决策

  1. Python + DeepSeek:开发速度快,成本极低
  2. 抽象层设计:为未来多模型支持打好基础
  3. 极简依赖:只有 2 个外部依赖
  4. CLI 优先:简单高效,适合开发者

技术亮点

  1. 300 行核心代码实现完整 Agent 循环
  2. 工厂模式支持多模型扩展
  3. 模块化设计,职责清晰
  4. 测试完备:API/逻辑/流程 三层验证

成本优势

DeepSeek 让个人开发者也能负担 AI Agent:

  • 单次调用:<¥0.001
  • 100 次修复:¥0.18
  • vs Claude:便宜 300 倍

下一步计划

  • 添加代码搜索功能
  • 实现错误重试机制
  • 集成智谱 GLM
  • PyInstaller 打包
  • 跨平台测试

项目地址:https://github.com/AriesOxO/meowcode

欢迎 Star 和贡献!下一篇将记录代码搜索功能的实现过程。