标签
Josh W. Comeau 报告称,他的开发者课程销量大幅下滑,并将这一下降归因于 AI 带来的担忧:人们对开发者工作的未来感到不确定,转而使用大型语言模型获取免费个性化辅导,而非购买付费课程。
探讨当AI能够编写和优化代码时,学习算法是否仍然重要,以及在AI编程助手时代,算法理解所扮演的角色。
# 终端背后的秘密:终端模拟器、TTY 与 Shell 作为开发者,我们每天都在使用终端。但你有没有想过,当你打开一个"终端窗口"时,究竟发生了什么?你输入的字符经过怎样的路径,最终变成命令的输出? 大多数人将整个体验笼统地称为"终端",但实际上,这背后存在三个截然不同的层次,它们各司其职、协同工作。理解这三个层次,不仅能帮助你排查奇怪的问题,更能让你对自己每天使用的工具有更深刻的认识。 ## 三个层次概览 在深入细节之前,先来认识这三位主角: 1. **终端模拟器(Terminal Emulator)**:你在屏幕上看到的那个窗口 2. **TTY / 伪终端(Pseudo-terminal)**:操作系统内核中的一个抽象层 3. **Shell**:真正解释并执行你命令的程序 它们的关系可以这样理解: ``` 你的键盘输入 ↓ 终端模拟器(GUI 窗口) ↓ 伪终端(内核层,PTY) ↓ Shell(bash / zsh / fish …) ↓ 命令输出原路返回 ``` --- ## 第一层:终端模拟器 ### 它是什么 终端模拟器是一个**图形界面程序**。常见的有 iTerm2、GNOME Terminal、Alacritty、Windows Terminal、Kitty 等。它的核心职责是: - 渲染文字到屏幕上 - 捕获你的键盘输入 - **模拟**老式硬件终端(如 VT100、xterm)的行为 "模拟器"这个词至关重要。在个人电脑普及之前,终端是一台真实的硬件设备——一台带显示器和键盘的哑终端,通过串口连接到大型主机。现代的终端模拟器用软件重现了那台硬件的行为。 ### 它做什么 当你按下键盘上的一个键,终端模拟器会将这个按键**转换成字节序列**,然后写入伪终端。例如,方向键 `↑` 通常被转换为转义序列 `\x1b[A`。 反过来,当程序向终端写入转义序列时,终端模拟器负责**解释**这些序列,并作出相应动作,例如: ``` \x1b[31m → 将后续文字渲染为红色 \x1b[2J → 清空整个屏幕 \x1b[1A → 光标上移一行 ``` 这就是为什么同一个程序(比如 `vim`)在不同的终端模拟器里,行为可能略有差异——不同的模拟器对转义序列的支持程度不同。 ### 一个常见的误解 很多人以为终端模拟器"运行"了 Shell。实际上,终端模拟器只是**启动**了 Shell,并为它提供一个可以读写的伪终端接口。之后,终端模拟器和 Shell 是两个独立运行的进程,通过内核中的伪终端相互通信。 --- ## 第二层:TTY 与伪终端(PTY) 这是三层中最容易被忽视、也最难理解的一层,但它是整个系统的**核心枢纽**。 ### TTY 的历史渊源 TTY 是 **Teletype**(电传打字机)的缩写。在计算机的早期历史中,人们用电传打字机与计算机交互——输入字符,打印机打印出响应。这套设备通过串口连接到计算机。 Unix 操作系统从一开始就将这些串口设备抽象为文件,放在 `/dev/tty*` 路径下。程序只需要读写这些文件,就能与用户交互,而不需要关心底层是什么硬件。 ### 伪终端(PTY)的出现 当我们转向图形界面,不再有真实的硬件串口时,Unix 需要一种方式来保持这套抽象,同时支持终端模拟器这样的软件。于是**伪终端(Pseudo-Terminal,PTY)**诞生了。 PTY 是内核提供的一对相互连接的虚拟设备: - **主端(master side)**:终端模拟器持有这一端 - **从端(slave side)**:Shell 及其子进程持有这一端,对应 `/dev/pts/0`、`/dev/pts/1` 这样的设备文件 你可以把它想象成一条**双向管道**,但这条管道远比普通管道聪明——它内置了一个叫做**行规程(line discipline)**的模块。 ### 行规程:被遗忘的功能 行规程是 TTY 层中最精妙的设计之一。它在内核中处理大量"低级"的终端行为,让每个 Shell 和应用程序不必自己重新实现这些功能: | 功能 | 说明 | |------|------| | **回显(Echo)** | 将你输入的字符显示在屏幕上 | | **行编辑** | `Backspace` 删除字符,`Ctrl+U` 清除整行 | | **信号生成** | `Ctrl+C` 发送 `SIGINT`,`Ctrl+Z` 发送 `SIGTSTP` | | **规范模式** | 缓冲整行输入,直到你按下回车 | 这解释了一个有趣的现象:即使在 Shell 还没启动、或 Shell 崩溃的情况下,`Backspace` 键依然"有效"——因为删除字符这个操作是由**内核**在 TTY 层处理的,而不是由 Shell 处理的。 ### 用命令亲眼验证 在终端里输入: ```bash tty ``` 你会看到类似这样的输出: ``` /dev/pts/3 ``` 这就是当前 Shell 正在使用的伪终端从端设备文件。你可以用 `ls -la /dev/pts/` 查看系统中所有活跃的伪终端。 更有趣的是,你可以直接向另一个终端窗口写入文字: ```bash # 在终端 A 中运行 tty,假设输出是 /dev/pts/3 # 然后在终端 B 中运行: echo "你好,终端 A" > /dev/pts/3 ``` 终端 A 的屏幕上会直接出现这段文字——这直观地展示了 TTY 设备文件的本质。 --- ## 第三层:Shell Shell 是你**最熟悉**却往往被与终端混为一谈的那一层。 ### Shell 是一个普通进程 Shell(bash、zsh、fish 等)本质上是一个**普通的用户空间进程**。它的特别之处在于: - 它将 `/dev/pts/N` 作为自己的**标准输入(stdin)**、**标准输出(stdout)**和**标准错误(stderr)** - 它读取你输入的文本,解析为命令,然后通过 `fork()` + `exec()` 创建子进程来执行这些命令 - 子进程同样继承了对 TTY 设备的连接 ### 原始模式 vs 规范模式 Shell 在启动后,通常会让 TTY 层工作在**规范模式(canonical mode)**下。此时行规程会帮 Shell 缓冲输入、处理退格键等,Shell 直接读取一整行已处理好的输入。 但当你运行 `vim` 或其他全屏程序时,情况就不同了。`vim` 会将 TTY 切换到**原始模式(raw mode)**: - 行规程的大部分处理被**绕过** - 每个按键立即传递给应用程序 - 应用程序自行决定如何处理每一个字节 这就是为什么在 `vim` 里,`Backspace` 的行为可以被完全自定义,而在普通 Shell 提示符下,`Backspace` 的行为是由内核保证的。 ### Shell 不是终端 这个区别在实践中非常重要。考虑以下场景: ```bash # 这会失败,因为 ssh 命令没有分配 TTY ssh user@host vim /etc/hosts # 这会成功,-t 参数强制分配一个伪终端 ssh -t user@host vim /etc/hosts ``` `vim` 需要一个真实的 TTY 才能工作(它需要将终端切换到原始模式)。当 `ssh` 不分配 TTY 时,远端的 `vim` 无法正常运行——因为它的标准输入只是一个普通管道,而不是一个 TTY 设备。 --- ## 三层如何协同工作:一次完整的按键之旅 让我们追踪一次按键——假设你在 Shell 提示符下输入字母 `l`,准备输入 `ls` 命令: ``` 1. 你按下键盘上的 "l" 键 2. 操作系统检测到按键事件 3. 终端模拟器收到按键事件, 将其编码为字节 0x6C(ASCII 'l'), 写入 PTY 主端 4. 内核 TTY 层(行规程)收到这个字节: - 将字节追加到输入缓冲区 - 因为开启了回显,将 'l' 写回 PTY 主端 5. 终端模拟器从 PTY 主端读取到回显的 'l', 将其渲染到屏幕上 (这就是你"看到"自己输入的原因) 6. Shell 此时还没有收到任何东西—— 它在等待一个完整的行(规范模式) 7. 你继续输入 "s",然后按下回车 8. 行规程收到回车,将完整的行 "ls\n" 送入 Shell 可读取的队列 9. Shell 的 read() 调用返回,得到 "ls\n" 10. Shell 解析命令,fork() 出子进程, exec() 执行 /bin/ls 11. ls 将输出写入其标准输出(同一个 PTY 从端) 12. 内核 TTY 层将输出传递到 PTY 主端 13. 终端模拟器读取输出,渲染到屏幕上 ``` 整个过程在毫秒之内完成,但涉及了用户空间和内核空间之间多次切换,以及三个独立组件的协作。 --- ## 为什么这些知识对开发者有用 理解这三个层次,能帮助你解释和解决很多实际问题: **1. 为什么有些程序检测到自己的输出被重定向后,行为会改变?** ```bash ls --color=auto # 输出彩色 ls --color=auto | cat # 输出变成黑白 ``` `ls` 通过检查标准输出是否是 TTY(使用 `isatty()` 系统调用)来决定是否输出颜色代码。管道不是 TTY,所以颜色被关闭。 **2. 为什么 `sudo` 有时会提示输入密码失败?** `sudo` 需要从 TTY 读取密码。如果你在一个没有 TTY 的环境中运行 `sudo`(例如某些 CI 环境),它就无法工作。 **3. 为什么 `screen` 和 `tmux` 能"保持"会话?** `screen` 和 `tmux` 本身就是终端模拟器(运行在终端里的终端模拟器)。它们创建自己的 PTY,Shell 连接到这个 PTY。当你断开 SSH 连接时,真正的终端模拟器消失了,但 `tmux` 创建的 PTY 和连接到它的 Shell 仍然存在于服务器上。 **4. 理解 `stty` 命令** `stty` 命令直接操作 TTY 层的设置: ```bash stty -echo # 关闭回显(输入密码时脚本里常用) stty echo # 重新开启回显 stty -a # 查看当前 TTY 的所有设置 ``` --- ## 总结 | 层次 | 代表 | 职责 | |------|------|------| | 终端模拟器 | iTerm2, GNOME Terminal, Alacritty | 图形渲染、转义序列解释、按键捕获 | | TTY / PTY | `/dev/pts/N`(内核模块) | 数据路由、行规程、信号生成 | | Shell | bash, zsh, fish | 命令解析、进程管理、脚本执行 | 这三层各自解决了不同的问题,通过清晰的接口相互协作。Unix 的设计哲学在这里体现得淋漓尽致:每个组件做好一件事,通过标准化的接口(文件描述符、设备文件)组合在一起,形成一个灵活而强大的整体。 下次当你打开终端窗口,看到那个闪烁的光标时,你知道自己看到的不只是一个"终端"——而是三个精心设计的软件层,在内核与用户空间之间默默协作的成果。
GitHub学生大礼包2026年升级,总价值超$3500,免费提供GitHub Pro、Copilot Pro、Cursor Pro、JetBrains全家桶等50+工具,13岁以上学生零门槛即可领取。
哈佛大学CS50团队发布了一门65分钟的Git和GitHub大师课,强调版本控制是现代开发者的必备技能,并针对合并冲突等常见问题进行讲解。
《Learn Harness Engineering》是一个开源课程,系统整理了 OpenAI 和 Anthropic 提出的 AI 编程代理可靠性工程(Harness Engineering)概念,包含 12 节讲座和 6 个项目,旨在帮助开发者构建可靠的 AI 代理环境。
Anthropic工程师的一段短视频解释了如何构建高效的AI Agent,提供了通常开发者需要数月才能掌握的密集见解。
# AI 智能体术语不过是新瓶装旧酒 如果你是一位经验丰富的软件工程师,却对 AI 智能体(AI Agent)的世界感到困惑,原因很可能不是技术太复杂——而是行话太多。 欢迎了解**"词汇税"**:这是一种因新造术语而产生的认知负担,让你误以为自己面对的是全新的概念,而实际上不过是你已经熟悉的老朋友换了身行头。 --- ## 什么是词汇税 每隔几年,技术圈都会经历一轮术语洗牌。某个领域起飞了,新词汇随之涌现,旧有的工程概念被重新包装,贴上新标签。 这并不总是有意为之的炒作。有时候,新词汇确实能承载细微的差别,或者为特定社区提供更精准的表达。但很多时候,它制造的困惑远比带来的清晰要多。 词汇税的本质就是:**你为了弄懂这些词在说什么,而不得不付出额外的认知成本**。 AI 智能体领域目前正在大量征收这笔税。 --- ## 逐一拆解那些花哨术语 ### "Orchestrator"(编排器) 这个词让人联想到某种神秘的 AI 大脑,在幕后统筹全局。 实际上?它就是一个**控制流管理器**。它决定先调用哪个函数,根据结果走哪条分支,什么时候结束循环。你在写业务逻辑的第一天就做过这件事。 换个说法:`main()` 函数加上一些条件判断。 --- ### "Harness"(执行框架) AI 圈子喜欢说某个模型被"装进了一个 harness"。 这翻译过来就是:**一个包装类或运行时环境**,负责管理模型调用的生命周期——处理输入输出、捕获错误、维护状态。 换个说法:适配器模式(Adapter Pattern)加上一个 try/catch 块。 --- ### "Memory Layer"(记忆层) 这个词听起来像是给 AI 装上了某种类人的记忆系统。 实际上它就是**存储和检索机制**。短期记忆是会话上下文(session context),长期记忆是数据库查询,语义记忆是向量搜索。 换个说法:缓存 + 数据库 + 搜索索引。 --- ### "Tool Use"(工具调用) 模型"学会了使用工具",这句话读起来颇具魔幻色彩。 脱下这层外衣,它就是:**函数调用**。模型输出一个结构化的请求,系统解析它,执行对应的函数,把结果返回给模型。 换个说法:API 调用的调度与执行。 --- ### "Agentic Loop"(智能体循环) 这个术语让整个架构听起来像是某种自主意识的涌现。 它的本质是:**一个 while 循环**,每次迭代都会:获取当前状态 → 决定下一步行动 → 执行行动 → 更新状态 → 判断是否结束。 换个说法:事件循环(Event Loop),或者任何一个游戏引擎里的主循环。 --- ### "Grounding"(落地/锚定) "模型需要被 grounded"——这句话在 AI 文章里频繁出现。 它的意思是:**把模型的输出与可验证的外部数据绑定**,防止它胡说八道(即"幻觉")。RAG(检索增强生成)是最常见的实现方式。 换个说法:数据验证 + 外部数据源注入。 --- ### "Reflection"(反思) 听起来像是 AI 在进行哲学沉思。 实际操作是:**让模型评估自己的上一个输出**,判断是否满足要求,如果不满足则重新生成。这是一个带有评判步骤的迭代优化循环。 换个说法:带校验逻辑的重试机制(retry with validation)。 --- ### "Chain"(链) LangChain 里的"链",以及各种"prompt chain"。 这就是**函数组合(function composition)**,或者说是管道(pipeline)。输出 A 作为输入传给 B,B 的输出传给 C。 换个说法:Unix 管道。`cat file | grep keyword | sort | uniq` --- ## 那么,是不是什么都没变? 当然不是。有几件事确实是新的,或者至少是在规模和能力上发生了质变: 1. **不确定性变成了一等公民**:传统函数给定相同输入,输出是确定的。LLM 不是。这要求你在架构层面认真对待概率性行为,而不只是在边界情况里处理它。 2. **自然语言成为了接口**:当接口是自然语言时,你没办法写一个传统意义上完整的类型规范。这对系统边界的设计提出了新要求。 3. **上下文窗口是有限资源**:你需要像管理内存一样精心管理上下文,这是一种在普通 Web 开发里不太常见的约束。 4. **涌现行为(Emergent Behavior)确实存在**:模型组合起来之后,有时会产生你没有显式编程的行为。这既是能力,也是风险。 --- ## 如何用已有知识来理解 AI 智能体 这里有一个简单的映射框架,供有经验的工程师参考: | AI 智能体术语 | 等价的工程概念 | |---|---| | Orchestrator | 控制流 / 状态机 | | Memory Layer | 缓存 + 数据库 | | Tool | 可调用函数 / API | | Agentic Loop | 事件循环 / 主循环 | | RAG | 查询 + 上下文注入 | | Reflection | 带校验的重试 | | Chain / Pipeline | 函数组合 / Unix 管道 | | Prompt Template | 带参数的字符串模板 | | Agent | 带状态的服务 + 决策逻辑 | --- ## 写在最后 词汇税不是阴谋,但它有真实的代价。它让有经验的工程师低估自己已有的能力,让新人觉得这个领域比实际上更难进入。 下次当你遇到一个陌生的 AI 术语,不妨先问自己:**"如果我是五年前,没有这个词,我会怎么描述这件事?"** 大多数时候,你会发现你早就认识它了。 AI 智能体领域确实有令人兴奋的新东西。但其中最难的部分,往往不是理解那些新概念——而是先剥掉裹在旧概念外面的那层新皮。