计算机网络

网络分层模型 OSI七层模型 OSI(Open Systems Interconnection)是国际标准化组织提出的网络通信参考模型: 层级 名称 功能 协议/设备举例 7 应用层 为应用程序提供网络服务 HTTP, FTP, DNS, SMTP 6 表示层 数据格式转换、加密/解密 SSL/TLS, JPEG, ASCII 5 会话层 建立、管理和终止会话 RPC, SQL 4 传输层 端到端的可靠数据传输 TCP, UDP 3 网络层 路由选择与IP寻址 IP, ICMP, ARP 2 数据链路层 帧的封装与MAC寻址 Ethernet, Wi-Fi 1 物理层 比特流的物理传输 光纤, 双绞线 TCP/IP四层模型 实际工程中更常用的是TCP/IP四层模型,它将OSI的上三层合并为应用层,下两层合并为网络接口层: graph TB subgraph "TCP/IP四层模型" A["应用层HTTP, DNS, FTP, SMTP"] B["传输层TCP, UDP"] C["网络层IP, ICMP, ARP"] D["网络接口层Ethernet, Wi-Fi"] end subgraph "OSI七层模型" E["应用层"] F["表示层"] G["会话层"] H["传输层"] I["网络层"] J["数据链路层"] K["物理层"] end A --- E A --- F A --- G B --- H C --- I D --- J D --- K 数据封装与解封装 数据在发送方从上到下逐层封装,每一层都将上层传来的数据视为载荷(Payload),在其前面添加本层的头部信息(部分层还会添加尾部信息),然后交给下一层处理。接收方则从下到上逐层解封装,每一层剥离本层头部后将载荷交给上层。 ...

May 3, 2026

Agent 面试题

共 119 道 Agent 面试题。答案默认折叠,便于先自行作答。 1. 如何设计一个企业级 AI Agent 平台? 难度:3.5 · 类型:QA 题目要点 企业级 AI Agent 平台的设计重点不是“接一个大模型接口”,而是围绕业务任务构建完整体系:前端提供可解释、可配置、可监控的交互体验;中间层负责 Agent 编排、工具调用和知识增强;底层统一管理模型、数据和企业系统;治理层保障权限、安全、审计、评测和成本控制。真正可落地的平台,一定是在智能能力和工程确定性之间取得平衡。 参考答案 企业级 AI Agent 平台不能只设计成一个“聊天入口”,更应该设计成一个可治理、可扩展、可观测的智能任务执行平台。它的核心目标是让企业可以安全地把大模型能力接入真实业务流程,例如客服、知识检索、数据分析、代码辅助、审批流、运营自动化等场景。 从架构上看,可以分为四个关键层次:前端体验层、Agent 编排层、能力接入层和企业治理层。 前端体验层不应该只有 Chat UI,而要支持多种交互形态。对于普通用户,可以提供对话式入口、任务面板、历史记录、引用来源、执行进度和人工确认节点;对于业务管理员,需要有 Agent 配置台,可以配置角色、Prompt、工具权限、知识库、工作流、触发条件和发布版本;对于运维和安全人员,则需要看到调用链路、成本消耗、失败原因、审计日志和风险拦截记录。 Agent 编排层是平台的核心。这里不能简单地把用户输入直接发给模型,而是要经过意图识别、上下文构建、计划拆解、工具选择、执行反馈和结果汇总。复杂任务通常需要支持多 Agent 协作,例如一个 Agent 负责理解需求,一个 Agent 负责查知识库,一个 Agent 负责调用业务系统,还有一个 Agent 负责校验输出质量。对于企业场景,工作流式 Agent 和自主规划式 Agent 往往要结合使用:高风险业务用确定性流程约束,低风险探索任务可以给 Agent 更高自主度。 能力接入层主要解决模型、知识和工具的问题。模型层需要做统一网关,屏蔽不同模型厂商差异,支持模型路由、降级、限流、重试和成本统计。知识层通常采用 RAG 架构,需要处理文档解析、切片、向量化、权限过滤、召回排序和答案引用,避免 Agent 编造来源。工具层则要把企业内部系统封装成标准 Tool,例如 CRM、工单、审批、BI、代码仓库、飞书、邮件、数据库等,同时每个工具都要有清晰的输入输出 Schema、权限边界和幂等设计。 企业级平台最容易被低估的是治理能力。Agent 一旦可以调用工具,就不再只是“生成文本”,而是在执行真实操作,所以必须有权限控制、数据脱敏、敏感词策略、操作审计、人工确认和回滚机制。例如查询类工具可以自动执行,但转账、删除、发公告、修改生产配置这类操作必须进入 human-in-the-loop 流程。平台还需要支持租户隔离、部门权限、知识库权限继承和操作留痕。 前端设计上,要重点解决“可解释”和“可控”。用户不能只看到一个最终答案,而应该能看到 Agent 做了哪些步骤、用了哪些知识、调用了哪些工具、哪些地方失败了、是否需要用户确认。对于长任务,要有状态机式的任务视图,而不是让用户一直等在输入框前。对于企业用户来说,信任感往往来自透明度,而不是更拟人的回复。 另外,平台需要内置评测体系。Agent 发布前要经过离线评测,例如准确率、召回率、幻觉率、工具调用成功率、越权风险和响应时延;上线后要持续收集用户反馈、人工纠错、失败样本和成本数据,再反向优化 Prompt、知识库、工具描述和模型路由。没有评测闭环的 Agent 平台,很难在企业里长期稳定运行。 ...

