AI Agent

ChatGPT能写文章、能回答问题,但你让它帮你订一张机票,它做不到。它能告诉你"你应该去携程搜一下",但它没法真的打开网页、输入日期、比价、下单。 **AI Agent(智能体)**就是为了解决这个问题——让LLM不仅能"想",还能"做"。Agent = LLM + 工具使用 + 自主规划 + 记忆。 一、从聊天机器人到智能体 1.1 纯LLM的局限 一个纯粹的LLM本质上是一个"无状态的文本生成器": graph LR IN["输入文本"] --> LLM["LLM"] LLM --> OUT["输出文本"] 它有几个根本性的限制: 限制 表现 无法执行动作 不能调API、不能操作文件、不能访问数据库 无持久记忆 每次对话结束后就"忘了" 知识过时 无法获取训练数据之后的信息 无法自主规划 不能将复杂任务分解并逐步执行 单轮思考 一次生成就结束,不能自我检查和修正 1.2 Agent的定义 AI Agent是一个以LLM为"大脑"的自主系统,能够: 感知:理解用户的意图和当前环境状态 规划:将复杂目标分解为可执行的步骤 行动:调用外部工具完成具体操作 观察:获取行动的结果 反思:根据结果调整策略 graph TD USER["用户目标"] --> PERCEIVE["感知理解意图"] PERCEIVE --> PLAN["规划分解任务"] PLAN --> ACT["行动调用工具"] ACT --> OBSERVE["观察获取结果"] OBSERVE --> REFLECT["反思是否完成?"] REFLECT -->|未完成| PLAN REFLECT -->|已完成| RESULT["返回结果"] 二、Agent的核心组件 2.1 架构总览 graph TB subgraph Agent ["AI Agent"] LLM["LLM(大脑)"] PLAN["规划模块"] MEM["记忆系统"] TOOL["工具集"] end USER["用户"] --> LLM LLM --> PLAN PLAN --> LLM LLM --> MEM MEM --> LLM LLM --> TOOL TOOL --> LLM LLM --> USER 2.2 LLM:Agent的大脑 LLM负责理解、推理和决策。作为Agent大脑的LLM需要具备: ...

May 2, 2026

App启动流程

启动类型概览 iOS应用启动分为三种类型: 启动类型 描述 特点 冷启动(Cold Launch) App完全不在内存中,需要从头开始加载 耗时最长,需要完整执行所有启动流程 热启动(Warm Launch) App在后台被挂起(Suspended),重新进入前台 最快,只需恢复App状态,不需要重新创建进程 预热启动(Pre-warm Launch) 系统预测用户可能启动App,提前在后台执行部分启动流程 iOS 15+引入,介于冷启动和热启动之间 graph LR subgraph "冷启动(耗时最长)" A1[创建进程] --> A2[加载dylib] A2 --> A3[Rebase/Bind] A3 --> A4[+load] A4 --> A5[main] A5 --> A6[首帧渲染] end subgraph "预热启动(iOS 15+)" B1[系统提前完成(后台):创建进程 → 加载dylib → Rebase/Bind] --> B2[用户点击后执行:+load → main → 首帧渲染] end subgraph "热启动(最快)" C1[唤醒进程] --> C2[WillEnterForeground] C2 --> C3[DidBecomeActive] C3 --> C4[恢复UI] end 一、冷启动(Cold Launch) 冷启动是最完整的启动流程,也是启动优化的主要关注点。 冷启动流程概览 flowchart TD Start([App 冷启动流程]) --> PreMain subgraph PreMain["Pre-main 阶段"] direction TB A[加载可执行文件] --> B[加载动态库(包括 Swift Runtime)] B --> C[Rebase & Bind] C --> D[ObjC Runtime 初始化(类注册、Category 附加)] D --> D2[Swift Runtime 元数据注册(类型元数据、协议遵循表)] D2 --> D3[调用 +load 方法] D3 --> E[执行 Initializers(C++静态构造函数、__attribute__)] end PreMain --> MainPhase subgraph MainPhase["main 阶段"] direction TB F[main函数] --> G[UIApplicationMain] G --> H[AppDelegate回调] H --> I[首帧渲染(创建Window、RootViewController、首屏UI)] end MainPhase --> End([启动完成]) Pre-main 阶段 Pre-main阶段是指从用户点击App图标到main函数执行之前的过程,这个阶段主要由dyld(动态链接器)负责。 ...

May 2, 2026

DRY原则

