一、项目背景

最近我尝试用 Python 设计一个属于自己的编程型智能体,目标不是简单地做一个聊天机器人,而是希望它能够真正参与到编程工作中,比如:

  • 读取项目文件

  • 创建和修改代码

  • 运行 Python 脚本

  • 自动执行测试

  • 根据运行结果继续修复问题

  • 保存一些长期记忆,例如代码风格、测试命令、项目习惯

  • 通过 DeepSeek API 实现自然语言控制

经过初步实现后,项目已经可以正常运行。作为第一个版本,整体效果是可接受的。后续如果有时间,还可以继续优化工具调用逻辑、任务规划能力、代码修改算法和多轮调试能力。

本文主要记录这个项目的设计思路、核心结构、使用方式和运行效果。为了避免文章过长,本文不会贴出完整源码,只展示核心框架和关键代码思路。


二、项目目标

这个项目的定位是一个 编程型智能体 CodePilot

它和普通聊天机器人的区别在于:

普通聊天机器人更多是:

用户提问 → 模型回答

而编程型智能体希望做到:

用户提出编程任务
        ↓
智能体分析任务
        ↓
决定是否需要调用工具
        ↓
读取文件 / 写入文件 / 执行代码 / 运行测试
        ↓
根据工具结果继续判断
        ↓
最终给出完成结果

也就是说,它不仅要“会说”,还要“会做”。


三、技术栈

本项目主要使用:

Python
DeepSeek API
SQLite
JSON 工具调用协议
文件系统操作
subprocess 执行命令
命令行交互

整体没有使用特别复杂的第三方框架,主要是为了方便理解智能体底层运行逻辑。


四、整体架构设计

项目大体分为以下几个部分:

CodePilot 编程型智能体
│
├── main.py              # 程序启动入口
├── config.py            # 配置读取
├── llm.py               # DeepSeek API 客户端
├── agent.py             # 智能体核心循环
├── memory.py            # SQLite 长期记忆
├── logger_setup.py      # 日志配置
│
├── tools/
│   ├── base.py          # 工具基类和工具注册表
│   ├── file_tools.py    # 文件读写工具
│   ├── code_tools.py    # 代码运行与测试工具
│   ├── git_tools.py     # Git 相关工具
│   ├── memory_tools.py  # 记忆工具
│   └── project_tools.py # 项目结构分析工具
│
└── workspace/
    └── .agent/
        ├── memory.sqlite3
        └── logs/

这里最核心的是三个模块:

agent.py
llm.py
tools/

其中:

  • llm.py 负责和 DeepSeek API 通信

  • agent.py 负责智能体的思考与工具调用循环

  • tools/ 负责给智能体提供实际操作能力


五、为什么要设计工具系统

如果智能体只能调用大模型,那它最多只能生成代码文本。

但如果我们给它工具,它就可以真正操作项目。

例如:

read_file      读取文件
write_file     写入文件
replace_text   替换代码片段
run_python     运行 Python 文件
run_tests      执行测试
list_files     查看项目结构
remember       保存长期记忆
recall         读取长期记忆

这样一来,用户输入:

帮我创建一个 hello.py,运行后输出 Hello DeepSeek Agent

智能体内部就可能执行:

1. 调用 write_file 创建 hello.py
2. 调用 run_python_file 运行 hello.py
3. 根据运行结果判断任务是否完成
4. 最终告诉用户执行成功

六、DeepSeek API 配置方式

为了安全,API Key 不能直接写进代码里。

推荐使用 .env 文件或环境变量保存。

例如在项目根目录创建 .env 文件:

DEEPSEEK_API_KEY=你的DeepSeek_API_Key
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=你的模型名称

如果使用 Windows CMD,也可以临时设置:

set DEEPSEEK_API_KEY=你的DeepSeek_API_Key
set DEEPSEEK_BASE_URL=https://api.deepseek.com
set DEEPSEEK_MODEL=你的模型名称
python main.py

如果使用 PowerShell,可以这样设置:

$env:DEEPSEEK_API_KEY="你的DeepSeek_API_Key"
$env:DEEPSEEK_BASE_URL="https://api.deepseek.com"
$env:DEEPSEEK_MODEL="你的模型名称"
python main.py