AI相关 面试题

共 43 道 AI相关 面试题。答案默认折叠,便于先自行作答。 1. 如何实现 ChatGPT 类似的流式输出? 难度:1.5 · 类型:QA 题目要点 流式输出的核心是服务端通过 SSE、fetch stream 或 WebSocket 持续返回增量内容,前端通过 ReadableStream 边读边渲染。实现时要注意网络 chunk 不等于完整消息,需要做缓冲和协议解析。前端渲染要做节流,避免频繁状态更新导致卡顿。Agent 场景下建议使用结构化事件协议,把文本增量、工具调用、状态、错误、完成信号分开处理。最后还要支持取消、错误恢复、最终 Markdown 校正和滚动体验,才能达到接近 ChatGPT 的交互质量。 参考答案 类似 ChatGPT 的流式输出,本质是:服务端不要等完整答案生成后再返回,而是把模型生成的 token 或文本片段持续写入 HTTP 响应;前端边读边渲染,并在完成、取消、异常时维护好状态。 实际项目里最常见的是 SSE 或 fetch + ReadableStream。如果只是服务端向前端单向推送,SSE 足够稳定;如果需要双向实时交互,比如语音、多人协作、实时 Agent 控制,可以用 WebSocket。在 ChatGPT 这类文本生成场景中,fetch 读取流也很常用,因为它支持 POST、请求体、鉴权、AbortController,控制能力比原生 EventSource 更强。 前端核心代码大致是这样: async function streamChat(messages: any[], onDelta: (text: string) => void) { const controller = new AbortController(); const response = await fetch('/api/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ messages }), signal: controller.signal, }); if (!response.ok || !response.body) { throw new Error('stream request failed'); } const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const events = buffer.split('\n\n'); buffer = events.pop() || ''; for (const event of events) { const line = event .split('\n') .find((item) => item.startsWith('data:')); if (!line) continue; const data = line.replace(/^data:\s*/, ''); if (data === '[DONE]') { return; } const payload = JSON.parse(data); if (payload.type === 'delta') { onDelta(payload.content); } } } controller.abort(); } 服务端返回时通常使用类似这样的协议: ...

CSS 面试题

共 114 道 CSS 面试题。答案默认折叠,便于先自行作答。 1. Tailwind 的响应式断点(如 md:)底层如何实现? 难度:2 · 类型:QA 题目要点 Tailwind 的响应式断点基于构建期的变体系统实现。md: 在编译阶段被解析为 screen 变体,并根据 screens 配置生成对应的 @media (min-width) 包裹规则。所有响应式逻辑依赖原生 CSS media query,遵循 mobile-first 原则,通过规则生成管线支持多变体组合,不涉及运行时计算。 参考答案 这个问题的关键在于理解: md: 不是运行时逻辑,而是 构建期的变体(variant)展开机制。 在 Tailwind CSS 中,响应式系统建立在三个核心之上: screens 配置映射 变体解析机制 JIT 构建阶段规则生成 一、md: 本质是什么? 当写: <div class="md:text-red-500"> Tailwind 在构建阶段会将其解析为: variant:md utility:text-red-500 然后生成如下 CSS: @media (min-width: 768px) { .md\:text-red-500 { color: #ef4444; } } 关键点: md: 只是一个前缀标记 构建阶段转换成 @media 冒号会被转义为合法 CSS 选择器 二、断点从哪里来? 断点定义在配置文件中: ...

ES6 面试题

共 51 道 ES6 面试题。答案默认折叠,便于先自行作答。 1. 说说 Class 语法糖的底层实现 难度:3 · 类型:QA 题目要点 class 是基于函数构造器和原型链的语法封装,实例方法本质挂载在 prototype 上,继承通过 Object.create 构建原型链,同时通过构造函数原型链继承静态方法。super 依赖内部 [[HomeObject]] 机制定位父类方法。class 并未引入新的对象模型,而是在原型继承基础上增加语义约束与语法增强。 参考答案 这个问题的关键在于理解:class 并不是一种新的面向对象机制,而是对 原型继承(prototype-based inheritance) 的语法封装。底层仍然是函数构造器 + 原型链。 如果把 class 当作 Java 那种基于类的模型去理解,就会产生误解。 先看一个简单例子: class Person { constructor(name) { this.name = name; } say() { console.log(this.name); } static create(name) { return new Person(name); } } 它在底层大致等价于: function Person(name) { this.name = name; } Person.prototype.say = function () { console.log(this.name); }; Person.create = function (name) { return new Person(name); }; 一、实例方法的实现 ...

HTML 面试题

共 77 道 HTML 面试题。答案默认折叠,便于先自行作答。 1. SPA和MPA的区别? 难度:1 · 类型:QA 题目要点 SPA 通过前端路由在单一 HTML 中完成页面切换,交互流畅但首屏和 SEO 成本较高;MPA 每次导航请求新页面,SEO 友好且隔离性强,但交互体验有限;两者本质差异在于前后端渲染与路由职责的划分;现代项目常通过 SSR、SSG 等方式在两种模式之间折中。 参考答案 SPA 和 MPA 的区别,表面看是“页面跳转方式不同”,本质上是前端与服务器在渲染职责、路由控制以及资源加载策略上的分工差异。理解这一点,才能在具体业务中做出合理选择。 SPA(Single Page Application)只有一个 HTML 入口,首屏加载时会拉取完整的 JavaScript 应用骨架,之后的页面切换通过前端路由完成。URL 的变化并不会触发浏览器重新请求文档,而是由前端框架根据路由状态切换视图并复用已有资源。这种模式的优势在于页面切换流畅、状态可以在客户端长期保留,复杂交互体验更接近原生应用,非常适合后台系统、强交互工具型产品。但代价也同样明显:首屏资源压力大,SEO 先天不足,对 JS 执行和异常处理的依赖极高,一旦主 bundle 出现问题,整个应用都无法正常工作。 MPA(Multi Page Application)则以“页面”为核心单元。每一次导航都会向服务器请求一个新的 HTML 文档,路由和渲染主要由服务端控制。页面之间天然隔离,资源和状态会随页面刷新而重置。这种模式的优势在于首屏直观、SEO 友好、容错性强,适合内容型站点、营销页和对搜索引擎依赖较高的业务场景。但在交互复杂度上,MPA 往往需要付出更多成本,例如重复加载公共资源、跨页面状态共享困难,整体体验也更接近传统 Web。 从工程角度看,SPA 和 MPA 的差异还体现在构建和部署方式上。SPA 更强调前端工程化和运行时能力,构建目标是一个可长期运行的客户端应用;MPA 更强调页面级拆分和服务端模板渲染,前端更多是增强层而非控制中心。在现代实践中,两者并非对立关系,常见的折中方案包括 SSR、SSG 以及“多入口 SPA”,试图在交互体验和首屏性能、SEO 之间取得平衡。 因此,选择 SPA 还是 MPA,并不存在绝对优劣,关键在于业务目标:是以复杂交互和状态管理为核心,还是以内容分发和搜索可见性为核心。 2. 常见的H5标签有哪些 ,你是怎么用的 难度:1 · 类型:QA 题目要点 常见 H5 标签包括结构语义标签、文本语义标签、表单标签和媒体标签;实际使用中优先考虑语义表达和可访问性,而非样式;充分利用表单和媒体标签的原生能力以减少 JS 复杂度;canvas 与 svg 需根据场景选择;合理使用语义化标签能显著提升页面质量和长期可维护性。 ...

JavaScript 面试题

共 407 道 JavaScript 面试题。答案默认折叠,便于先自行作答。 1. requestAnimationFrame 与 requestIdleCallback 在渲染优化中的执行时机差异?谁优先触发? 难度:3 · 类型:QA 题目要点 requestAnimationFrame 在浏览器即将进行下一帧渲染之前执行,用于动画与视觉更新,优先级高于渲染阶段后的任务。requestIdleCallback 只会在当前帧所有渲染任务完成且仍有剩余时间时触发,属于低优先级空闲调度机制。因此在同一帧中,rAF 必然先于 rIC 执行。rAF 用于保证帧同步,rIC 用于利用空闲时间执行非关键任务。 参考答案 这个问题本质是理解 浏览器一帧的调度模型。 不搞清楚一帧里发生了什么,很容易回答成“谁快谁慢”这种表层结论。 核心结论先给出: 在有下一帧渲染需求的情况下,requestAnimationFrame 一定优先于 requestIdleCallback 执行。 requestIdleCallback 只会在当前帧还有“空闲时间”时才会触发。 下面从浏览器帧模型拆解。 一、浏览器一帧的基本流程 以 60fps 为例,一帧约 16.6ms。 一次完整渲染循环大致是: 执行宏任务(例如 setTimeout) 清空微任务队列 触发 requestAnimationFrame 回调 进行样式计算(Style) 布局计算(Layout) 绘制(Paint) 合成(Composite) 如果还有时间 → 执行 requestIdleCallback 需要明确一点: rAF 是“下一帧渲染前”的回调 rIC 是“当前帧剩余时间”的回调 二、requestAnimationFrame 的执行时机 requestAnimationFrame 的设计目标是: 在浏览器即将进行下一次重绘之前执行回调。 特点: 每帧最多执行一次 与刷新率同步 在样式计算之前触发 如果标签页不可见,会暂停 执行时机可以理解为: ...

leetcode 面试题

共 38 道 leetcode 面试题。答案默认折叠,便于先自行作答。 1. 最大公共前缀 难度:2 · 类型:QA 编写一个函数,接收一个字符串数组作为输入,找出这些字符串的最大公共前缀。如果没有公共前缀,则返回空字符串。 输入输出要求: 输入:一个字符串数组。 输出:一个字符串,表示最大公共前缀。 示例: const input = ["flower", "flow", "flight"]; const result = longestCommonPrefix(input); console.log(result); // 输出:'fl' 题目要点 最大公共前缀问题的核心是逐步缩小候选前缀;横向扫描通过不断用后续字符串修剪前缀,实现简单且高效;在最坏情况下时间复杂度为 O(n·m),空间复杂度为 O(1),非常适合在实际工程和面试中使用。 参考答案 这个问题本质是在多个字符串之间求一个公共的、连续的前缀子串,并且要求尽可能长。关键不在于字符串操作技巧,而在于如何逐步收敛搜索空间。 一、整体思路说明(横向扫描) 横向扫描的核心思想是: 先假设第一个字符串是公共前缀,然后不断用后续字符串去“修剪”它。 执行过程可以概括为: 取第一个字符串作为初始前缀 从第二个字符串开始,逐个比较 如果当前字符串不以该前缀开头,就不断缩短前缀 一旦前缀缩短为空,说明不存在公共前缀,可以提前结束 这个过程的好处是逻辑直观,且在工程中可读性和可维护性都很好。 二、示例代码实现(横向扫描) function longestCommonPrefix(strs) { if (!strs || strs.length === 0) return ""; let prefix = strs[0]; for (let i = 1; i < strs.length; i++) { while (!strs[i].startsWith(prefix)) { prefix = prefix.slice(0, -1); if (prefix === "") return ""; } } return prefix; } 示例验证 const input = ["flower", "flow", "flight"]; console.log(longestCommonPrefix(input)); // "fl" 三、时间与空间复杂度分析 时间复杂度: 最坏情况下为 O(n * m) ...

Node.js 面试题

共 40 道 Node.js 面试题。答案默认折叠,便于先自行作答。 1. NestJs、Nust.js、Next.js 这几个框架有什么区别 难度:2 · 类型:QA 题目要点 NestJS 是 Node.js 的企业级服务端框架,强调模块化、依赖注入和 TypeScript,适合构建 API 和微服务; Nuxt.js 是 Vue 生态的全栈框架,在 Vue 基础上提供 SSR、SSG、文件路由等能力; Next.js 是 React 生态的全栈框架,除了 SSR、SSG 外,还支持 React Server Components、Server Actions 等现代特性。 三者最大的区别在于定位不同:NestJS 负责后端服务,Nuxt.js 和 Next.js 负责全栈 Web 应用开发,分别服务于 Vue 和 React 技术栈。 参考答案 这几个框架名字非常相似,但定位完全不同。简单来说: NestJS:Node.js 服务端开发框架。 Nuxt.js:Vue 生态的全栈应用框架。 Next.js:React 生态的全栈应用框架。 三者分别解决的是不同层面的问题,并不存在直接竞争关系。 先来看 NestJS。 NestJS 是基于 Node.js 构建的服务端框架,底层默认使用 Express,也可以切换到 Fastify。它借鉴了很多 Java Spring 的设计思想,例如依赖注入(DI)、模块化、装饰器以及 IoC 容器,因此对于大型团队开发来说,代码组织会更加规范。 ...

React.js 面试题

共 148 道 React.js 面试题。答案默认折叠,便于先自行作答。 1. 说说 Hooks 的依赖数组原理 难度:3 · 类型:QA 题目要点 Hooks 的依赖数组本质是一次浅层引用比较机制。React 在 commit 阶段通过 Object.is 逐项比较新旧依赖数组,决定是否重新执行 effect,并在变化时先执行 cleanup。依赖数组不会监听变量变化,也不负责触发 render,只用于声明副作用与状态之间的关系。依赖缺失会导致闭包捕获旧值,依赖不稳定会导致 effect 频繁执行,因此依赖必须完整且保持引用稳定。 参考答案 这个问题的核心,不是“依赖数组怎么写”,而是:React 是如何判断副作用是否需要重新执行的。 依赖数组的存在,本质是为了让 React 在两次 render 之间进行一次“浅层比较(shallow compare)”,从而决定是否跳过本次 effect 的执行。 一、从 useEffect 的执行时机说起 useEffect(() => { // effect }, [a, b]); React 在每次 render 结束后,会把 effect 收集到 Fiber 节点的 effect 链表中。 当进入 commit 阶段时,React 会: 取出当前 Fiber 上一次保存的依赖数组 与本次 render 生成的新依赖数组进行比较 决定是否标记该 effect 需要执行 如果依赖变化,则: ...