什么是DRY原则? DRY(Don’t Repeat Yourself)原则是由 Andy Hunt 和 Dave Thomas 在《The Pragmatic Programmer》一书中提出的软件开发原则。 Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. 系统中的每一项知识都必须有一个单一、明确、权威的表示。 DRY 原则的核心不仅仅是"不要复制粘贴代码",而是确保知识和逻辑在系统中只存在一处。 核心思想 知识的单一来源 DRY 原则强调的是"知识"的不重复,而非简单的"代码"不重复。知识包括: 业务规则:如"订单金额超过100元免运费" 数据结构定义:如用户模型的字段 算法逻辑:如价格计算公式 配置信息:如 API 地址、超时时间 // 违反 DRY:同一个业务规则在多处定义 class OrderService { func calculateShipping(orderAmount: Double) -> Double { if orderAmount > 100 { // 业务规则:100元以上免运费 return 0 } return 10 } } class CartViewController { func updateShippingLabel() { if cartTotal > 100 { // 重复的业务规则! shippingLabel.text = "免运费" } else { shippingLabel.text = "运费:¥10" } } } class CheckoutViewModel { func getShippingFee() -> Double { return totalAmount > 100 ? 0 : 10 // 又是重复! } } // 遵循 DRY:业务规则只定义一次 struct ShippingPolicy { static let freeShippingThreshold: Double = 100 static let standardShippingFee: Double = 10 static func calculateFee(for orderAmount: Double) -> Double { return orderAmount > freeShippingThreshold ? 0 : standardShippingFee } static func isFreeShipping(for orderAmount: Double) -> Bool { return orderAmount > freeShippingThreshold } } // 所有地方都使用这个单一来源 class OrderService { func calculateShipping(orderAmount: Double) -> Double { return ShippingPolicy.calculateFee(for: orderAmount) } } class CartViewController { func updateShippingLabel() { if ShippingPolicy.isFreeShipping(for: cartTotal) { shippingLabel.text = "免运费" } else { shippingLabel.text = "运费:¥\(ShippingPolicy.standardShippingFee)" } } } DRY vs WET WET 是 DRY 的反义词,常见的解释有: ...

May 2, 2026

Flutter 详解

Flutter 是 Google 推出的跨平台 UI 工具包,用 Dart 写一份代码即可编译到 iOS、Android、Web、macOS、Windows、Linux、嵌入式设备。它与 KMP/RN 的最大区别是"不映射到原生控件,也不跑在 WebView 里"——Flutter 自带一套 Impeller 渲染引擎,直接向 GPU(Metal / Vulkan)提交绘制命令,从按钮到滚动条的每一个像素都是自己画出来的。 这种"自绘"架构让 Flutter 拥有"双端像素级一致“的超能力,也带来了”iOS 原生设计语言永远慢半拍“的原罪。在 AI Coding 时代,Flutter 的统一性和 GenUI 生态让它成为 LLM 生成 UI 的理想容器,但端侧 AI、Liquid Glass 等 Apple 独占能力的缺失也让它在 iOS 26 时代面临新的挑战。 本文从架构出发,穿透到 Dart VM 编译链、Impeller 渲染管线、iOS 嵌入原理,再到 AI 时代的机遇与陷阱,是一篇写给 iOS 开发者的 Flutter 体系化导读。 一、Flutter 是什么 1.1 一句话定义 Flutter 是一套”声明式 UI + 自绘渲染 + AOT 原生代码“的跨平台框架: 声明式 UI:UI 是 build(context) => Widget 的纯函数结果,状态变 → 重新 build → diff → 更新。 自绘渲染:所有控件都是 Flutter 自己用 Impeller 绘制出来的,不使用 UIKit / SwiftUI 组件。 AOT 编译:发布时 Dart 代码被 gen_snapshot 编译为 ARM64 机器码,与 C++ 引擎一起链接到 Flutter.framework 内。 一句话记忆:Flutter 不是跑在 iOS 上,它是借 iOS 的一块 CAMetalLayer 画自己的东西。 ...

May 2, 2026

Instruments详解