七、核心配置模块设计

配置模块主要负责读取工作区路径、API Key、模型名称、超时时间等。

大体框架如下:

from dataclasses import dataclass
from pathlib import Path
import os


@dataclass
class Settings:
    workspace: Path
    api_key: str
    base_url: str
    model: str
    command_timeout: int
    max_steps: int
    require_confirmation: bool


def load_settings() -> Settings:
    workspace = Path("workspace").resolve()
    workspace.mkdir(parents=True, exist_ok=True)

    return Settings(
        workspace=workspace,
        api_key=os.getenv("DEEPSEEK_API_KEY", ""),
        base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
        model=os.getenv("DEEPSEEK_MODEL", "your-model-name"),
        command_timeout=30,
        max_steps=8,
        require_confirmation=True,
    )

这里有一个关键点:

api_key=os.getenv("DEEPSEEK_API_KEY", "")

程序不会把密钥写死,而是从环境变量读取。


八、DeepSeek 客户端设计

llm.py 的作用是封装大模型调用。

它只负责一件事:

把 messages 发给 DeepSeek API
拿回模型返回的文本

大体框架如下:

import requests


class DeepSeekClient:
    def __init__(self, api_key: str, base_url: str, model: str):
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self.model = model

    @property
    def enabled(self) -> bool:
        return bool(self.api_key)

    def chat(self, messages: list[dict]) -> str:
        url = f"{self.base_url}/chat/completions"

        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }

        payload = {
            "model": self.model,
            "messages": messages,
            "temperature": 0.2,
        }

        response = requests.post(url, headers=headers, json=payload, timeout=60)
        response.raise_for_status()

        data = response.json()
        return data["choices"][0]["message"]["content"]

这一层不关心智能体怎么调用工具,也不关心用户任务是什么,只负责 API 通信。


九、工具注册系统设计

为了让智能体可以调用不同工具,需要一个统一的工具注册表。

每个工具包含:

工具名称
工具描述
工具参数
工具处理函数
风险等级

核心框架如下:

from dataclasses import dataclass
from typing import Callable, Any


@dataclass
class Tool:
    name: str
    description: str
    schema: dict
    handler: Callable[..., str]
    risk: str = "low"

    def run(self, arguments: dict[str, Any]) -> str:
        return self.handler(**arguments)


class ToolRegistry:
    def __init__(self):
        self.tools = {}

    def register(self, tool: Tool):
        self.tools[tool.name] = tool

    def get(self, name: str) -> Tool | None:
        return self.tools.get(name)

    def descriptions(self) -> list[dict]:
        return [
            {
                "name": tool.name,
                "description": tool.description,
                "schema": tool.schema,
                "risk": tool.risk,
            }
            for tool in self.tools.values()
        ]

这样后续新增工具会很方便,只要注册进去即可。


十、文件工具设计

文件工具是编程型智能体最基础的能力。

主要包括:

list_files     列出文件
read_file      读取文件
write_file     写入文件
append_file    追加内容
replace_text   替换文本
search_text    搜索文本

核心设计思想是:

所有文件操作都限制在 workspace 工作区内
禁止访问工作区外部路径
写入文件前可开启备份
高风险操作需要确认

大体框架如下:

from pathlib import Path


def resolve_workspace_path(workspace: Path, path: str) -> Path:
    target = (workspace / path).resolve()

    if not str(target).startswith(str(workspace)):
        raise ValueError("禁止访问工作区之外的路径")

    return target


def read_file(workspace: Path, path: str) -> str:
    file_path = resolve_workspace_path(workspace, path)
    return file_path.read_text(encoding="utf-8")


def write_file(workspace: Path, path: str, content: str) -> str:
    file_path = resolve_workspace_path(workspace, path)
    file_path.parent.mkdir(parents=True, exist_ok=True)
    file_path.write_text(content, encoding="utf-8")
    return f"已写入文件:{path}"

这个模块最重要的不是代码复杂度,而是安全边界。

因为智能体拥有文件写入能力,如果不限制路径,可能会误操作系统文件。


