
无论你是有过几台云服务器还是在小公司负责研发环境大概率都遇到过同一个场景想快速知道服务器 CPU 是不是被打满了、内存还剩多少、磁盘是不是又要报警却只能登录 SSH 敲top、free -h、df -h。单机看看没问题但只要你希望让这个状态“持续可见”或者想让同事打开浏览器就能看懂当前负载字符界面就不太够用了。于是“做一个服务器监控面板”成了很常见的需求。可传统路径往往很重要么上 Prometheus Grafana组件多、学习曲线陡要么自己动手写前端页面、轮询接口、处理图表没有半天下不来。这篇文章要探讨的是第三条更轻的路径用 Codex 这类 AI 编程智能体把一个“服务器监控面板CPU/内存/磁盘一目了然”从想法变成可运行的小项目。这也是“Codex 100 个真实案例”系列里非常有代表性的场景。先给出我对这个案例的判断监控面板本身不难难的是它同时涉及数据采集、后端 API、前端图表、进程运行、端口访问多个环节。任何一个环节没接上页面照样白屏。因此它很适合用来检验 Codex 这类工具究竟是“只会生成代码片段”还是“真的能帮你把工程任务做完”。读完这篇文章你会知道如何把需求拆给 AI、如何让它自主迭代运行、如何验证最终结果以及哪些坑必须由人来兜底。1. 为什么用“服务器监控面板”跑 AI 编程案例很多人第一次用 AI 编程时会从算法题或片段级代码开始。这类任务对 AI 太“友好”了上下文短、目标单一、错误容易定位。但真实开发中我们遇到的更多是“小但完整”的系统需求例如这个监控面板。它有几个特点非常适合用来观察 AI 编程工具的能力边界目标指标非常明确CPU 使用率、内存使用率、磁盘使用率。范围不过大不需要用户体系、多租户、分布式采集一个轻量网页足够。跨多个技术环节后端要读系统指标要暴露 HTTP 接口前端要定时拉数据并画趋势图。验证成本极低浏览器打开页面数值会变化成功失败一眼可见。这套特征决定了它是一个“代码量不大但工程结构完整”的任务。如果 AI 能独立完成这份工作说明它已经不只是聊天窗口里的代码生成器而具备了一定的任务拆解和工程组织能力。实际情况也确实如此。Codex 这类 AI 编程智能体和普通补全代码的 Copilot 有一个本质差异它可以被安排去“执行一个任务”而不仅仅是“补全一段代码”。在这个过程中它会读取项目目录、创建文件、安装依赖、运行命令、查看报错然后继续修改代码直到任务达到验收标准。所以这个案例真正要回答的问题是当代码量从“一个函数”升级为“一个带前后端的小项目”时AI 还能不能保持可靠如果它连监控面板这个小而完整的场景都不能闭环那放到更大的软件工程里就更需要谨慎对待。反过来如果它能在合理迭代后把服务跑通那就说明它至少能承担一部分“初级研发工程师”的工作——前提是需求被描述得足够清楚。2. Codex 是什么它和普通代码助手的区别在展开操作之前先明确 Codex 的定位。从公开信息看Codex 是 OpenAI 面向开发者提供的 AI 编程工具核心思路不是简单的“对话生成代码”而是以一个编程智能体的身份参与开发。它能理解你的自然语言目标访问当前项目文件在终端中执行命令根据错误日志修改代码并反复迭代直到任务完成。实际入口可能包括桌面端、命令行工具或 IDE 扩展不同时期官方提供的形态会变化具体以官方文档为准但这不影响我们讨论它的工作方式。它和传统 AI 辅助编程工具最大的区别可以用一个例子说明。过去用聊天式 AI 写代码时流程通常是我把问题描述给 AI它返回一段代码我复制到项目里自己运行报错后把错误再粘回去问它。而 Codex 这类 Agent 形态的工具会把“写代码 - 运行 - 看日志 - 改代码 - 再运行”这个循环承担起来。它不是在提供建议而是在执行开发任务。这对开发体验的改变是结构性的你不需要在 IDE 和聊天窗口之间反复切换。它可以一次修改多个文件而不只是返回单个代码片段。它能通过执行命令来验证自己的产出比如启动服务、请求接口、观察返回数据。当代码报错时它能直接读取报错信息而不是等你手动复制。但凡事都有另一面。Codex 能“执行命令”不代表你可以完全放弃控制。它在工作目录里创建的文件、安装的依赖、启动的服务都需要人来做安全审查。它可能误解你的需求可能过度设计也可能在一个错误方向上来回打转。真正决定项目质量上限的仍然是开发者给出的需求质量与验收标准。这也带出了本文后面贯穿始终的一个观点与 Codex 协作的正确姿势不是“当甩手掌柜”而是“当一个会写需求文档、会验收结果的工程负责人”。3. 环境准备与前置条件在开始项目之前需要把运行环境准备好。这个项目很小但目录是否干净、Python 环境是否可用、Codex 是否完成授权都会影响后续开发体验。3.1 准备一台可操作的机器我建议把实验放在自己的云服务器、虚拟机或本地开发机上不要在未授权的生产环境随意操作。演示项目会安装 Python 依赖并启动 Web 服务在这些操作之前先确认这台机器是你有权做实验的环境。操作系统方面Linux 最合适因为采集 CPU、内存、磁盘数据时各平台的系统调用差异最小。macOS 和 Windows 也能运行但部分指标可能受平台限制。本文后续示例以 Linux 为主如果你在 Windows 上实验注意磁盘路径和负载指标的差异。3.2 确认 Python 环境后端使用 Python 生态建议使用 Python 3.9 及以上版本。版本不是本文的硬性要求但太低会导致部分依赖无法安装。先确认版本python3 --version如果没有问题创建一个干净的实验目录mkdir -p ~/server-monitor cd ~/server-monitor这里有一个容易被忽略的点让 AI 在空目录里开始工作比让它在一个塞满不相关文件的项目里工作安全得多。因为 Codex 在执行任务时可能读取目录内容也可能批量修改文件。目录越干净越能避免它误碰无关代码。3.3 登录并授权 Codex由于 Codex 的实际入口和授权方式会随官方版本调整这里不给出可能过时的安装命令。你只需要知道使用 Codex 前需要先通过官方渠道完成账号登录或开发者授权。这个环节通常在安装后的第一次启动时完成按终端提示打开授权链接即可。如果在这个过程中遇到网络访问或连接问题优先检查你所在环境的网络策略再查看 Codex 自身日志。要注意的是任何与访问边界有关的问题都建议使用合规网络环境解决。3.4 准备好依赖安装条件本项目需要安装psutil、fastapi、uvicorn它们都会从 PyPI 下载。如果你所在服务器无法直接访问公网包源需要先配置可用的镜像源或离线包否则 Codex 运行到“安装依赖”这一步时会卡住。这一步看起来基础但它是 AI 开发项目中经常出问题的位置——AI 写代码通常很流畅真正卡住它的往往是环境网络问题。4. 需求拆解让 AI 读得懂你的目标很多人第一次让 Codex 干活时会直接说“帮我做一个服务器监控面板。”这个说法太模糊AI 大概率会按照自己的想象自由发挥。它可能生成一个你不需要的数据库可能做出一个极其复杂的仪表盘也可能漏掉“实时刷新”这个关键点。更好的做法是像和一个新同事沟通需求一样把项目边界写清楚然后让 AI 先读文档再开始编码。4.1 把监控面板拆成四个部分一个轻量监控面板从职责上可以拆成四层采集层定时读取 CPU 使用率、内存使用率、磁盘使用率最好还能带上系统平均负载。服务层提供一个 HTTP 接口把采集结果以 JSON 格式输出。展示层网页每 2 秒拉取一次接口数据并把最近几十个点绘制成趋势图。运行与安全服务能稳定启动端口可配置避免以 root 直接运行默认不对外开放危险端口。这四层不需要全部由人手工编码但开发者必须能在心里把它们拆开。拆得越清楚发给 AI 的指令就越精确。4.2 写一份最小需求文档在项目目录下创建一个INSTRUCTIONS.md内容如下# 服务器监控面板需求 ## 目标 用 Python 构建一个轻量服务器监控面板通过浏览器查看当前服务器的 CPU 使用率、内存使用率和磁盘使用率。 ## 功能要求 1. 后端使用 FastAPI。 2. 使用 psutil 采集指标。 3. 提供一个 GET /api/stats 接口返回 JSON 格式 - cpu_percentCPU 使用率百分比 - memory_percent内存使用率百分比 - disk_percent根分区磁盘使用率百分比 - timestamp采集时间戳 4. 前端页面使用原生 HTML JavaScript Chart.js不要引入构建工具。 5. 页面每 2 秒请求一次 /api/stats并展示最近 60 个点的折线图。 6. 第一版不需要登录认证不需要数据库不需要 Redis。 ## 验收标准 - 启动后访问 http://127.0.0.1:8787 可以看到页面。 - 页面上的 CPU、内存、磁盘数字随刷新间隔变化。 - 打开浏览器控制台不会出现接口 404 或 CORS 错误。这份文档的作用不仅是给 Codex 看更是给自己做需求评审。你会发现里面明确了几个约束“第一版不需要登录认证”可以防止 AI 擅自引入无关复杂度。“不使用构建工具”可以避免生成一个需要 npm 安装的工程。“验收标准”让 AI 知道自己什么时候算完成任务。很多人会低估需求文档的价值。实际上Codex 类工具对模糊需求的第一反应通常是“选择一个它认为最合适的默认方案”而默认方案未必符合你的真实场景。你写下的约束越多它的自由度就越小最终结果越可控。5. Codex 实战从提示词到第一个可运行版本需求文档准备好以后就可以让 Codex 开始干活了。这里给出一个可复用的对话流程而不是一个神奇的“万能提示词”。5.1 第一步让 Codex 先出方案不要直接写代码在 Codex 中打开项目目录后如果它支持读取文件先让它阅读需求文档请阅读当前目录下的 INSTRUCTIONS.md。 先不要写代码。先给出你的实现方案包括 1. 你会创建哪些文件每个文件负责什么。 2. 后端接口的返回格式。 3. 前端页面如何实现自动刷新。 确认方案后再开始实现。这一步非常重要。直接让 AI 写代码它可能会在错误的技术选型上一路狂奔。先让方案浮出水面你才能低成本纠正方向。比如 AI 如果提出要引入 WebSocket 做实时推送你可以果断拒绝。因为对于“每 2 秒拉取一次”这个需求传统 HTTP 轮询已经足够WebSocket 是过度设计。如果 AI 提出要为历史数据建 SQLite也可以在第一版拒绝因为内存保留最近 60 个点完全能满足演示目标。5.2 第二步确认方案让 AI 进入编码循环方案确认后可以继续要求它实现方案没问题开始实现。 请严格按 INSTRUCTIONS.md 中的文件规划创建文件。 创建 requirements.txt并在需要时安装依赖。 代码实现完成后启动服务并用 curl 验证 /api/stats 返回 JSON。 如果运行报错请读取终端日志并修复直到接口返回 200。Codex 收到这个指令后会发生几件可能让你意外的事。它会自己创建collector.py、server.py、static/index.html运行pip install安装依赖启动 Uvicorn然后请求接口检查返回。如果接口报错它会回头修改代码再试。这个“自主迭代”的过程就是它和普通代码助手最大的不同。过去你需要手动复制代码、运行、复制报错现在 AI 把这些环节串成了闭环。但要注意AI 执行命令也有风险。它可能会在不该执行的地方执行破坏性命令也可能在正式环境安装一些有依赖冲突的包。所以在让它执行命令前一定要确认工作目录正确并且当前环境允许实验。5.3 第三步当执行过程卡住时给它补充上下文Codex 并非总是畅通无阻。最常见的卡点是环境问题而不是代码逻辑问题。例如pip install因为网络原因失败。依赖安装后 Python 版本不兼容。8787 端口被占用。前端页面能打开但接口请求跨域被拦截。遇到这类问题时Codex 自己往往能从终端日志中找到原因但如果它反复失败你需要给它补充约束。比如pip 安装失败请先查看错误日志判断是网络问题还是 Python 版本问题。 如果是网络问题请检查是否配置了当前环境可用的 pip 镜像源。 不要使用 sudo不要修改系统级 Python 环境。这里体现的其实是工程判断力。AI 编程工具可以处理“已知错误”但它不知道你的公司内部网络有哪些可用镜像源也不知道你所在环境的安全规范。这些信息只能由人补齐。与其说你在“指挥 AI 写代码”不如说你在“管理一个执行效率很高但缺少领域经验的新人”。6. 项目落地后的核心代码实现Codex 会生成一份可运行的代码但开发者仍然需要看懂核心实现才能在它出错时接管。这一节给出监控面板项目的核心代码也方便你手动对照审查 AI 的产出。如果 Codex 生成的文件名和职责划分合理最终目录大概会是这个样子server-monitor/ ├── INSTRUCTIONS.md ├── requirements.txt ├── collector.py ├── server.py └── static/ └── index.html6.1 采集模块collector.py数据采集是监控面板的地基。psutil是 Python 生态里读取系统指标的常用库cover 了 CPU、内存、磁盘、进程等能力。# collector.py import time import psutil def collect_metrics(): cpu_percent psutil.cpu_percent(interval1) memory psutil.virtual_memory() disk psutil.disk_usage(/) load_avg psutil.getloadavg() if hasattr(psutil, getloadavg) else (0, 0, 0) return { cpu_percent: cpu_percent, memory_percent: memory.percent, disk_percent: disk.percent, load_avg: [round(x, 2) for x in load_avg], timestamp: int(time.time() * 1000), }关键点在于psutil.cpu_percent(interval1)。这个调用会阻塞 1 秒让内核在这段时间内完成一次真实采样。如果改成intervalNone第一次调用通常返回 0后续调用才返回相对上次采样的值容易让首屏数据看起来不真实。磁盘使用率这里取的是根分区/。如果你的服务器把数据盘挂在/data代码里可以增加一个可配置的磁盘路径列表而不是硬编码。实际项目中磁盘监控通常需要关注多个挂载点这正是一个可以交给 AI 扩展的后续需求。6.2 后端模块server.py后端用 FastAPI 暴露一个简单 JSON 接口并把static/index.html作为静态页面托管。# server.py from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from collector import collect_metrics app FastAPI() app.get(/api/stats) def stats(): return collect_metrics() app.mount(/, StaticFiles(directorystatic, htmlTrue), namestatic)这段代码很短但包含一个容易理解错的设计静态文件挂载在根路径/同时接口路径/api/stats单独定义。FastAPI 会先匹配/api/stats路由再匹配静态目录所以接口和页面不会冲突。如果 Codex 给出的实现是用app.get(/)返回index.html也没有问题只是托管方式不同。这里没有把历史数据保存在后端。前端请求一次后端就采集一次并返回当前快照。对“页面展示最近 60 个点”这个需求来说数据保存在浏览器内存里更简单页面打开后才开始积累数据刷新页面会清空重来。如果你希望服务器端保留最近 N 分钟的数据那就要考虑加 SQLite 或列表缓存这也是一个自然的扩展点。6.3 前端页面static/index.html前端页面是“一目了然”的最终出口。完整代码可以交给 Codex 生成但核心逻辑值得手写验证。下面给出一个可直接复制的版本它使用 Chart.js 画三条趋势线并通过fetch每 2 秒拉取一次接口。!DOCTYPE html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / title服务器监控面板/title script srchttps://cdn.jsdelivr.net/npm/chart.js/dist/chart.umd.min.js/script style * { box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Microsoft YaHei, sans-serif; background: #f6f7fb; margin: 0; padding: 24px; color: #24292f; } h1 { font-size: 20px; } .cards { display: flex; gap: 16px; flex-wrap: wrap; margin: 16px 0; } .card { background: #fff; border-radius: 10px; padding: 16px 20px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06); min-width: 150px; } .card .label { font-size: 13px; color: #57606a; } .card .value { font-size: 28px; font-weight: 600; margin-top: 6px; } .chart-wrapper { background: #fff; border-radius: 10px; padding: 16px; margin-bottom: 16px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06); } canvas { max-height: 260px; } .footer { color: #57606a; font-size: 12px; margin-top: 8px; } /style /head body h1服务器监控面板/h1 div classcards div classcard div classlabelCPU 使用率/div div classvalue idcpu--/div /div div classcard div classlabel内存使用率/div div classvalue idmem--/div /div div classcard div classlabel磁盘使用率/div div classvalue iddisk--/div /div /div div classchart-wrapper canvas idtrendChart