Instruments 是 Xcode 内置的性能分析套件,基于 DTrace/Apple Trace 基础设施构建,可以对 iOS、iPadOS、macOS、watchOS、tvOS、visionOS 的应用与系统进行 CPU、内存、图形、能耗、网络、I/O 等各个维度的观测。 Xcode 26(WWDC25)对 Instruments 做了近年来最大规模的升级,重点包括: Power Profiler:全新的能耗分析工具,支持 Tethered 与 Passive 两种录制模式。 下一代 SwiftUI instrument:基于 Cause & Effect Graph 可视化状态变更到视图更新的因果链。 Processor Trace:基于 Apple Silicon 硬件特性的"全量指令级"采集(Xcode 16.3 引入,26 完善)。 CPU Counters 重做:引入 Bottleneck Analysis 方法论与 CPU Bottlenecks 模板。 Foundation Models instrument:为 FoundationModels 框架提供 Prompt / Asset Loading / Inference 分阶段观测。 Animation Hitches 重做:修正多显示器场景下的统计,数据量显著减少,处理更快。 UI 重设计:主菜单精简、Settings 页面重写、Track 次级菜单、Launch/Attach 环境变量配置。 一、Instruments 基础 1.1 启动 Instruments 的几种方式 方式 使用场景 Xcode Product → Profile(⌘I) 日常开发,自动以 Release 模式编译并附加符号 Xcode Debug Navigator 中的图表 粗略观测 CPU / Memory / Disk / Network / Energy 直接打开 Instruments.app 对已安装的 App、系统进程或已录制的 .trace 文件进行分析 命令行 xcrun xctrace record CI / 自动化性能回归 设备端 Developer Settings 的 Power Profiler 离线、无 Mac 场景采集能耗数据 Xcode 26 中,Product → Profile 会按默认 scheme 的 Profile 构建配置(通常是 Release + -O),这与 Debug 模式的数据差异很大,做性能评估时必须用 Profile 构建。 ...

May 2, 2026

代理模式

在iOS开发中,“代理"这个词通常有两层含义: 设计模式中的代理模式(Proxy Pattern):提供目标对象的替代品或占位符,控制对目标对象的访问 iOS中的代理模式(Delegate Pattern):一种基于协议的回调机制,用于对象间通信 虽然中文都叫"代理”,但它们是不同的设计模式,解决不同的问题。本文将两者都进行详细介绍。 一、Proxy Pattern(代理模式) 定义 代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。 核心思想:代理对象作为真实对象的"替身",客户端通过代理间接访问真实对象。代理可以在访问前后添加额外逻辑,而调用者对此毫不知情。 Client → Proxy → RealSubject ↑ 控制访问 添加功能 延迟创建 关键特征: 代理对象与真实对象实现相同的接口 客户端不知道自己使用的是代理还是真实对象 代理对象持有真实对象的引用(通常是强引用) 模式结构 classDiagram class Subject { <<interface>> +request() } class RealSubject { +request() } class Proxy { -realSubject: RealSubject +request() } class Client { +useSubject() } Subject <|.. RealSubject Subject <|.. Proxy Proxy --> RealSubject : delegates to Client --> Subject : uses Proxy的类型 1. 虚拟代理(Virtual Proxy) 虚拟代理用于延迟创建开销大的对象,只有在真正需要时才创建: ...

May 2, 2026

小红书-社招-5年 · 第 1 轮 · 技术面试

← 已是第一轮 · 返回本次面经 · 第 2 轮 → 本轮概述: 该轮面试主要考察了前端基础知识,包括BFC、浮动元素、作用域、异步编程、Webpack、Vue等多方面的内容。题目涉及理论知识和代码实现。 本轮共 18 道题。答案默认折叠,便于先自行作答。 1. BFC讲一下 题库原题:什么是BFC? 题目要点 BFC(Block Formatting Context) 是 CSS 中一个重要的布局概念,它描述了一个块级元素的内部布局和外部布局之间的关系。BFC 主要用于处理元素的布局、浮动、边距合并等问题。 BFC 的作用 阻止外边距折叠: 外边距折叠:当两个块级元素垂直相邻时,它们的外边距会合并,形成一个更大的外边距。 BFC:在 BFC 内部的元素的外边距不会影响到外部 BFC 的元素,避免了外边距折叠的问题。 包含浮动元素: 浮动元素:通常会从其包含块中溢出。 BFC:具有 BFC 的元素可以包含其内部的浮动元素,确保其高度包括浮动元素的高度。 控制元素的布局: BFC:在 BFC 内部,元素的布局(如浮动、定位)会受到影响和控制,避免与外部元素发生冲突。 防止元素重叠: BFC:能够隔离不同的 BFC 区域,避免元素之间的重叠或干扰。 如何触发 BFC BFC 会在以下情况中被触发: 块级格式化上下文的创建: 元素的 display 属性值为 block 或 inline-block。 元素的 position 属性值为 absolute 或 fixed。 元素的 float 属性值为 left 或 right。 元素的 overflow 属性值为 hidden、scroll 或 auto。 其他常见触发情况: ...

February 1, 2026

小红书-社招-3年 · 第 1 轮 · 技术面试

