LLM Wiki:Karpathy 提出的"后 RAG 时代"个人知识库范式

2026 年 4 月,Andrej Karpathy 在 GitHub Gist 上发布了一份名为 llm-wiki.md 的"想法文档"(idea file),短短几天收获了 5000+ 星和 5000+ fork。这篇文档篇幅不长,却被评价为"后 RAG 时代"个人知识管理的一个**分类定义性(category-defining)**框架。 原文地址:https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f Karpathy 写这篇文档的风格非常有意思:它不是一份实现规范,而是一份"可以复制粘贴给你自己的 LLM Agent(Claude Code、Codex、OpenCode/Pi 等)“的模式描述。它告诉 Agent 模式的核心是什么,剩下的细节(目录结构、schema 约定、页面格式、工具链)交给用户和 Agent 一起协同演化。 本文将详细拆解这份文档的核心思想,并结合社区讨论(scale 极限、Zettelkasten 替代方案、反对派观点等)给出一个完整解读。 一、问题起点:RAG 的"每次从零开始” 大多数人今天使用 LLM 处理文档的方式是 RAG(检索增强生成): 把一堆文档上传到系统 查询时 LLM 从原文中检索相关片段 基于片段合成答案 NotebookLM、ChatGPT 文件上传、绝大多数 RAG 系统都是这个模式。 1.1 RAG 的根本局限:没有累积 Karpathy 点出了 RAG 的一个关键问题:LLM 每次都在从零开始重新发现知识。 问一个需要综合 5 篇文档的微妙问题 → LLM 每次都要重新找出相关片段,重新拼接,重新推理 上一次你问过的关键问题的答案,下一次再问时不会被保留 跨源的矛盾、交叉引用、细微的关联 —— 每次都要重新建立 什么都没有被"沉淀"下来。 这就像每次考试都重新读一遍教科书,却从不做笔记、从不整理知识卡片、从不画思维导图。 1.2 LLM Wiki 的核心差异 LLM Wiki 提出了一个根本性的反转: ...

May 2, 2026

iOS音视频原理

音视频开发是iOS中一个庞大且核心的技术领域,涵盖采集、编码、传输、解码、渲染等完整链路。本文从基础概念出发,深入iOS平台提供的音视频框架和底层原理。 音视频处理全链路 一个完整的音视频系统(如直播、视频通话、短视频)包含以下环节: flowchart LR A["采集Camera/Mic"] --> B["前处理美颜/降噪"] B --> C["编码H.264/AAC"] C --> D["封装FLV/MP4"] D --> E["传输RTMP/HLS"] E --> F["解封装解析容器"] F --> G["解码VideoToolbox"] G --> H["后处理滤镜/混音"] H --> I["渲染Metal/Speaker"] 环节 做什么 iOS中对应的技术 采集 从摄像头/麦克风获取原始数据 AVCaptureSession 前处理 美颜、滤镜、降噪、回声消除 CoreImage、vDSP 编码 将原始数据压缩为体积更小的格式 VideoToolbox、AudioToolbox 封装 将编码后的音视频数据打包到容器中 AVAssetWriter 传输 通过网络发送到服务器或其他端 URLSession、第三方推流SDK 解封装 从容器中分离出音频流和视频流 AVAssetReader 解码 将压缩数据还原为原始像素/采样数据 VideoToolbox、AudioToolbox 后处理 滤镜、水印、混音等 CoreImage、Metal 渲染 将画面显示到屏幕、声音输出到扬声器 AVSampleBufferDisplayLayer、Metal、AudioUnit 核心术语 在深入细节之前,先了解几个贯穿全文的核心概念: 什么是编解码器(Codec) Codec = Coder + Decoder,即编码器+解码器的合称。编码器负责将原始音视频数据压缩为更小的格式,解码器负责将压缩数据还原。 音视频中有两类完全不同的"格式"概念,初学者极易混淆: 编码格式(Codec):定义数据怎么压缩 视频:H.264、H.265、VP9、AV1 音频:AAC、MP3、Opus、FLAC 封装格式(Container):定义压缩后的数据怎么组织存储 MP4、MOV、FLV、MKV、TS 两者是独立的,可以自由组合: MP4容器 + H.264视频 + AAC音频 (最常见的组合) MOV容器 + H.265视频 + AAC音频 (iPhone录制默认) FLV容器 + H.264视频 + AAC音频 (直播推流常用) MKV容器 + AV1视频 + Opus音频 (新一代免费组合) 什么是H.264和H.265 H.264(也叫AVC,Advanced Video Coding)是目前使用最广泛的视频编码标准,由ITU-T和ISO/IEC联合制定,2003年发布。它定义了一套将视频帧压缩为比特流的算法规范——包括如何划分图像块、如何预测、如何变换量化等。几乎所有设备和平台都支持H.264硬件解码。 ...