十一、代码执行工具设计

代码执行工具主要负责运行 Python 文件和测试命令。

例如:

run_python_file
run_tests
run_shell_command

大体框架如下:

import subprocess
import sys
from pathlib import Path


def run_python_file(workspace: Path, path: str, timeout: int = 30) -> str:
    file_path = workspace / path

    result = subprocess.run(
        [sys.executable, str(file_path)],
        cwd=workspace,
        capture_output=True,
        text=True,
        timeout=timeout,
    )

    return f"""
退出码:{result.returncode}

stdout:
{result.stdout}

stderr:
{result.stderr}
"""

运行代码是一个高风险操作,所以我在项目里加入了确认机制。

当智能体准备执行代码时,会提示用户:

需要确认高风险操作:
工具:run_python_file
风险:execute
是否执行?输入 y 执行,其他任意键取消:

这样可以避免模型自动执行一些不安全命令。


十二、记忆系统设计

为了让智能体记住长期偏好,我使用 SQLite 实现了简单记忆系统。

例如可以记住:

我的代码风格
项目使用的测试命令
常用技术栈
命名习惯
是否优先使用 pathlib

大体框架如下:

import sqlite3
import time


class SQLiteMemory:
    def __init__(self, db_path):
        self.db_path = db_path
        self.init_db()

    def init_db(self):
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS memories (
                    key TEXT PRIMARY KEY,
                    value TEXT NOT NULL,
                    updated_at REAL NOT NULL
                )
            """)

    def set(self, key: str, value: str):
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("""
                INSERT INTO memories(key, value, updated_at)
                VALUES (?, ?, ?)
                ON CONFLICT(key)
                DO UPDATE SET value = excluded.value,
                              updated_at = excluded.updated_at
            """, (key, value, time.time()))

    def get(self, key: str) -> str:
        with sqlite3.connect(self.db_path) as conn:
            row = conn.execute(
                "SELECT value FROM memories WHERE key = ?",
                (key,)
            ).fetchone()

        return row[0] if row else ""

使用示例:

记住我的代码风格:Python 使用类型注解,函数尽量短,优先使用 pathlib

后续再让智能体生成代码时,它就可以参考这个偏好。


十三、智能体核心循环

整个项目最核心的是 agent.py

智能体并不是一次性回答,而是一个循环:

接收用户任务
    ↓
构造系统提示词
    ↓
调用 DeepSeek
    ↓
解析模型返回的 JSON
    ↓
判断 action
    ↓
如果是工具,则执行工具
    ↓
把工具结果反馈给模型
    ↓
继续下一轮
    ↓
直到模型返回 finish

大体框架如下:

class CodingAgent:
    def __init__(self, settings, llm, registry):
        self.settings = settings
        self.llm = llm
        self.registry = registry

    def answer(self, user_input: str) -> str:
        messages = [
            {"role": "system", "content": self.build_system_prompt()},
            {"role": "user", "content": user_input},
        ]

        for step in range(self.settings.max_steps):
            raw = self.llm.chat(messages)
            decision = self.parse_json(raw)

            action = decision.get("action")
            action_input = decision.get("action_input", {})

            if action == "finish":
                return decision.get("final", "任务完成")

            tool = self.registry.get(action)
            observation = tool.run(action_input)

            messages.append({
                "role": "assistant",
                "content": raw,
            })

            messages.append({
                "role": "user",
                "content": f"工具执行结果:{observation}",
            })

        return "达到最大执行步数,任务未完全完成。"

这里有一个很关键的点:

模型不能随便输出自然语言,而是被要求输出固定 JSON,例如:

{
  "thought": "我需要先创建 hello.py 文件",
  "action": "write_file",
  "action_input": {
    "path": "hello.py",
    "content": "print('Hello DeepSeek Agent')"
  },
  "final": ""
}

如果任务完成,则输出:

{
  "thought": "文件已经创建并运行成功",
  "action": "finish",
  "action_input": {},
  "final": "已创建 hello.py,并验证运行成功。"
}

这样 Python 程序才能稳定解析模型意图,并调用对应工具。


十四、安全机制设计

编程型智能体一定要考虑安全问题。

因为它具有:

写文件能力
运行代码能力
执行命令能力
读取项目文件能力

所以必须加限制。

我在项目里做了几层保护:

1. 工作区沙盒

所有文件操作只能发生在 workspace 目录下。

例如:

允许:
workspace/hello.py

禁止:
../system_file.txt
C:/Users/xxx/Desktop/private.txt

2. 高风险操作确认

对于写文件、运行代码、执行命令等操作,需要用户确认。

示例:

需要确认高风险操作:
工具:write_file
风险:write
是否执行?输入 y 执行,其他任意键取消:

3. API Key 不写入代码

API Key 通过环境变量读取,不硬编码到 Python 文件中。

4. 日志记录

每次运行会记录日志,方便排查问题。

日志目录大体为:

workspace/.agent/logs/

5. 命令白名单

对于 shell 命令,不建议完全开放。

更安全的做法是只允许:

python
git
dir
ls
type
cat

危险命令应该禁止或要求二次确认。


十五、启动入口设计

main.py 负责启动程序。

大体框架如下:

def main():
    settings = load_settings()
    agent = build_agent(settings)

    print("DeepSeek CodePilot 编程型智能体已启动")
    print(f"工作区:{settings.workspace}")
    print(f"模型:{settings.model}")

    if agent.llm.enabled:
        print("模式:DeepSeek 自然语言智能体模式")
    else:
        print("模式:本地命令模式")

    while True:
        user_input = input("你:").strip()

        if user_input in {"/exit", "exit", "quit", "退出"}:
            break

        response = agent.answer(user_input)
        print(f"\nCodePilot:\n{response}")

程序启动后,如果检测到 API Key,就进入自然语言智能体模式。

如果没有检测到 API Key,则进入本地命令模式。


十六、项目运行方式

1. 进入项目目录

cd your_project_path

这里的 your_project_path 替换为自己的项目目录即可,不建议在文章或截图中暴露真实电脑路径。

2. 设置环境变量

set DEEPSEEK_API_KEY=你的DeepSeek_API_Key
set DEEPSEEK_BASE_URL=https://api.deepseek.com
set DEEPSEEK_MODEL=你的模型名称

3. 启动程序

python main.py

如果看到类似信息:

DeepSeek CodePilot 编程型智能体已启动
模式:DeepSeek 自然语言智能体模式

说明 API Key 已经配置成功。


十七、实际测试任务

我先测试了一个最简单的任务:

帮我创建一个 hello.py,运行后输出 Hello DeepSeek Agent

智能体执行流程大概是:

1. 模型判断需要创建文件
2. 调用 write_file 工具
3. 用户确认写入
4. 创建 hello.py
5. 模型判断需要运行文件
6. 调用 run_python_file 工具
7. 用户确认执行
8. 程序输出 Hello DeepSeek Agent
9. 智能体返回验证通过

其中生成的 hello.py 内容非常简单:

print("Hello DeepSeek Agent")

最终智能体返回:

已创建 hello.py,运行后输出 Hello DeepSeek Agent,验证通过。

这个测试说明:

DeepSeek API 调用正常
JSON 决策循环正常
工具调用正常
文件写入正常
代码执行正常
高风险操作确认正常

十八、效果图展示

下面预留 4 张实际运行效果图。

图 1:Shell 中启动智能体成功


图 2:Shell 中修改py文件


图 3:PyCharm 中创建并运行 hello.py


图 4:PyCharm 中查找创建的 hello.py


十九、遇到的问题和解决方法

问题 1:程序提示未检测到 API Key

一开始启动时,如果看到:

未检测到 DEEPSEEK_API_KEY

说明当前 Python 运行环境没有读取到密钥。

解决方式:

set DEEPSEEK_API_KEY=你的DeepSeek_API_Key
python main.py

或者在 .env 文件中配置:

DEEPSEEK_API_KEY=你的DeepSeek_API_Key

需要注意的是:

.env 文件要放在项目根目录
不要命名成 .env.txt
等号两边不要有空格
PyCharm 和 CMD 的环境变量可能不是同一个环境

问题 2:CMD 中检测不到环境变量

可以用下面命令测试:

python -c "import os; print('已检测到' if os.getenv('DEEPSEEK_API_KEY') else '未检测到')"

如果显示:

未检测到

说明当前终端没有设置成功。

CMD 中正确写法是:

set DEEPSEEK_API_KEY=你的DeepSeek_API_Key

PowerShell 中正确写法是:

$env:DEEPSEEK_API_KEY="你的DeepSeek_API_Key"

这两个命令不能混用。

问题 3:文件创建后短时间内没有查到

测试时发现,有时刚创建完文件后,询问文件位置可能会出现短暂判断不稳定。

但直接查看工作区目录,可以找到文件。

例如:

dir workspace

或者:

type workspace\hello.py

这个问题后续可以通过优化工具调用策略解决,例如当用户问“文件在哪里”时,强制先调用 list_files,而不是直接让模型凭上下文回答。


二十、当前版本的不足

这个项目目前只是初始版本,还有很多可以优化的地方。

1. 工具选择算法还可以优化

目前模型根据提示词自行决定调用哪个工具。

后续可以加入更稳定的规则,例如:

用户问文件位置 → 优先调用 list_files
用户问文件内容 → 优先调用 read_file
用户要求运行 → 调用 run_python_file
用户要求测试 → 调用 run_tests

这样可以减少模型误判。

2. 代码修改方式还可以优化

当前版本可以直接写入文件,但更理想的方式是使用补丁:

读取原文件
生成 diff
用户确认
应用 patch
运行测试

这样会更安全,也更适合真实项目。

3. 项目索引能力还可以增强

后续可以加入项目索引器,自动分析:

项目入口文件
依赖文件
测试目录
核心模块
函数和类结构

这样智能体面对大型项目时会更稳定。

4. 多轮调试能力还可以增强

理想状态下,智能体应该可以:

运行测试失败
    ↓
读取错误信息
    ↓
定位相关文件
    ↓
修改代码
    ↓
再次运行测试
    ↓
直到测试通过

当前版本已经具备基础能力,但还可以继续优化任务规划和错误分析。


二十一、后续优化方向

后续如果继续完善,我计划从以下几个方向入手。

1. 增加代码 diff 展示

每次修改文件前,先展示修改前后的差异。

例如:

- old code
+ new code

这样用户可以更清楚智能体改了什么。

2. 增加补丁式修改

相比直接覆盖文件,补丁式修改更安全。

流程可以设计成:

read_file
    ↓
generate_patch
    ↓
show_diff
    ↓
user_confirm
    ↓
apply_patch

3. 增加项目索引数据库

可以将项目中的类、函数、文件摘要保存起来,方便智能体快速理解项目结构。

4. 增加 Web 页面

目前是命令行版本,后续可以做成:

FastAPI 后端
Vue / React 前端
Web 聊天界面
文件管理面板
工具调用日志面板

5. 增加多智能体协作

例如拆分成:

架构师 Agent
编码 Agent
测试 Agent
代码审查 Agent
文档 Agent

不同智能体负责不同任务。


二十二、总结

这次项目实现了一个基于 DeepSeek API 的 Python 编程型智能体 CodePilot。

它已经具备了一个编程助手的基础能力:

自然语言交互
DeepSeek API 调用
文件读写
代码运行
测试执行
高风险操作确认
SQLite 长期记忆
日志记录
工作区沙盒保护

通过测试,智能体可以根据自然语言任务创建 Python 文件,并运行验证结果。

虽然当前还只是初始版本,算法和工具调用策略还不够完善,但整体流程已经跑通,说明这个方向是可行的。

后续只要继续优化工具选择、项目索引、补丁修改、测试反馈和多轮调试能力,就可以逐步把它打造成一个更加实用的个人编程智能体。

这次实践最大的收获是:

智能体的核心不是单纯调用大模型,
而是让大模型学会规划任务、调用工具、观察结果、继续行动。

当模型拥有了工具能力之后,它就不再只是一个问答系统,而是可以真正参与实际开发流程的自动化助手。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