← 已是第一轮 · 返回本次面经 · 第 2 轮 → 本轮概述: 这一轮主要考察了前端基础知识,包括JavaScript的基本类型和引用类型的存储、闭包的内存泄漏、垃圾回收机制、事件循环、React框架的理解等。 本轮要点: Promise、CDN原理、Class、数据类型、浏览器渲染过程 本轮共 24 道题。答案默认折叠,便于先自行作答。 1. js中基本类型 和 引用类型的存储有什么不一样 题库原题:引用类型有哪些,有什么特点 题目要点 在JavaScript中,引用类型是指非基本数据类型,它们是由对象、数组、函数等复杂数据结构组成的。 参考答案 在JavaScript中,引用类型是指非基本数据类型,它们是由对象、数组、函数等复杂数据结构组成的。 常见的引用类型包括: 对象(Object):对象是JavaScript中最基本的引用类型,它可以用来存储键值对,也可以通过原型链实现继承。 数组(Array):数组是一种有序的集合,可以存储任意类型的数据,它的长度是动态的,可以随时添加或删除元素。 函数(Function):函数是一种可执行的对象,可以封装一段可重复使用的代码。函数可以接收参数并返回值。 引用类型的特点包括: 引用类型的值是可变的:引用类型的值是存储在堆内存中的,当我们修改一个引用类型的值时,实际上是修改了它在内存中的地址,而不是修改了该值本身。 引用类型值的比较是引用的比较:当使用"==“或”===“运算符比较两个引用类型的值时,它们会进行引用的比较,即判断它们是否指向同一个内存地址。只有当两个引用指向同一个对象时,它们才被认为是相等的。 引用类型可以有自己的属性和方法:引用类型的值可以拥有自己的属性和方法。例如,数组对象有长度属性和一些常用的数组方法(例如push、pop、sort等),而函数对象有call、apply等方法。 引用类型可以通过原型链实现继承:通过原型链,引用类型可以继承父类型的属性和方法。 基本类型(如数字、字符串、布尔值)在JavaScript中是按值传递的,而引用类型是按引用传递的。 这意味着当将一个引用类型的值赋给另一个变量时,实际上是将内存地址复制给了新的变量,两个变量引用的是同一对象。而基本类型的值赋给另一个变量时,会创建一个新的值并赋给新的变量。 2. 闭包的内存泄漏应该如何监控和解决? 题库原题:如何检查Javascript中的内存泄漏? 题目要点 Chrome 浏览器查看内存占用,按照以下步骤操作。 参考答案 浏览器 Chrome 浏览器查看内存占用,按照以下步骤操作。 1、打开开发者工具,选择 Timeline 面板 2、在顶部的Capture字段里面勾选 Memory 3、点击左上角的录制按钮。 4、在页面上进行各种操作,模拟用户的使用情况。 5、一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况。 如果内存占用基本平稳,接近水平,就说明不存在内存泄漏。 反之,就是内存泄漏了。 命令行 命令行可以使用 Node 提供的process.memoryUsage方法。 console.log(process.memoryUsage()); // { rss: 27709440, // heapTotal: 5685248, // heapUsed: 3449392, // external: 8772 } process.memoryUsage返回一个对象,包含了 Node 进程的内存占用信息。该对象包含四个字段,单位是字节,含义如下。 ...

February 1, 2026

小红书-社招-1年 · 第 1 轮 · 技术面试

← 已是第一轮 · 返回本次面经 · 第 2 轮 → 本轮概述: 这一轮主要考察了前端技术栈的深度和广度,包括CSS框架、响应式设计、渲染优化、Vue框架的高级特性、HTTP协议、浏览器渲染机制等方面的知识。 本轮共 22 道题。答案默认折叠,便于先自行作答。 1. 项目中为何选择 Tailwind CSS ? 题目要点 灵活性, 可维护性, 现代前端工具链 参考答案 Tailwind CSS 是一个高度可定制的低级 CSS 框架,它提供了大量的原子类,可以快速构建一致且响应式的布局。选择 Tailwind CSS 的原因可能包括其灵活性、可维护性以及与现代前端工具链的良好集成。 2. Tailwind 是怎么解决样式冲突的? 题库原题:Tailwind 是怎么解决样式冲突的? 题目要点 Tailwind 通过原子化 utility 类、统一选择器权重、避免层级嵌套、构建期生成样式以及可预测的覆盖规则,从架构层面降低样式冲突的发生概率。它不是依赖命名规范解决冲突,而是通过设计约束和单一职责类模型,使样式天然解耦,从而让冲突可控且可预测。 参考答案 这个问题如果只回答“因为类名不会冲突”是远远不够的。 Tailwind 解决样式冲突,本质上是通过 设计约束 + 原子化模型 + 构建期生成策略,从源头消除冲突的产生条件,而不是事后避免覆盖。 下面从机制层面拆解。 一、传统 CSS 为什么容易冲突 传统样式冲突来自三个方面: 1. 选择器冲突 .btn { color: red; } .button { color: blue; } 不同模块命名不规范会互相覆盖。 2. 层级嵌套带来的权重问题 .page .btn { ... } 权重升级,导致后续难以覆盖。 3. 样式全局污染 CSS 默认是全局作用域,没有隔离边界。 ...