May 2, 2026

建造者模式

定义 建造者模式(Builder Pattern)是一种创建型设计模式,它将复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。建造者模式特别适用于创建包含多个可选参数的对象。 建造者模式的核心思想是:将一个复杂对象的构建过程分解为多个简单的步骤,通过一步一步地构建,最终得到完整的对象。 为什么需要建造者模式 建造者模式要解决的核心问题是:简化复杂对象的创建过程,特别是当对象有很多可选参数时。 问题场景:假设我们需要创建一个网络请求配置对象,它有很多可选参数。 使用构造函数的方式: struct NetworkConfig { let baseURL: URL let timeout: TimeInterval let retryCount: Int let cachePolicy: CachePolicy let headers: [String: String] let enableLogging: Bool let certificatePinning: Bool // 构造函数参数太多! init(baseURL: URL, timeout: TimeInterval = 30, retryCount: Int = 3, cachePolicy: CachePolicy = .default, headers: [String: String] = [:], enableLogging: Bool = false, certificatePinning: Bool = false) { // ... } } // 使用时 - 参数太多,难以阅读 let config = NetworkConfig( baseURL: URL(string: "https://api.example.com")!, timeout: 60, retryCount: 5, cachePolicy: .reloadIgnoringCache, headers: ["Authorization": "Bearer token"], enableLogging: true, certificatePinning: true ) 这种方式有什么问题? ...

May 2, 2026

Mach-O的链接、装载与库

本文深入介绍iOS/macOS开发中的Mach-O文件格式、动态库与静态库,帮助理解程序的链接、装载过程,以及Rebase/Bind的底层原理。 一、基础概念 在深入学习之前,先了解几个核心概念。 1.0 从源代码到可执行文件 一段源代码要变成可执行文件,需要经历以下几个阶段: flowchart TB A["源代码 (.m/.swift)"] --> B["预处理展开宏、处理#import/#include"] B --> C["编译源代码 → 汇编代码"] C --> D["汇编汇编代码 → 目标文件(.o)"] D --> E["链接目标文件 + 库 → 可执行文件"] E --> F["可执行文件"] 1.1 什么是符号(Symbol) 符号是程序中函数、变量、类等实体的名称标识。当你写下一个函数调用时,编译器需要知道这个函数在哪里,符号就是用来定位它的。 // 这段代码中包含多个符号 void myFunction(void) { // 定义了符号 _myFunction NSLog(@"Hello"); // 引用了外部符号 _NSLog } int globalVar = 10; // 定义了符号 _globalVar 符号分为两类: 已定义符号:在当前编译单元(即当前源文件编译生成的.o文件)中有具体代码或数据定义的符号(如上例中的 _myFunction 和 _globalVar) 未定义符号:当前编译单元只是引用,实际定义在其他编译单元或库中的符号(如上例中的 _NSLog),需要在链接时解析 1.2 什么是链接(Linking) 链接是将多个编译后的目标文件(.o)组合成一个可执行文件的过程。链接器的核心工作是符号解析——把所有"未定义符号"与其他编译单元中的"已定义符号"关联起来。 flowchart LR subgraph 输入 A["main.o已定义: _main未定义: _foo"] B["foo.o已定义: _foo未定义: _NSLog"] C["Foundation已定义: _NSLog"] end A --> D["链接器符号解析"] B --> D C --> D D --> E["可执行文件所有符号已解析"] 符号解析的过程 链接器会遍历所有输入的目标文件和库,执行以下步骤: ...

May 2, 2026

iOS无障碍树详解

前言 打开 iPhone 的"设置 → 辅助功能 → 旁白(VoiceOver)",再用三指在屏幕上滑动,iPhone 就会逐个读出当前界面上的每个控件:"登录按钮,双击以激活"、"用户名,文本框,已填写 ancheng"、"价格,¥99"。这背后支撑 VoiceOver、开关控制(Switch Control)、语音控制(Voice Control)、全键盘访问(Full Keyboard Access)、Xcode UITest、AI Agent 识屏 等一系列能力的,是一棵并行于 UIView 视图树的 无障碍树(Accessibility Tree,AX Tree)。 对大多数 iOS 工程师来说,无障碍树是"黑盒":设了 isAccessibilityElement = true、填了 accessibilityLabel,然后就不管了。但一旦遇到以下问题,就不得不深入理解它的工作机制: 为什么 UITest 里用 XCUIElement 定位不到某个按钮? 为什么打开 VoiceOver 后 App 明显变卡? 为什么一个自绘的 CALayer 按钮在 VoiceOver 下无法被朗读? 为什么 UILabel 嵌在 UIStackView 里焦点顺序是乱的? SwiftUI 的 .accessibilityElement(children: .combine) 到底合并了什么? Accessibility Inspector 里看到的树结构从哪儿来? AI Agent(如 iOS 上的 Claude、ChatGPT Mac 客户端)是如何"看到"屏幕的? 本文从操作系统层的 AX 架构讲起,逐层拆解:AX Server 守护进程、UIKit 中的 UIAccessibility 协议、UIAccessibilityElement 类、UIAccessibilityContainer 协议、焦点排序算法、SwiftUI 的 AX 实现、辅助技术的工作原理、性能优化、UITest 与 AI 识屏原理、调试工具与最佳实践。 ...

May 2, 2026

Harness Engineering 精读笔记

基于 2025.11 — 2026.03 期间 Anthropic、OpenAI、LangChain、Google DeepMind、Stanford 发布的九篇核心文章的精读整理。 目标:理解 Harness Engineering 是什么,以及大公司和学术机构在实践中做了哪些探索。 一、什么是 Harness Engineering? 1.1 Harness 的定义 LangChain 给出了最清晰的公式化定义: Agent = Model + Harness “If you’re not the model, you’re the harness.” “A harness is every piece of code, configuration, and execution logic that isn’t the model itself.” 具体来说,Harness 包括: System Prompts — 系统提示词,定义 Agent 的行为边界 Tools / Skills / MCPs — 工具集及其描述(文件读写、代码执行、浏览器操作等) Bundled Infrastructure — 捆绑基础设施(文件系统、沙箱、浏览器) Orchestration Logic — 编排逻辑(子 Agent 派生、任务交接、模型路由) Hooks / Middleware — 确定性执行逻辑(压缩、续行、lint 检查等) Anthropic 不给 Harness 下抽象定义,而是通过实践来展示:他们用 “harness design” 和 “harnesses for long-running agents” 来描述围绕 Agent 执行循环的编排层。Anthropic 在博客中明确说:“harness design is key to performance at the frontier of agentic coding”。 ...

May 2, 2026

工厂模式

定义 工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建逻辑与使用逻辑分离。工厂模式有三种变体:简单工厂模式、工厂方法模式和抽象工厂模式。 为什么需要工厂模式 工厂模式要解决的核心问题是:将对象的创建过程与使用过程分离。 问题场景:假设我们正在开发一个网络层,需要创建URLSession对象。 直接使用构造函数创建对象看起来很简单: class NetworkManager { func fetchData(from url: URL) { // 直接创建URLSession let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 30 config.httpAdditionalHeaders = ["Authorization": "Bearer xxx"] let session = URLSession(configuration: config) session.dataTask(with: url) { data, response, error in // 处理响应... }.resume() } } 这种方式有什么问题? 创建逻辑与业务逻辑混在一起:fetchData方法既要负责创建session,又要负责发起请求,职责不单一 创建逻辑无法复用:如果另一个方法也需要相同配置的session,只能复制这段创建代码 难以替换实现:如果想在测试时使用Mock的session,或者在不同环境使用不同配置,需要修改业务代码 隐藏了依赖关系:从方法签名看不出这个类依赖URLSession,依赖关系被隐藏在方法内部 工厂模式的解决思路: 工厂模式的核心是:调用方不再自己new对象,而是向工厂"要"对象。 // 工厂负责创建 class URLSessionFactory { static func createDefault() -> URLSession { let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 30 config.httpAdditionalHeaders = ["Authorization": "Bearer xxx"] return URLSession(configuration: config) } static func createBackground(identifier: String) -> URLSession { let config = URLSessionConfiguration.background(withIdentifier: identifier) return URLSession(configuration: config) } } // 业务代码只负责使用 class NetworkManager { private let session: URLSession init(session: URLSession = URLSessionFactory.createDefault()) { self.session = session } func fetchData(from url: URL) { session.dataTask(with: url) { data, response, error in // 处理响应... }.resume() } } 这样做的好处: ...

May 2, 2026

KVO底层原理

基础概念 KVO(Key-Value Observing,键值观察)是 Apple 基于 KVC 实现的一种观察者模式,允许对象监听另一个对象特定属性的变化。当被观察对象的属性值发生改变时,观察者会收到通知。 // 注册观察 [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; // 接收通知 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context { if ([keyPath isEqualToString:@"name"]) { NSLog(@"name changed: %@ → %@", change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]); } } // 移除观察 - (void)dealloc { [person removeObserver:self forKeyPath:@"name"]; } KVO 的底层实现原理:isa-swizzling KVO 的核心实现机制是 isa-swizzling(isa 指针替换)。当对象被添加观察者时,Runtime 会在运行时动态创建该对象所属类的一个子类,并将对象的 isa 指针指向这个新的子类。 完整流程 flowchart TD A["addObserver:forKeyPath:"] --> B["Runtime 动态创建子类NSKVONotifying_ClassName"] B --> C["重写被观察属性的 setter 方法"] B --> D["重写 class 方法返回原始类"] B --> E["重写 dealloc 方法"] B --> F["重写 _isKVOA 方法返回 YES"] C --> G["将对象的 isa 指向NSKVONotifying_ClassName"] G --> H["当属性值改变时"] H --> I["调用 willChangeValueForKey:"] I --> J["调用原始 setter 方法"] J --> K["调用 didChangeValueForKey:"] K --> L["通知所有观察者"] 第一步:动态创建子类 当首次对某个类的实例调用 addObserver:forKeyPath:options:context: 时,Runtime 会: ...

May 2, 2026

iOS国际化

前言 很多团队对"国际化"的理解停留在"把中文文案翻译成英文",等真正把 App 推向多区域时就会发现远远不够:阿拉伯语用户看到的界面左右颠倒了,用户在纽约和东京看到的"今天"不是同一天,欧洲的小数点变成了逗号,日本用户抱怨价格里的¥符号指向人民币而不是日元,印度用户发现自己的 ₹1,23,456.78 被错误地按千分位显示成 ₹123,456.78,复数规则在俄语里比英语复杂得多,而这些问题单靠"翻译"都解决不了。 国际化(Internationalization,简称 i18n,取首末字母与中间 18 个字符)是架构层面的能力建设,让 App 能够适配不同语言、地区、文字方向、日历、时区、货币、单位制与文化习俗;而本地化(Localization,l10n)是针对某个具体地区的"适配落地"。两者的关系是:“国际化一次,本地化 N 次”。 本文从 NSLocale/Locale 的基础模型开始,把 iOS 国际化的完整工程面梳理清楚——文案本地化、复数/性别规则、RTL 布局、多时区、多币种、单位与数字格式、图片资源、字体、App 内切换语言、测试方法,最后给出研发规范与排查清单。 一、基础概念:Locale、Language 与 Region 1.1 Locale 不等于 Language 大多数工程师第一次接触国际化时会把"语言"和"地区"混为一谈,这在 iOS 下会直接导致格式错误。 Language(语言):zh、en、ar、ja…决定 UI 文字内容。 Region(地区):CN、US、SA、JP…决定格式(日期、数字、货币、时间制 12/24h、起始星期、度量单位)。 Locale(语言+地区):两者合成一个完整的标识,如 zh_CN、en_US、ar_SA、en_IN。 一个美国人移居日本后,完全可能把 iPhone 语言设为英文,但地区设为日本。此时: 场景 期望 UI 文案 英文 日期格式 1月23日(月) 风格?No,应显示 January 23 (Mon),因为语言决定月/星期的名字 数字分隔符 , 作千分位(与日本/美国一致) 货币符号 ¥(JPY) 温度单位 ℃(日本) 首日周 周日(日本区域) 对应的 Locale 是 en_JP——看似奇怪,但在现实中非常普遍。代码里要区分"用哪个语言去查字符串"和"用哪个 Locale 去格式化数据": let preferredLanguage = Locale.preferredLanguages.first ?? "en" let currentLocale = Locale.current let formatter = DateFormatter() formatter.locale = currentLocale formatter.dateStyle = .long print(formatter.string(from: Date())) 不要传 Locale(identifier: "en") 去格式化数字,那会丢掉用户的区域偏好。 ...

May 2, 2026

Function Calling与MCP

LLM很聪明,但它被困在一个"文本沙箱"里——它只能接收文本、输出文本。要让LLM调用API、查数据库、操作文件,就需要一个"桥梁"协议来连接LLM与外部世界。 Function Calling和**MCP(Model Context Protocol)**就是这样的桥梁。它们的目标相同——让LLM能调用外部工具——但设计理念和使用场景有很大不同。本文将详细对比这两种方案。 一、Function Calling:LLM原生的工具调用 1.1 基本原理 Function Calling是LLM提供商(OpenAI、Anthropic、Google等)在模型层面支持的工具调用能力。核心思路是: 你告诉LLM有哪些函数可用(函数名、参数定义、功能描述) LLM根据用户的问题判断是否需要调用函数 如果需要,LLM输出一个结构化的函数调用请求(而不是自然语言) 你的代码执行这个函数,把结果返回给LLM LLM基于结果生成最终回答 sequenceDiagram participant U as 用户 participant App as 应用程序 participant LLM as LLM API participant Tool as 外部工具/API U->>App: "北京今天天气怎么样?" App->>LLM: 用户消息 + 可用函数定义 LLM->>App: 函数调用请求get_weather(city="北京") App->>Tool: 调用天气API Tool->>App: {"temp": 22, "weather": "晴"} App->>LLM: 函数执行结果 LLM->>App: "北京今天天气晴朗,气温22°C" App->>U: 最终回答 关键点:LLM自身并不执行函数,它只是生成"我想调用这个函数"的结构化指令,真正的执行由应用程序完成。 1.2 函数定义 以OpenAI的格式为例,开发者需要用JSON Schema描述每个可用的函数: { "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的当前天气信息", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,如'北京'、'上海'" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位" } }, "required": ["city"] } } } ] } LLM通过阅读这个定义来理解:这个函数叫什么、干什么用、需要什么参数。description字段的质量直接影响LLM能否正确选择和调用函数。 ...

May 2, 2026