作者:Ben Tossell
如果你不具备深厚的编程背景,但希望利用当今的 AI 编程智能体进行开发,那么本文所涵盖的基础知识将是你不可或缺的基石。
本地与远程
首先,你必须搞清楚“本地”和“远程”到底是什么意思。
简单来说,本地意味着文件就在你的电脑上;而远程则意味着它存储在云端。
举个例子:如果你在手机上编辑一份没有联网的 Google 文档,那么你就是在该设备(这台机器)上进行本地操作,文档仅存在于手机中。一旦你连接网络并将其保存到 Google Drive,它就拥有了一个远程目标。随后,你就可以在电脑上访问并继续编辑之前在手机上处理的那份文档了。
这就是本地与远程的区别:是仅存在于你的电脑中,还是存储在云端。
终端(Terminals)
在使用编程智能体时,你可能会经常用到“终端”。
终端本质上就是一个应用程序,它可以直接在你的电脑上运行各种指令。棘手的地方在于,通常你需要知道确切的命令才能让它工作,比如列出文件、编辑、移动或创建文件等。
但在编程智能体的帮助下,你不再需要死记硬背这些命令了。你可能唯一需要了解的是cd命令——它的意思是“切换目录”(Change Directory)。例如,输入cd /Users/bentossell/project/bensbites/,就可以在特定的文件夹中启动你的智能体。
目录与路径
计算机有一套自己的语言,初看可能觉得晦涩,但接触多了你就会发现其中的规律。
目录其实就是文件夹。所以当你听到“创建一个目录”时,其实就是“创建一个文件夹”,没那么神秘。
当你寻找特定的文件或文件夹时,它会有一个类似于网址的“目的地”。就像网站的 URL:这个完整的网址就是URL 路径。
在你的文件系统中,逻辑是一样的。路径就是通往你文件的“路线图”。
例如,文件可能在你的“主目录”下,路径可能是/Users/你的用户名(正如我在cd示例中展示的那样)。那么~/projects/bensbites/就是通往该文件夹或文件的路径。
其中~/(波浪号)是主目录的缩写(替代了长长的/Users/你的用户名/)。如果你在主目录下,只需输入~/projects/bensbites/,系统就能准确找到位置。
什么是编程智能体
现在当你听到“编程智能体”这个词时,可以把它想象成在和 ChatGPT 或 Claude 聊天。它是一个大语言模型(LLM),一个能与你对话的 AI 系统。但不同的是,它还拥有访问你电脑工具的权限,正如我们上文提到的那些。
顺便提一下:我曾在我的上发布过相关内容,在那里我分享了我的实践心得,并对一些难以记忆的术语和工具做了详细解释。
系统提示词
编程智能体都自带“系统提示词”。例如 Claude Code 有自己的系统提示词,、Codex 等也各有一套。有些公司会公开这些提示词供人查阅,有些则不会。不过,webo 上常有一些大神通过“套话”技巧让系统暴露自己的系统提示词,你总能找到它们。
本质上,这些提示词会告诉 AI:“你是一名资深的软件工程师。这是你可以做的事情,这是你不该做的事情。你是哪家公司开发的。你的知识截止日期是某天,但现在的日期是某天。”(因为 LLM 的知识库是有时效性的)。
编程智能体可以使用网页搜索工具来获取最新信息。它们还可能包含对自己文档的引用。所以如果你问:“我该如何在智能体中配置这个功能?”它会查阅自己的文档来引导你(甚至直接帮你完成)。
Markdown 文件与 agents.md
另一个经常被提及的概念是Markdown 文件(后缀为.md)。
Markdown 是一种纯文本文件格式,但支持简单的格式化(如标题、列表等)。如果你用过 PowerPoint,你知道导出文件是.pptx;在 Mac 上可能是.keynote;Excel 是.xlsx,而 Google Sheets 可能是.csv。
这些 Markdown 文件允许你在系统提示词之外,给智能体提供自定义指令。大多数智能体会寻找一个名为的文件(Claude 则寻找)。
在这个文件中,你可以写下这样的提示:“我完全不懂技术,所以每当你谈论代码时,请务必详细解释它的作用,”或者“请用五岁小孩都能听懂的方式向我解释。”你会发现,有无这条指令,智能体输出的内容会有天壤之别。
开发者通常会在这些文件中放入很多信息,比如:“这是我的技术栈。这些是需要重点关注的文件。这些是运行该项目所需的命令。”这些指令是层级化的,离你当前工作文件最近的 Markdown 文件会被优先读取。
my-project/
├── AGENTS.md ← 根目录指令(最后读取)
├── src/
│ ├── AGENTS.md ← 源代码特定规则(第二顺位读取)
│ └── components/
│ ├── AGENTS.md ← 组件规则(最优先读取!)
│ └── Button.tsx
当你编辑Button.tsx时,智能体会先读取components/目录下的,然后逐级向上直到根目录。
技能(Skills)
现在还出现了一个叫“技能”的概念。
你可以把技能看作是“可重复使用的自动化工作流”,即那些你会反复执行的操作。
技能拥有独立的指令文件。如果你同时处理多个项目,并希望以特定格式工作(比如处理新闻通讯内容),你可以在/project/newsletter/下创建一个专门用于撰写标题的技能。你只需提供内容并说“根据这个写个标题”,而在你的中,已经预设了指令:“严禁使用表情符号,全部使用小写,字数不超过四个词,并提取最重要的主题”等等。
技能也可以是指令与工具的结合。比如,同样的指令配上一个能根据内容生成图片的工具。
对于实际的代码项目,你可能会有一个专门负责“前端设计”(即用户看到的界面)的技能。
我经常使用一个名为agent-browser的技能,它包含如何操作浏览器的指令(让智能体能够查看 Chrome、点击、滚动、阅读和输入等)。
前置元数据(Frontmatter)与渐进式披露(Progressive Disclosure)
实际上,“技能”与之间的区别非常微小。主要的区别在于:技能是智能体根据需要随时调用的,而不是始终加载在系统提示词中。这里有两个值得一提的术语:前置元数据(Frontmatter)和渐进式披露(Progressive Disclosure)。
Frontmatter本质上就是元数据。就像你写博客时会设置标题和标签,这些 SEO 数据是为了让搜索引擎能够精准匹配。当用户搜索相关词汇时,系统会匹配你的标签和描述。比如你在谈论 AI,你就会在元数据中加入“AI”这个词。
在这些文档中,Frontmatter 的作用也是如此。你只需简单标注:name: Newsletter-content-writer description: 当被要求编写任何新闻通讯内容时,请使用此技能。
智能体会自动预填这些标题和描述。它会这样思考:“我已经读取了系统提示词,这是我的基础认知;接着我读取了文件,了解了用户的特定指令;我还看到有十个可用的技能。现在我知道,如果用户问起新闻通讯,我会主动调用这些技能。”
这就是渐进式披露:智能体根据需求自行加载相关内容。你不需要每次都明确指令“使用这个技能来做这件事”,尽管我个人为了确保万无一失,还是经常会手动指定。
这一切是如何关联的
你所做的一切,本质上都是在为智能体提供额外上下文(Context)。我们稍后会详细讨论上下文。这些额外的上下文、指令和能力,能让智能体在特定项目或所有项目中更好地为你服务。
你可以为每个项目设置特定的指令和技能,也可以设置通用的全局配置。
点文件(Dot Files)与技能的存放位置
你的技能存放在哪里?
在文件系统中,有些文件默认是隐藏的。在 Mac 上,你可以通过按下Command + Shift + .来查看它们。这些文件被称为点文件,因为它们以点开头,例如.factory、.claude、.codex等。
这些文件夹里存储着你的配置信息、会话记录、设置以及技能。
通常路径是~/.factory/skills/。
那里通常也会有一个文件。你应该确保你的核心指令都存放在这个位置。
命令(Commands)
自定义斜杠命令是为了快速运行某个工作流。例如,你可以设置一个/newsletter命令,它会自动从“Transcripts”文件夹提取转录文本,使用特定技能进行格式化,并完成重写。
智能体可以帮你设置这些命令,你也可以在~/.factory/commands/中手动创建。
Bash
简单聊聊工具。
当你开始与智能体合作时,你会看到电脑屏幕上飞快地跳出各种命令。如果你是一名整天泡在终端里的资深技术人员,这正是你会手动执行的操作。
这里最核心的工具叫Bash。智能体非常喜欢它,因为通过 Bash,它们可以大显身手:运行命令、查阅资料、移动文件,甚至将一系列操作串联起来。
上下文窗口(Context Windows)与 Token
AI 模型是有“内存限制”的,这被称为上下文窗口。它通常用Token来衡量。一个 Token 大约相当于 4 个字符或 3/4 个单词。如果一个模型的上下文窗口是 100k,意味着它一次能记住大约 10 万个 Token。
智能体只能“看到”窗口范围内的内容。如果你有一个包含数百个文件的庞大代码库,它无法一次性全部加载。它必须表现得足够聪明:读取需要的文件,完成工作,有时为了腾出空间还会“忘记”一些旧信息。
无论是工具自带的庞大系统提示词,还是你提供的超长,都会占用这个空间。你塞进去的东西越多,智能体在关注重点信息时的表现就可能越差。(不过,Factory 的 Droid 在“上下文压缩”方面做得非常出色——压缩本质上就是智能体对上下文进行总结,尽量保留核心要点)。
这就是为什么我们要保持指令的精简和聚焦。这也是为什么技能被设计成独立文件,仅在需要时加载。你本质上是在管理智能体的“注意力”。
如果你发现智能体在长对话中忘记了你之前说过的话,通常就是因为那些信息超出了上下文窗口。这时你可能需要提醒它,或者开启一个新的会话。
现在,让我们聊聊代码项目。
Git 与版本控制
如果你打算用编程智能体做点实事,Git可能是你最需要理解的概念。Git 就是版本控制——它不仅能保存你的工作,还能让你随时回退到任何历史版本。
当你在项目中做出更改时,你会进行“提交”(Commit)。一个 Commit 就像是一个快照。它在说:“好了,这是项目现在的样子,我很满意,我要存个档。”你会写一条简短的消息,比如“添加了新的页眉”或“修复了那个 Bug”,这样未来的你就能知道这次改动做了什么。
分支(Branching)
你可能会有一个main分支,那是项目的正式版本、线上版本。然后你会创建一个新分支来尝试新想法。比如,“我想尝试这个新设计”,你就分出一个支线,在上面折腾。如果效果不错,你就把它“合并”(Merge)回main分支;如果搞砸了,直接删掉那个分支,主程序不会受到任何影响。
这里又涉及到了“本地”与“远程”。
当你在电脑上操作时,你的提交是本地的,仅存在于你的机器上。当你执行“推送”(Push)时,你是在将这些提交发送到远程(通常是 GitHub 这种云端平台)。而当你执行“拉取”(Pull)时,你是在下载别人推送的改动,或者是你在另一台机器上推送的改动。简单来说,Push 就是上传,Pull 就是下载。
一个Repo(仓库,Repository 的缩写)就是一个配置了 Git 的项目文件夹。它内部有一个隐藏的.git文件夹,记录了所有的提交和分支。所以当有人说“克隆(Clone)这个仓库”时,意思就是“从 GitHub 下载这个项目到你的电脑,以便你在本地开展工作”。
编程智能体会为你处理这一切:提交、推送、创建分支。
理解这些很有帮助,因为智能体可能会问你:“需要我提交这些更改吗?”到时你就知道它在说什么了。而且,当(注意是“当”而不是“如果”)事情搞砸时,Git 就是你的“后悔药”。
环境变量与 API 密钥
这些本质上是代码运行所需的“秘密”,但你绝不想直接写在代码里。比如你连接 Stripe 或 OpenAI 时,它们会给你一个 API 密钥(相当于一个独特的密码)。你不能把这个密码直接写在代码里,因为一旦你推送到 GitHub,全世界都能看到你的密码了。那将是一场灾难。
正确的做法是创建一个名为.env的文件,把秘密存放在里面。比如:OPENAI_API_KEY=sk-xxxxxx。然后你的代码会从这个文件中读取密钥,而不是直接读取硬编码的字符串。.env文件只留在你的本地机器上,永远不会被推送。通常还会有一个.gitignore文件明确告诉 Git:“嘿,千万别上传.env文件。”
环境变量不仅用于 API 密钥。你可能在开发环境和生产环境(线上环境)有不同的设置。比如,本地数据库的地址和线上的肯定不一样。所以你会为不同的环境准备不同的.env配置。
当你设置新项目并被要求“将 API 密钥添加到 .env”时,指的就是这个操作。编程智能体可以帮你完成设置,但它会向你索要真实的密钥,因为它显然不知道你的密码。
安全提示:最好直接打开文件添加 API 密钥,而不是直接在对话框里发给智能体(虽然我也经常偷懒直接发给它)。
依赖与包管理器
在构建项目时,你不需要从零开始写每一行代码。你会使用别人已经写好的代码,这些被称为依赖、包或库(这些词通常可以互换使用)。如果你想连接数据库,有很多现成的包;如果你想发邮件,也有现成的包。你只需要安装并使用它们即可。
管理这些包的工具叫做包管理器。如果你使用 JavaScript(大多数网页开发所用的语言),你会用到npm(Node Package Manager)。现在还有一个叫Bun的新工具,它更快、更流行,功能一样但效率更高。如果你用 Python,你会用到pip。虽然还有其他工具,但这两个是你最常遇到的。
当你克隆一个仓库并想运行它时,智能体做的第一件事通常是执行npm install或pip install。
它会查看项目中的配置文件(JavaScript 是package.json,Python 是requirements.txt),然后下载项目所需的所有包。在 JavaScript 项目中,这些包会存放在一个叫node_modules的文件夹里。你会发现这个文件夹巨大无比,包含成千上万个文件。别担心,这很正常,别去动它,它只是项目运行所需的各种零件。
编程智能体通常会自动处理这些。当它需要新功能时,它会自动安装相应的包。
在本地运行项目
当你配置好项目并安装了依赖后,你需要让它真正跑起来。这时你会遇到localhost。Localhost 就是你的电脑在“假装”成一台 Web 服务器。智能体会启动服务器,你访问 localhost 就能看到项目在你的机器上运行。
你会看到类似localhost:3000或localhost:8080的地址。后面的数字是端口(Port)。你可以把它想象成不同的频道。你可以同时在 3000 端口运行一个项目,在 3001 端口运行另一个,它们互不干扰。
运行项目通常使用npm run dev或npm start等命令,具体取决于项目的设置。通常README文件会说明,或者智能体能自己摸索出来。运行后,终端会显示一个 URL,比如“ready on localhost:3000”,你只需在浏览器中打开它即可。
此时项目正在运行,但仅限你的本地机器。别人看不到,你也无法分享这个链接。这是为了让你在部署到互联网供全球访问之前,进行开发、测试和调试。
当你在开发服务器运行时修改代码,大多数情况下页面会自动刷新,无需重启服务器。这被称为热重载(Hot Reloading)。
部署
你在本地构建好了东西,运行完美。现在你想把它发布到互联网上,让别人也能看到。你需要进行部署。
目前有几个非常流行的平台:Vercel是 React 或 Next.js 项目的首选;Netlify也很棒,非常适合静态网站和简单应用;Cloudflare的 Pages 和 Workers 则适合追求极致访问速度的项目。
这些平台都有自己的命令行工具(CLI),方便智能体帮你完成部署。你可以说“把这个部署到 Vercel”,智能体就会运行 Vercel CLI,上传代码,并给你一个可以公开访问的网址。
这些工具还支持MCP(模型上下文协议),让智能体能更直接地与这些服务交互(类似于 CLI)。它可以检查部署状态、查看日志、管理环境变量等。
关于部署,唯一需要记住的是环境变量。你的.env文件不会被部署(这是为了安全)。所以,你的应用所需的任何秘密信息,都必须手动添加到部署平台的后台管理面板中。如果你的应用在本地运行正常,但部署后报错,请首先检查环境变量配置。
阅读错误信息
当事情搞砸时(这是必然的),终端会显示错误信息。通常是一大堆红色的文字,看起来很吓人。但实际上,它是在试图帮助你。
寻找关键信息的秘诀是:看那堆红字的最顶部或最底部。它通常会说“找不到模块(cannot find module)”、“未定义不是函数(undefined is not a function)”或“连接被拒绝(connection refused)”。
接着通常会有一个堆栈追踪(Stack Trace),即一连串的文件路径和行号。它在告诉你错误发生的具体位置。比如,“问题出在这个文件的第 47 行”。这样你就能定位并查看那行代码。
好消息是,你不需要完全理解这些。你只需复制错误信息,发给编程智能体,然后说“修复这个”。智能体非常擅长处理错误,因为它见过无数类似的案例。它会说:“噢,这意味着你忘了安装那个依赖,”或者“这里有个拼写错误。”然后它会帮你修好。
久而久之,你会认出一些常见的错误。比如 “ENOENT” 意思就是文件找不到;“ECONNREFUSED” 意味着无法连接,可能是你的数据库没启动。你会慢慢掌握这些规律。
Chrome 开发者工具与控制台错误
另一个非常有用的错误来源是浏览器的开发者工具,特别是Chrome DevTools。如果你的网站按钮没反应或数据加载不出来,答案通常就在“控制台(Console)”里。
在页面任何地方右键点击“检查(Inspect)”,或者按下Command + Option + I(Mac) /Ctrl + Shift + I(Windows) 即可打开。点击Console选项卡,你会看到 JavaScript 抛出的错误、失败的网络请求等各种有用信息。
你需要关注那些红色的文字。它可能会说 “Failed to fetch” 或 “Uncaught TypeError”。这就是线索。复制它,发给智能体,并说明:“当我点击提交按钮时,控制台出现了这个错误。”这比只说“它坏了”要有用得多。
还有一个Network选项卡,显示了页面发出的所有请求。如果你尝试从 API 加载数据失败,你可以在这里看到请求是否发出、发送了什么以及服务器返回了什么。这对于调试非常关键。你可以截图或复制错误详情给智能体。
这也是agent-browser的用武之地——智能体可以亲自查看网站,检查控制台,读取错误信息,并将其带回上下文进行修复。
错误、Bug 修复与测试
你会不断遇到错误和 Bug,专业的开发者也是如此。
初学者最容易犯的错误是:东西坏了,直接喊“修好它”,智能体试了一下没修好,你喊“还是坏的”,它又试了一下还是不行,结果你们就在原地打转。这样会浪费大量时间(我也经历过)。
在说“修好它”之前,先给智能体提供上下文:你当时想做什么?你预期的结果是什么?实际发生了什么?并贴出错误信息。
你应该要求智能体在修复之前先进行调查。比如:“先别急着修。首先告诉我你认为哪里出了问题,以及为什么。”因为如果它不理解问题根源就盲目改动,可能只是治标不治本,下周还会出现同样的 Bug。
测试(Tests)在这里起到了巨大作用。测试就是一段检查其他代码是否正常运行的代码。你可以写一个测试:“当我点击这个按钮时,应该发生某件事。”运行测试可以确保你的改动没有破坏原有功能。智能体可以为你(或为它自己)编写测试。
一个好的工作流是:出现 Bug -> 要求智能体先写一个能重现该 Bug 的测试 -> 得到一个失败的测试 -> 智能体修复 Bug -> 测试通过。这样你就确信 Bug 真的被修好了,而且如果以后这个 Bug 再次出现,测试会立刻发现它。这被称为测试驱动开发(TDD)。
此外,日志(Logs)也很重要。你可以要求智能体添加日志输出,以便观察运行状态。比如打印“运行到这一步了”或“这个变量的值是某某”。这能帮你和智能体快速定位问题。
现在的顶尖智能体都有专门的工具来处理这些:它们会运行代码、查看错误、检查变量,并监控每一步的执行情况。
在你陷入死循环之前
我提到过不要在修复问题时原地打转,但每个人都会遇到这种情况。以下是我用来提醒自己的一些建议:
提交你的工作:在开始排查 Bug 之前,确保当前进度已保存。执行 Git commit。这样即使你把事情搞得更糟,也能随时回退。
考虑开启新会话:有时候智能体在某个功能上钻了牛角尖,之前的失败尝试让它的上下文变得混乱。
检查基础配置:.env文件真的在吗?密钥对吗?你是不是只在本地设置了环境变量,却忘了在 Vercel 或 GitHub Secrets 等发布平台上添加它们?
更新你的上下文:检查你的、技能或任何你给出的指令。如果你反复遇到同一类问题,说明智能体需要更好的引导。
如果智能体老是尝试使用一个无效的工具,或者格式化老是出错,或者老是忘记项目的特殊配置,这就是在提醒你:把这些信息加入。“我们使用 pnpm 而不是 npm”、“测试前务必运行数据库迁移”、“API 端点是这个,不是那个”。
非技术人员可能不知道该加什么。秘诀是:当智能体最终解决问题时,问它:“刚才的问题出在哪?我应该在指令里补充什么,才能避免下次再犯?”它会诊断问题并给出建议。
智能体循环(The Agent Loop)
智能体本质上是一个循环:计划 -> 行动 -> 观察 -> 重复。
你给它一个任务,比如“帮我建一个落地页”。首先,它会计划:我需要创建哪些文件,安装哪些依赖,构建什么样的结构。接着,它开始行动:创建文件、写代码、运行命令。然后,它会观察:结果如何?报错了吗?页面能加载吗?根据观察结果,它要么继续下一步,要么回头修复错误。
这个循环会一直持续,直到任务完成或它彻底卡住。当它卡住时,它会向你求助:“我试了这个但没成功,我该怎么办?”你提供更多上下文或新方向后,它会继续这个循环。
自主模式
不同的智能体有不同的自主程度。有些在做任何事之前都会征求你的意见:“我准备创建这个文件,可以吗?”你必须批准每一个动作。这很安全,但很慢。
另一些则会直接修改、创建、运行,完全不打招呼。这快得多,但你需要信任它。有时候它会跑偏,你需要把它拉回来。
大多数智能体允许你进行配置。比如:“删除文件前问我,但创建新文件可以直接操作。”或者你可以设置“探索模式”(多问)和“执行模式”(直接干活)。
我喜欢让它自己跑,我在旁边看着。如果它开始做一些奇怪的事,我会介入。但我不会去批准每一个细小的动作。随着使用经验的增加,你会对更高的自主权感到越来越放心。
以上就是所有的基础知识。你不需要死记硬背。开始动手构建吧,让智能体引导你,你会在实践中自然而然地掌握它们。最好的学习方式就是“边做边学”。
还有什么我漏掉的吗?