February 1, 2026

360-实习 · 第 1 轮 · 技术面试

← 已是第一轮 · 返回本次面经 · 已是最后一轮 → 本轮概述: 这一轮主要考察了候选人的自我介绍、职业规划、项目经验、前端技术细节以及算法问题。面试氛围较为全面,涵盖了从基础到高级的多个方面。 面试时间: 45分钟 本轮共 21 道题。答案默认折叠,便于先自行作答。 1. 自我介绍 题目要点 个人背景, 工作经验, 技术栈, 项目经验, 未来展望 参考答案 自我介绍应包括个人背景、工作经验、技术栈、项目经验和对未来的展望。重点在于展示自己的专业能力和对前端技术的热情。 2. 你的职业规划与前端技术方向的关系? 题目要点 技术趋势, 个人兴趣, 学习和实践, 技能提升, 未来发展 参考答案 回答时可以结合当前的技术趋势和个人兴趣,说明如何通过不断学习和实践来提升自己在前端领域的技能。同时,可以提到对未来技术发展的看法和如何适应这些变化。 3. 讲一个你参与过的项目 题库原题:你参与过哪些前端基建方面的建设? 题目要点 前端基建涵盖了从项目架构、构建工具配置到开发环境、自动化流程和安全等多个方面。良好的前端基建可以提升开发效率、代码质量和系统性能,为项目的长期维护和扩展打下坚实的基础。 参考答案 可以从以下方面进行回答: 1. 项目架构 目录结构:定义项目文件和目录的组织结构,确保项目的可维护性和可扩展性。 代码规范:统一代码风格和规范,使用 ESLint、Prettier 等工具进行代码检查和格式化。 模块化:采用模块化设计,将代码拆分为独立的模块,提高可重用性和可维护性。 脚手架:自动生成项目的基础结构,包括目录结构、配置文件、示例代码等。 2. 构建工具和配置 构建工具:使用工具如 Webpack、Vite、Rollup 等进行项目的构建和打包。 配置管理:配置构建工具以支持各种功能,如代码分割、热重载、环境变量等。 优化:配置代码压缩、缓存策略、Tree Shaking 等,提升构建产物的性能和效率。 3. 开发环境 开发服务器:设置本地开发服务器,支持热重载和调试功能。 环境配置:管理开发、测试、生产环境的配置和变量。 4. 包管理 依赖管理:使用 npm、Yarn 或 pnpm 等工具管理项目依赖,确保依赖版本一致性。 发布管理:管理和发布自定义的 npm 包或组件库。 5. 自动化流程 CI/CD:配置持续集成(CI)和持续部署(CD)流程,自动化构建、测试和部署。 测试:集成单元测试、集成测试和端到端测试工具,如 Jest、Cypress 等,确保代码质量。 6. 代码质量 静态分析:使用 ESLint、TSLint 等工具进行代码静态检查。 测试覆盖率:监控测试覆盖率,确保关键代码路径被充分测试。 7. 文档 代码文档:编写和维护项目文档,包括 API 文档、开发指南和使用说明。 自动化文档生成:使用工具如 Storybook、JSDoc 等生成组件库和 API 文档。 8. 组件库 设计系统:构建和维护一套一致的设计系统和组件库,提高开发效率和界面一致性。 共享组件:创建和管理共享组件,促进代码复用。 9. 性能优化 前端性能:优化页面加载速度、响应时间和渲染性能。 网络请求:管理和优化网络请求策略,减少请求次数和数据传输量。 10. 安全 安全最佳实践:遵循前端安全最佳实践,如防范 XSS 和 CSRF 攻击。 敏感数据保护:确保敏感数据不被暴露或滥用。 4. 项目中遇到的印象最深的难题是什么?如何解决的? 题目要点 难题背景, 影响, 解决方案, 最终结果 ...

January 31, 2026