WEEK 01 / HANDS-ON

7 天,做出你的第一个 LLM 聊天应用

面向已有 React / TypeScript 基础的前端开发者。目标不是“看懂概念”,而是亲手完成一个支持流式回复、上下文对话和工具调用的可运行项目。

16-WEEK / PORTFOLIO-FIRST

从业务前端,到 AI 应用工程师

一条以作品为中心的 16 周路线。点击任意技能,查看精选资料、实战任务和可验证的完成标准。

每周 8–10 小时
17 个技能节点

前端工程补强

3 个节点
01TypeScript 工程化必修
所属分类:前端工程补强前置技能:起点技能

实战任务

把模型返回、工具参数与业务状态类型化

验收指标

为聊天消息、模型配置、工具调用定义类型;核心代码不使用 any

02流式交互与状态管理必修
所属分类:前端工程补强前置技能:起点技能

实战任务

实现 SSE、停止生成、重试和会话状态

验收指标

可停止、可重试的流式聊天页,刷新后会话保留

03Electron 与 IPC重点
所属分类:前端工程补强前置技能:TypeScript 工程化

实战任务

把 Web 前端扩展为可安装桌面应用

验收指标

解释主/渲染进程边界;受限 IPC 调用本地能力并打包

LLM 应用基础

3 个节点
04LLM API 与流式输出必修
所属分类:LLM 应用基础前置技能:TypeScript 工程化、流式交互与状态管理

实战任务

掌握消息、Token、流式响应和模型切换

验收指标

同一界面可切换两种模型;正确处理限流、超时、鉴权错误

05Prompt 工程必修
所属分类:LLM 应用基础前置技能:LLM API 与流式输出

实战任务

设计清晰指令、示例、边界和结构化输出

验收指标

20 条固定测试样例;结构化输出解析成功率 95%

06Tool Calling核心
所属分类:LLM 应用基础前置技能:LLM API 与流式输出、Prompt 工程

实战任务

让模型可靠调用真实业务函数

验收指标

实现天气查询、知识搜索、创建待办三个工具并展示调用过程

RAG 知识库

3 个节点
07Embedding 与向量检索核心
所属分类:RAG 知识库前置技能:文档解析与 Chunking

实战任务

理解语义向量、相似度与 Top-K 召回

验收指标

30 个问题评测集,Top-5 召回命中率 80%

08文档解析与 Chunking重点
所属分类:RAG 知识库前置技能:起点技能

实战任务

处理 PDF、Markdown、TXT 并合理切分

验收指标

支持三种文件;能用数据解释块大小、重叠长度为何这样设置

09引用溯源与 RAG 评估核心
所属分类:RAG 知识库前置技能:Embedding 与向量检索、文档解析与 Chunking

实战任务

衡量召回、忠实度与拒答表现

验收指标

回答展示原文引用;无答案时拒答;每次改动可重复跑评测

Agent 与工作流

3 个节点
10LangGraph.js核心
所属分类:Agent 与工作流前置技能:Tool Calling

实战任务

用图组织状态、节点、路由和循环

验收指标

实现至少 5 个节点的状态图;能从日志还原每次状态变化

11Memory 与上下文管理重点
所属分类:Agent 与工作流前置技能:LangGraph.js

实战任务

处理短期记忆、摘要和持久会话

验收指标

长对话 Token 不持续线性增长;新会话与历史会话隔离

12Agent 可靠性与评估核心
所属分类:Agent 与工作流前置技能:LangGraph.js、Memory 与上下文管理

实战任务

为失败、重试、人工确认设计机制

验收指标

固定 20 个业务任务成功率 80%;敏感动作执行前要求人工确认

企业集成与工程化

3 个节点
13Node.js BFF必修
所属分类:企业集成与工程化前置技能:TypeScript 工程化、Tool Calling

实战任务

集中处理模型鉴权、业务编排和数据适配

验收指标

API Key 不进入浏览器;接入两个模拟 CRM/OA 接口并统一错误格式

14Ollama 本地模型加分
所属分类:企业集成与工程化前置技能:起点技能

实战任务

在隐私或离线场景运行开源模型

验收指标

云端不可用时可切本地模型;明确显示能力差异与运行状态

15Docker 与 CI/CD加分
所属分类:企业集成与工程化前置技能:起点技能

实战任务

让项目可复现、可自动检查和发布

验收指标

Docker Compose 一条命令启动;每次提交自动执行 lint、测试和构建

作品集与求职

2 个节点
16企业知识助手(毕业项目)毕业项目
所属分类:作品集与求职前置技能:引用溯源与 RAG 评估、Agent 可靠性与评估、Node.js BFF、Electron 与 IPC、Ollama 本地模型、Docker 与 CI/CD

实战任务

将前五个阶段合并成一条业务闭环

验收指标

支持文档问答、引用、3 个工具、多步流程、日志、模型切换和桌面打包

17项目表达与面试必修
所属分类:作品集与求职前置技能:企业知识助手

实战任务

用架构、数据和 Demo 证明真实能力

验收指标

陌生人按 README 10 分钟跑通;10 分钟讲清架构;3 分钟视频展示闭环

CAPSTONE

企业 AI 知识助手

不要做六个零散 Demo。把所有技能集中进一个能被面试官实际操作、追问和验证的产品。

React + TypeScriptElectronNode.js BFFLangGraph.jsChromaOllama
产品界面流式对话 · 文件上传
Agent状态图 · 工具调用
企业数据知识库 · CRM · OA

EXECUTION RULES

防止学着学着放弃

  1. 功能先于课程只学习当前功能需要的内容。每周必须留下一个可运行的功能。
  2. 16 周不换栈主技术栈固定。遇到问题先查文档,不因为新框架重新开始。
  3. 指标代替感觉不用“我看懂了”判断进度,只看测试集、成功率与可复现结果。
00

先看全景:你要成为怎样的 AI 前端

岗位目标不只是“会调一个模型接口”,而是能把模型、Agent、知识库与产品界面组合成稳定可交付的应用。本教程先攻下第 3 项,其余模块按依赖顺序继续推进。

AI Agent 开发工具、记忆、规划与可靠执行
AI 数字员工把业务流程编排成可复用工作流
大模型应用集成连接模型 API、本地模型与产品界面
持续优化与研究评估效果、降低成本、跟进技术演进
01已有基础

前端基础

HTML / CSS / JavaScript、React / Vue、TypeScript、状态管理与工程化。

02后续阶段

Electron 桌面端

主进程与渲染进程、IPC 通信、安全边界、打包与分发。

03本阶段 · 7 天

大模型应用

OpenAI / 通义 / 文心 / DeepSeek API,Ollama / llama.cpp 本地模型,Prompt、流式响应与上下文管理。

04后续阶段

Agent

LangChain、LangGraph、AutoGen、CrewAI,工具调用、多步骤编排与可靠性。

05后续阶段

RAG 与知识库

Embedding、文档解析切分、Milvus / Chroma / Weaviate、向量检索与效果评估。

06后续阶段

后端与工程化

Node.js BFF、Docker、CI/CD 与 Python 服务,完成可部署交付。

07贯穿全程

UI 与响应式

聊天交互、加载与错误状态、桌面与移动端适配、可访问性。

BONUS作品加分

AI 产品实践

AI 编程助手、智能搜索、内容生成;从真实场景出发沉淀作品。

审查

按 Merrill 五条原则逐项补课

先公开原版课程的缺口,再说明这次如何修复。你可以用这张表反查后面的内容,而不是只相信“已经优化”。

任务中心原版已有7 天围绕一个可运行聊天应用推进;保留主线,并把每天的产出写成可验证结果。
激活旧知原版缺失新增“前端类比”:token 对照字符编码、上下文窗口对照内存预算、SSE 对照 WebSocket、Function Calling 对照事件回调。
演示原版偏弱代码仍可一键复制;每段代码后新增“为什么这样做”,解释数据流、安全边界和失败方式。
应用原版不足删除选择题式自测,改为改参数、制造错误、中断请求、完成工具闭环等动手任务,并要求记录证据。
融入实际原版缺失阶段结尾新增 JD 对齐、面试问法与作品证据,帮助你把“做过”转成“能讲清”。
01

一周路线图

第 0 天建立底层模型,随后保留原来的 7 天实作结构。每天只解决一个问题;当天代码能跑,再进入下一天。

DAY 00 · 开课前

先画出“模型如何产生一个回答”

从文字被分词开始,走过上下文、概率分布、采样、SSE 传输与客户端执行循环。完成第 0 天后,再进入下面 7 天实作。

DAY 01打通 APIcurl 看见真实回复
DAY 02安全代理Key 留在服务端
DAY 03流式输出逐字渲染回答
DAY 04多轮对话维护 messages
DAY 05工具声明模型决定何时调用
DAY 06闭环执行结果回传模型
DAY 07整理作品验收、README、复盘
学习原则

复制代码是起点,不是终点。每跑通一段,都改一个变量、制造一次错误、再自己修复,才能建立真正的调试能力。

DAY 00

底层概念:模型到底在做什么

先建立一张因果图:文本被切成 token → token 进入有限上下文 → 模型计算下一个 token 的概率 → 采样得到输出 → 客户端接收流并决定是否执行工具。

1. Token 与分词

前端类比:字符编码 + 编译器词法分析

模型不直接读取“字”或“单词”,而是把文本映射成词表里的整数 ID。一个 token 可能是一个汉字、词片段、标点或空格;同一句话在不同 tokenizer 下数量可能不同。模型真正处理的是 ID 序列,因此费用、延迟和上下文占用通常按 token 计算,而不是按字符数计算。

因果链:更长输入 → 更多 token → 更多注意力计算与上下文占用 → 成本和延迟上升。字符数只能粗估,生产中应读取 API 返回的 usage。

2. 上下文窗口

前端类比:内存预算,不是硬盘

上下文窗口是一次推理中可见的 token 总预算,通常同时包含 system、历史消息、工具定义、检索内容和待生成输出。模型没有自动读取你上一次请求的“长期记忆”;你不重发,它就看不见。

  • 历史越长,留给本次输出的空间越少。
  • 超过限制时,请求会失败或内容被截断;不能只靠“无限追加 messages”。
  • 工程做法是预算、裁剪、摘要或检索,而不是把所有内容都塞进去。

3. Temperature / top_p

前端类比:对候选列表先改权重,再过滤

模型先为“下一个 token”产生一组分数(logits),经 softmax 变成概率。temperature 缩放分数:接近 0 时分布更尖、更偏向最高概率;升高后分布更平,低概率候选更有机会。top_p 则从高到低保留累计概率达到阈值的最小候选集合,再在其中采样。

不要误解:temperature 不是“创造力按钮”,top_p 也不提高事实准确率。它们只改变抽样分布;同样输入仍可能因采样或服务端实现而变化。调试时通常固定一个、只改另一个。

4. 系统提示词

前端类比:高优先级配置 + 接口契约

聊天模型在训练阶段学会区分 system、user、assistant 等角色,因此 system 内容会作为高优先级条件影响后续 token 的概率分布。它有效,是因为模型被训练去遵循这种消息结构,不是因为存在一个绝对安全的“系统权限”。

  • 它适合声明角色、边界、输出结构与决策规则。
  • 它不能替代参数校验、鉴权或敏感操作确认。
  • 清晰的约束能提高命中率,但提示注入仍需程序侧防守。

5. SSE 的本质

前端类比:单向事件流,对照 WebSocket 双向连接

SSE 是一个保持打开的 HTTP 响应,服务端按 data: 事件持续写入文本,客户端边读边渲染。聊天生成主要是服务端向客户端单向推送,因此无需为双向实时通信引入 WebSocket 的协议和连接管理成本。

关键边界:网络 chunk 不等于 SSE 事件,更不等于 token。UTF-8 字节、事件行和 JSON 都可能被拆开,所以客户端需要 TextDecoder 与 buffer。

6. Function Calling

前端类比:事件回调 + dispatcher

模型不会进入你的运行时调用函数。它只是根据工具 schema 生成结构化文本:工具名和参数。客户端像事件分发器一样校验意图、调用白名单函数、把结果作为 tool 消息回传,再让模型生成最终回答。

闭环:模型提议 → 程序校验 → 人类按需确认 → 程序执行 → 结果回传 → 模型组织答案。权限永远在程序和人手里。
动手任务 0

用一张纸画出一次请求的完整链路

  1. 画出文本、token ID、上下文窗口、概率分布、SSE chunk、tool_call 六个节点。
  2. 在每条箭头上写出“谁产生它、谁消费它”。
  3. 标出三个不能信任的边界:用户输入、模型输出、网络分块。

完成证据:你能不看页面,用 2 分钟向面试官解释这张图,并说明为什么 Function Calling 不是模型执行代码。

02

准备环境

我们采用 React + Vite 做界面,Node.js 做轻量代理。浏览器永远不直接持有 API Key。

开始前确认

  • Node.js 20 或更高版本
  • 一个可用的 DeepSeek API Key
  • VS Code 与终端
  • 会使用基本的 Git 命令

项目结构

src/ 放 React 界面,server/ 放代理接口,.env.local 只存本机密钥。前后端分开,是为了避免把密钥打进浏览器代码。

1. 创建项目

TERMINAL
npm create vite@latest llm-chat -- --template react-ts
cd llm-chat
npm install
npm install express cors dotenv
npm install -D tsx @types/express @types/cors
mkdir server
为什么这样做

Vite 提供最小 React + TypeScript 客户端;Express 只承担密钥隔离和流转发。先把浏览器职责与可信服务端边界分开,后面替换模型供应商时无需改界面。

2. 保存密钥

.env.local
DEEPSEEK_API_KEY=替换成你的密钥
为什么这样做

.env.local 是运行时配置,不是前端源码。它让服务端读取密钥,而不会被 Vite 打包进可下载的 JavaScript;这和前端不能保存数据库密码是同一条信任边界。

密钥红线

不要写进 src/,不要使用 VITE_ 前缀,也不要提交到 Git。确认 .gitignore 包含 .env*。

动手任务 1

证明密钥没有进入前端包

  1. 运行 npm run build。
  2. 在 dist/ 内搜索密钥前 6 位和 DEEPSEEK_API_KEY。
  3. 两者都应搜不到;若搜到,删除前端引用并重新构建。

完成证据:保留终端“0 个匹配”的截图,写进 README 的安全说明。

03

第一次调用:先证明链路通了

Day 1 不写 UI。先用 curl 验证密钥、网络、模型名和请求格式都正确。

TERMINAL · macOS / Linux
# 让当前终端加载 .env.local,再执行请求
set -a
source .env.local
set +a

curl https://api.deepseek.com/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $DEEPSEEK_API_KEY" \
  -d '{
    "model": "deepseek-chat",
    "messages": [
      {"role": "system", "content": "你是一个简洁的编程助教。"},
      {"role": "user", "content": "用一句话解释什么是 token。"}
    ],
    "stream": false
  }'
为什么这样做

source .env.local 先把密钥载入当前 shell;文件本身不会自动变成环境变量。再用 curl 绕过 UI 与状态管理,可把故障范围缩到鉴权、网络、模型名或请求体。PowerShell 可先运行 $env:DEEPSEEK_API_KEY="你的密钥"。

成功标准

HTTP 200,返回 JSON,并且 choices[0].message.content 中出现回答。

出错先看这里

401 检查 Key;429 检查余额或频率;连接失败先排查网络与代理;400 对照响应里的错误信息检查请求体。

可选练习:换成本地模型

如果电脑配置允许,可用 Ollama 先跑一个小模型,理解“云端 API”与“本地推理”的差别。llama.cpp 适合后续进一步研究量化格式与推理参数。

TERMINAL · OLLAMA
ollama pull qwen2.5:3b
ollama run qwen2.5:3b "用一句话解释什么是 token"

# 查看本地 OpenAI 兼容接口是否可用
curl http://localhost:11434/api/chat \
  -d '{"model":"qwen2.5:3b","messages":[{"role":"user","content":"你好"}]}'
为什么这样做

本地模型把“网络服务质量”和“模型推理能力”拆开观察。相同 messages 结构可以帮助你比较云端与本地的延迟、输出质量和资源成本,而不是把模型名写死在 React 组件里。

同一套前端,不同模型来源

把模型地址、模型名和鉴权方式放进服务端配置层,React 侧只调用自己的 /api/chat,以后切换通义、文心、OpenAI 或本地模型时不用重写 UI。

动手任务 2

只改采样参数,比较输出分布

  1. 固定同一个开放题,先设置 temperature: 0,连续请求 3 次。
  2. 改成 temperature: 1,再请求 3 次;其余参数保持不变。
  3. 记录措辞重复度与事实偏差,并解释“分布更平”如何导致差异。

完成证据:一张 6 次输出对照表。不要只写“更有创造力”,要指出哪些 token 选择变得不稳定。

04

把流接进 React

关键路径只有三段:浏览器发消息 → Node 转发请求 → 浏览器逐块读取 SSE。

React
POST /api/chat
→
Node 代理
隐藏 API Key
→
LLM API
SSE 数据流

1. 服务端代理

server/index.ts
import express from "express";
import cors from "cors";
import dotenv from "dotenv";

dotenv.config({ path: ".env.local" });
const app = express();
app.use(cors());
app.use(express.json());

app.post("/api/chat", async (req, res) => {
  try {
    const upstream = await fetch(
      "https://api.deepseek.com/chat/completions",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.DEEPSEEK_API_KEY}`,
        },
        body: JSON.stringify({
          model: "deepseek-chat",
          messages: req.body.messages,
          stream: true,
        }),
      }
    );

    if (!upstream.ok || !upstream.body) {
      return res.status(upstream.status).send(await upstream.text());
    }

    res.setHeader("Content-Type", "text/event-stream");
    const reader = upstream.body.getReader();
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      res.write(Buffer.from(value));
    }
    res.end();
  } catch (error) {
    res.status(500).json({ error: "代理请求失败" });
  }
});

app.listen(3001, () => console.log("API: http://localhost:3001"));
为什么这样做

代理不解析模型内容,只保管密钥、检查上游状态并把字节流向下游转发。res.write 保持响应开放,避免等待完整回答;upstream.ok 则阻止把错误页伪装成正常 SSE。

新开一个终端运行 npx tsx server/index.ts。浏览器端请求 http://localhost:3001/api/chat。

2. 客户端读取 SSE

核心读取函数 · src/App.tsx
async function streamReply(messages: Message[], onToken: (t: string) => void) {
  const response = await fetch("http://localhost:3001/api/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ messages }),
  });
  if (!response.ok || !response.body) throw new Error("请求失败");

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() ?? "";

    for (const line of lines) {
      if (!line.startsWith("data: ")) continue;
      const data = line.slice(6);
      if (data === "[DONE]") return;
      const token = JSON.parse(data).choices[0]?.delta?.content;
      if (token) onToken(token);
    }
  }
}
为什么这样做

getReader() 读取的是字节块,TextDecoder 负责保持跨 chunk 的 UTF-8 解码状态,buffer 负责保持跨 chunk 的事件状态。两层状态都保留,中文字符或 JSON 被截断时才不会随机报错。

为什么要保留 buffer?

网络分块不保证刚好按一行切开。一个 JSON 片段可能跨两个 chunk;先累积,再按换行取完整事件,才能避免随机解析失败。

动手任务 3

故意打断一次流式请求

  1. 给 fetch 传入 AbortController.signal,界面增加“停止生成”。
  2. 回答生成到一半时点击停止,捕获 AbortError,保留已收到文本并标记“已中断”。
  3. 再次发送消息,确认 loading 状态、reader 和按钮都已恢复。

完成证据:录一段 15 秒视频,包含生成、停止、显示中断态、再次发送四步。

05

多轮对话与基本体验

对话上下文不是模型自动记住的;每次请求都要把需要的历史消息一起发送。

src/App.tsx · 完整可运行组件
import { useState, type FormEvent } from "react";

type Message = {
  role: "system" | "user" | "assistant";
  content: string;
};

async function streamReply(messages: Message[], onToken: (token: string) => void) {
  const response = await fetch("http://localhost:3001/api/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ messages }),
  });
  if (!response.ok || !response.body) throw new Error("请求失败");

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop() ?? "";
    for (const line of lines) {
      if (!line.startsWith("data: ")) continue;
      const data = line.slice(6);
      if (data === "[DONE]") return;
      const token = JSON.parse(data).choices[0]?.delta?.content;
      if (token) onToken(token);
    }
  }
}

export default function App() {
  const [messages, setMessages] = useState<Message[]>([
    { role: "system", content: "你是简洁、准确的编程助教。" },
  ]);
  const [input, setInput] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  async function send(event: FormEvent) {
    event.preventDefault();
    const text = input.trim();
    if (!text || loading) return;

    const userMessage: Message = { role: "user", content: text };
    const next: Message[] = [...messages, userMessage];
    setInput("");
    setError("");
    setLoading(true);
    setMessages([...next, { role: "assistant", content: "" }]);

    try {
      await streamReply(next, (token) => {
        setMessages((current) => current.map((message, index) =>
          index === current.length - 1
            ? { ...message, content: message.content + token }
            : message
        ));
      });
    } catch (cause) {
      setError(cause instanceof Error ? cause.message : "未知错误");
    } finally {
      setLoading(false);
    }
  }

  return (
    <main>
      {messages.filter((message) => message.role !== "system").map((message, index) => (
        <p key={index}><b>{message.role}:</b> {message.content}</p>
      ))}
      {error && <p role="alert">{error}</p>}
      <form onSubmit={send}>
        <input value={input} onChange={(event) => setInput(event.target.value)} disabled={loading} />
        <button disabled={loading || !input.trim()}>{loading ? "生成中…" : "发送"}</button>
      </form>
    </main>
  );
}
为什么这样做

messages 是发给模型的临时工作内存,类似 Redux 中的显式状态:每次请求都提交一个确定快照。先插入空 assistant 项,再逐 token 更新最后一项,可让 UI 只维护一条正在增长的消息。

Prompt 不是“咒语”,而是接口契约

先写清角色、任务、约束与输出格式,再用输入样例测试。提示词和代码一样要能版本化、能回归验证。

system-prompt.ts
export const systemPrompt = `你是 TypeScript 编程助教。
任务:解释用户给出的代码问题,并给出可运行的最小修改。
约束:不猜测缺失信息;不确定时明确说明;不暴露密钥。
输出:先给结论,再给代码,最后列出 1 条验证命令。`;
为什么这样做

角色、任务、约束、格式分别减少不同的不确定性,相当于给模型定义 TypeScript 接口;但模型输出仍是概率结果,所以“接口契约”需要测试与校验,不能当成编译器保证。

最低体验线

  • 发送中禁用输入与按钮
  • 请求失败时显示可读错误
  • 允许中止本次生成
  • 空消息不能发送

先别急着做

登录、数据库、Markdown 高亮、聊天记录持久化都可以后补。第一周最重要的是请求链路与状态管理清楚。

动手任务 4

把上下文窗口当作内存预算

  1. 连续对话至少 10 轮,记录每次 usage 中的输入 token。
  2. 设置一个输入预算,超限时只保留 system、最近消息和旧消息摘要。
  3. 追问第一轮细节,比较裁剪前后模型能否回答,并解释取舍。

完成证据:README 写出你的裁剪规则、预算值和一个“丢失信息”的已知限制。

06

Function Calling:让模型学会“请求工具”

模型不会直接执行函数。它只输出一个结构化的调用意图,你的代码验证参数、执行函数,再把结果交回模型。

用户提问
→
模型返回 tool_call
→
代码执行并回传结果

声明一个安全、可验证的工具

tools.ts
const tools = [{
  type: "function",
  function: {
    name: "calculate",
    description: "计算两个数字的加减乘除",
    parameters: {
      type: "object",
      properties: {
        a: { type: "number" },
        b: { type: "number" },
        operation: {
          type: "string",
          enum: ["add", "subtract", "multiply", "divide"]
        }
      },
      required: ["a", "b", "operation"]
    }
  }
}];
为什么这样做

schema 是模型与执行器共享的调用协议,类似前端事件的 payload 类型。枚举收窄候选动作,required 保证必要字段存在;真正运行前仍要在服务端做运行时校验,因为模型生成的 JSON 不受 TypeScript 类型系统保护。

不要把模型输出当成可信输入

执行前必须校验工具名、参数类型与范围。涉及写文件、发消息、付款等操作时,还需要明确的人类确认;不要让模型自由拼接并执行 shell 命令。

你要理解的闭环

  1. 第一次请求携带 tools。
  2. 若响应包含 tool_calls,由你的代码解析参数并执行对应函数。
  3. 把 assistant 的调用消息和 role 为 tool 的结果都追加进 messages。
  4. 第二次请求让模型根据工具结果组织自然语言答案。
动手任务 5

让工具失败一次,再完成闭环

  1. 让用户输入“10 除以 0”,在执行器中拒绝非法参数。
  2. 把结构化错误作为 tool 消息回传,要求模型解释失败原因,而不是伪造结果。
  3. 再用正常参数运行一次,并在日志中打印 tool_call、校验结果和最终回答。

完成证据:保留成功与失败两条 trace;你要能指出模型负责决策、程序负责执行的边界。

07

第 7 天:按作品标准验收

能运行只是起点。把项目整理到陌生人 5 分钟内可以理解、启动和验证。

功能验收

  • 首次请求与多轮对话正常
  • 回答逐步出现,不等完整响应
  • 网络异常有明确提示
  • API Key 不出现在前端包与 Git
  • 计算工具能完成一次完整调用

README 必须写

  • 项目解决什么问题
  • 架构图与技术选型
  • 本地启动步骤
  • 环境变量说明
  • 截图、已知限制与下一步

最终动手验收

动手任务 6

从空仓库复现一次,不看答案修一个故障

  1. 只看 README,在新目录完成安装、配置与启动。
  2. 主动制造一个错误:无效 Key、SSE 半包、请求中断或非法工具参数任选其一。
  3. 用日志定位并修复;在 README 写“现象 → 假设 → 证据 → 修复”。

完成证据:一个可运行仓库、一段演示视频、一条有分析过程的故障记录。它们比答对选择题更能证明能力。

本阶段面试考点 · 大模型应用

对应 JD:大模型 API 接入、Prompt 工程、流式响应、上下文管理、Function Calling、React/TypeScript 工程化与错误处理。

题库范围:LLM 工具调用 16 题;大模型工程中的采样参数、Prompt、CoT、幻觉、KV Cache / Prompt Caching 与 vLLM / llama.cpp 部署选型。

面试官会问:为什么用 SSE 而不是 WebSocket?上下文窗口满了怎么处理?模型真的执行了函数吗?如何防止 API Key 泄露?temperature 与 top_p 分别改变什么?

回答结构:先讲底层机制,再讲你的选型与失败案例,最后展示 trace、README 或演示链接。不要只背定义。

NEXT

第二个里程碑:RAG 知识库

在这个项目上继续增加文档切分、Embedding、向量检索和引用来源。不要另起炉灶——让作品沿着一条清晰路线持续进化。

FDE

FDE 方向:AI 落地的最后一公里

前面 7 天你学会了把模型接进产品;FDE 解决的是下一个问题——把 AI 送进真实业务,让它产生可验证的结果。这是目前西安 AI 岗里薪资天花板最高、和你转型路线最贴合的方向。

一句话定位:Forward Deployed Engineer,以客户业务结果为目标、仍然直接写生产代码的工程岗。日常不在训练模型,而在做四件事:发现场景、研判可行性、跑通 POC、推广复制。和面试备战 · FDE 专题配合看:那边是面试怎么答,这里是能力怎么练。

四项核心能力(对标真实 JD)

能力在干什么对应你教程里的模块
场景洞察把“我们想用 AI”翻译成明确的用户、流程、约束和可验证目标。DAY 00 底层概念(理解模型能力边界)+阶段一作品的需求定义
技术可行性研判与选型效果评估、方案选型:RAG 还是微调?Agent 还是工作流?成本时延能不能接受?RAG(切分/召回/重排/评测)+ Agent(任务拆分/记忆/规划)+部署选型
POC 落地与闭环结合场景跑通创新试点,收集业务反馈,跟进问题闭环、提升效果。第 7 天验收标准(陌生人 5 分钟可理解/启动/验证)+故障复现记录
推广布道沉淀场景案例与落地经验,把方案在更大范围复制推广。README(架构/选型/限制/下一步)+演示视频

真实 JD 拆解:三一 AI FDE 工程师(25–50K,base 可选西安)

JD 要什么

  • 计算机相关专业,或能借助 AI 进行开发
  • 熟悉垂直业务场景(制造/HR/财务/销售),理解模型在该场景的能力边界
  • 基础技术/产品思维,自驱学习前沿 AI
  • 跨角色沟通、协同与 AI 研发项目管理能力

你现在的牌

  • 前端工程能力+正在补 AI 开发——对上“能借助 AI 进行开发”
  • 充电站配套项目=垂直场景经验,面试讲能源数字化故事
  • 每天在学,有自驱证据(这个教程站本身就是证据)
  • 外包对接业务方的协同经验,可以包装
差距清单

① 评测体系:给 AI 方案定指标(任务成功率/时延/成本/采用率)并做对比验证——RAG 评测模块补上;② POC 方法论:小流量试点、基线对照、回滚条件,写成你自己的检查表;③ 业务语言:把技术方案翻译成业务收益,这是 FDE 面试必考的。学历标签(硕士)先直接问招聘者确认,不要自己脑补被卡。

薪资锚点(西安实测,2026-10-19)

猎聘全国口径 FDE 平均年薪 40.8 万;西安实测:联东集团 AI Agent 工程师 25–40k·15薪、西安博达软件大模型应用开发 18–28k·13薪、道通科技 Agent 应用工程师 11–29k·13薪。对比纯前端中枢 1.0–1.4 万/月——这就是 A 轨(AI 应用/FDE)薪资天花板更高的实测证据。

动手任务 7

给充电站场景做一次 FDE 式 POC 复盘

  1. 选一个你做过的充电站业务痛点(如运维工单分类、场站异常问答),写出场景洞察:用户是谁、高频流程是什么、约束有哪些。
  2. 做可行性研判:RAG 还是 Agent?画出数据边界,定一个可验证的指标(如工单分类准确率、平均响应时长)。
  3. 用 Dify 或 LangGraph 跑一个最小 POC,记录一次失败和修复过程。
  4. 写一页“推广复盘”:这个方案复制到第二个场站需要改什么、成本多少。

完成证据:一页场景洞察文档、一个可运行的 POC、一条故障记录、一页推广复盘。拿着这四样去面 FDE,比“我学过 LangChain”有说服力得多。

FDE 面试题与答题思路见:面试备战 · FDE 专题 · 完整 30+ 道见 Forward Deployed Engineer 专题

面试

面试备战:74 道题按学习阶段消化

这里是题目索引和一句话答题方向,不搬运原站答案。先用项目经验组织自己的答案,再回到原文看图解与追问。

WEEK 1Agent · 16 题

先建立任务循环、记忆、规划和多 Agent 的整体框架。

WEEK 2–3RAG · 20 题

沿索引、检索、生成、评测顺序,把知识库项目讲完整。

WEEK 4工具调用 · 16 题

把 Function Calling、MCP、Skill、协议和网关串成一张图。

三步刷题法

先看“面试翻车现场”,明确答不清会被追问什么;再看图解,建立因果链;最后合上页面,用“定义 → 取舍 → 项目证据”复述。应用开发岗主攻 Agent、RAG、工具调用,约 1 个月完成首轮。

阶段与题库对齐

阶段一 · 大模型应用:工具调用 16 题,加大模型工程中的采样参数、Prompt、CoT、幻觉、KV Cache、部署选型。

阶段二 · RAG:RAG 20 题,答案必须带你的切分、召回、重排和评测证据。

阶段三 · Agent:Agent 16 题,重点讲任务拆分、记忆、规划、反思和多 Agent 选型。

算法岗进阶选看:Transformer、Flash Attention、RoPE、LoRA、RLHF / DPO / GRPO、量化、MoE;应用岗先会解释边界,不必先钻训练公式。

Agent 面试题 16 题 · 阶段三
  1. 什么是 Agent?与大模型有什么本质不同?从“单次生成”与“围绕目标感知、决策、行动、反馈的循环”对比。
  2. Agent 的基本架构由哪些核心组件构成?按模型、工具、记忆、规划器、状态与观测说明数据如何流动。
  3. Workflow、Agent、Tools 三者有什么区别?用确定流程、动态决策者、可执行能力三个层次回答边界。
  4. 还了解哪些 Agent 设计范式?Agent 和 Workflow 如何区分?列 ReAct、Plan-and-Execute、Reflection,并用可预测性与自治度做选型。
  5. Agent 推理模式有哪些?ReAct 如何实现?讲清思考、行动、观察循环,以及终止条件和工具结果回灌。
  6. ReAct、Plan-and-Execute、Reflection 如何选型?从任务长度、可恢复性、延迟成本和错误自纠需求权衡。
  7. 复杂任务为什么要拆分?怎样提升效果?先定义子目标与依赖,再用状态、验证器和失败重试控制执行。
  8. Agent 的记忆机制如何设计?区分会话状态、长期事实和外部知识,并说明写入与召回策略。
  9. 长短期记忆怎么存、粒度多大、怎么用?用消息窗口、摘要、向量记忆对比时效性、精度与成本。
  10. 什么是 Multi-Agent?说明多个有角色与状态的 Agent 如何分工、通信和汇总结果。
  11. Single-Agent 和 Multi-Agent 怎么选?优先单 Agent,只有专业分工、并行或隔离确有收益时再拆分。
  12. Agent 记忆压缩有哪些方法?比较滑动窗口、摘要、重要性筛选和检索,并说明信息损失。
  13. 为什么有时手搓 Agent,而不用成熟框架?从控制力、可观测性、依赖成本与复杂度说明取舍。
  14. 如何赋予 LLM 规划能力?让模型产出可验证计划,用状态机逐步执行并动态重规划。
  15. Agent 的反思机制为什么有效,如何实现?用生成、批评、修订循环解释收益,同时指出延迟与自我确认偏差。
  16. 如何设计多 Agent 协作与动态切换?讲路由规则、共享状态、交接协议、超时降级和冲突治理。

来源:小林面试笔记 · AI 大模型面试题

RAG 面试题 20 题 · 阶段二
  1. 什么是 RAG?完整流程是什么?按采集、解析、切分、索引、检索、重排、生成与引用闭环回答。
  2. RAG 主要解决大模型哪些问题?聚焦知识时效、私域知识、可追溯性和降低无依据生成。
  3. RAG 与微调各自优劣是什么?用“注入知识”与“改变行为/能力”区分,并比较成本与更新速度。
  4. 文档怎么存?Chunking 粒度怎么定?结合文档结构、查询粒度、模型窗口和召回评测选择切分。
  5. 怎样避免语义被切断?用结构化切分、重叠窗口、父子块和上下文扩展保留语义。
  6. Embedding 是什么?怎样选和评估模型?解释语义向量映射,并用领域数据上的召回指标和成本选型。
  7. 了解哪些 Embedding 算法?从稀疏到稠密、双塔到指令向量概括演进与适用场景。
  8. 什么是向量数据库?如何做选型?比较索引、过滤、规模、延迟、运维和生态,而不是只报产品名。
  9. 你用的向量库数据量和性能怎样?用真实规模、索引参数、P95 延迟与瓶颈定位数据回答。
  10. 一次 RAG 输入在系统内怎样流动?从 query 处理到证据拼装与答案引用,画出端到端链路。
  11. 向量检索和关键词检索有何区别?语义相似适合改写表达,关键词适合实体精确命中,生产常做混合。
  12. Query Rewrite 为什么做,怎么做?把口语、歧义和上下文依赖改成可检索查询,并防止语义漂移。
  13. 什么是多路召回?并行使用稠密、稀疏、规则等通道,再融合去重与重排。
  14. RAG 检索优化有哪些策略?从 query、索引、召回、rerank、上下文拼装和反馈逐层优化。
  15. 了解哪些复杂 RAG 范式?说明 Self-RAG、Corrective RAG 等在哪个环节增加判断与纠错。
  16. 什么场景用图数据库增强检索?当关系路径、实体约束和多跳推理比纯语义相似更关键时采用。
  17. 如何规避 RAG 幻觉?从提高证据质量、强制引用、拒答策略和事实核验共同治理。
  18. 怎样量化 RAG 效果?拆成检索召回、重排、答案忠实度、相关性与端到端任务成功率。
  19. 知识库如何动态持续更新?用增量解析、版本化索引、删除同步和回滚保证新旧一致。
  20. RAG 落地最难的地方是什么?用自己的失败案例说明数据质量、评测闭环和业务边界最难。

来源:小林面试笔记 · AI 大模型面试题

LLM 工具调用面试题 16 题 · 阶段一重点
  1. 什么是 Function Calling?原理是什么?模型生成结构化调用意图,客户端校验执行并回传结果,不是模型直接运行函数。
  2. LLM 如何学会调用外部工具?从带工具描述与调用样本的训练数据解释“选择工具并生成参数”。
  3. Function Call 能力怎样训练出来?概括监督微调、偏好优化与执行反馈,不把 schema 误当运行能力。
  4. 什么是 MCP?核心内容是什么?把它说明为连接模型应用与外部能力的标准上下文协议。
  5. MCP 由哪些部分组成?围绕 host、client、server 与资源/工具/提示的交互边界回答。
  6. MCP 和 Function Calling 有什么区别?FC 是模型输出调用的机制,MCP 是能力发现与连接协议,可组合使用。
  7. 何时用 Function Calling,何时用 MCP?少量内置工具直接 FC;跨应用复用、标准接入与动态发现考虑 MCP。
  8. 为什么有些推理模型不支持 MCP?先澄清 MCP 在应用层,再分析模型结构化输出与工具训练能力是否匹配。
  9. Skill 是什么?把完成一类任务的知识、流程和资源封装成可复用操作手册。
  10. MCP 和 Agent Skill 有何区别?MCP 提供可连接能力,Skill 描述如何组合能力完成任务。
  11. Function Calling、Skill、MCP 三者是什么关系?用“调用语言、操作手册、工具箱协议”建立分层关系。
  12. A2A 与 MCP 有何区别?A2A 处理 Agent 间协作,MCP 处理应用与工具/资源连接。
  13. MCP 通常采用什么通信方式?从本地 stdio 与远程流式 HTTP 说明传输只承载协议消息。
  14. WebSocket 和 SSE 有何区别与局限?比较双向/单向、协议复杂度、断线恢复、代理兼容与聊天场景需求。
  15. WebRTC 与 WebSocket 在 AI 对话流中有何差异?实时音视频优先 WebRTC,通用双向应用消息更适合 WebSocket。
  16. LLM 网关解决什么问题?统一鉴权、路由、限流、重试、观测、成本和多模型切换。

来源:小林面试笔记 · AI 大模型面试题

大模型工程面试题 22 题 · 应用必修 + 算法岗选看
  1. 什么是大语言模型?与传统 NLP 模型有何区别?从规模化预训练、通用生成能力与任务适配方式对比。
  2. Transformer、Encoder、Decoder 的基本原理是什么?围绕注意力、前馈网络、残差与自回归目标说明信息流。
  3. MHA 有何局限?MQA、GQA、Flash Attention 怎么解决?从显存带宽、KV 头共享和 IO 优化解释各自代价。
  4. 位置编码有什么用?sin/cos、RoPE、ALiBi 有何区别?先说明注意力缺乏顺序,再比较绝对、旋转与偏置式位置表达。
  5. 分词器是什么?原理是什么?文本映射为词表 ID,词表与算法决定 token 数、成本和多语言表现。
  6. 大模型是怎样训练出来的?按预训练、指令微调、偏好对齐与评测部署讲全景。
  7. Scaling Law 与涌现能力是什么?说明数据、参数、算力的经验规律,并谨慎区分连续提升与阈值现象。
  8. 大模型微调有哪些方案?比较全量、PEFT、提示微调等在数据、显存和能力改变上的取舍。
  9. LoRA 除了减少参数还有哪些优点?讲低秩增量、可插拔权重、较低存储与多任务切换。
  10. RLHF、DPO、GRPO、拒绝采样是什么关系?从反馈来源、优化目标、是否显式奖励模型与在线采样区分。
  11. DPO 与 PPO 有何区别?比较直接偏好优化和强化学习策略优化的训练链路与稳定性。
  12. 贪心、Beam Search、采样何时使用?用确定性、搜索宽度、多样性与任务目标解释选型。
  13. temperature、Top-P、Top-K 控制什么?先讲 logits 到概率,再讲温度缩放与候选集合截断。
  14. KV Cache 与 Prompt Caching 原理是什么?一个复用自回归注意力中间状态,一个复用相同前缀计算或计费结果。
  15. 量化是什么?INT8、INT4、AWQ、GPTQ 怎么选?以精度、显存、吞吐、硬件支持和校准成本权衡。
  16. 如何写好 Prompt?明确角色、任务、上下文、约束、输出格式,并用评测集迭代。
  17. 什么是 CoT?为何有效,有何局限?把复杂问题拆成中间步骤,但要说明成本、错误累积与不可验证性。
  18. 幻觉为什么出现,如何缓解?从概率生成、训练知识与上下文不足解释,再用检索、工具、约束和核验治理。
  19. MoE 是什么?为什么 DeepSeek、Qwen 使用它?每个 token 只激活部分专家,用路由换取更大容量与较低计算。
  20. vLLM、TGI、llama.cpp、SGLang 怎么选?按云端吞吐、本地轻量、硬件、并发、生态与运维要求选型。
  21. 大模型能力评测指标有哪些?区分通用 benchmark 与业务任务集,补充正确性、延迟、成本和安全。
  22. 项目为什么选这个模型?用真实业务测试集比较质量、延迟、价格、上下文、工具能力和稳定性。

来源:小林面试笔记 · AI 大模型面试题

延伸资源

同一资源页还有图解 Agent 专栏 6 篇、图解 Claude Code 专栏 10 篇及 LangChain 框架专题。另可用开源题库按项目链路补充练习。

GitHub · didilili/ai-agents-from-zero

FAQ

应用开发主考什么?
优先准备 Agent、RAG、工具调用三块,并把每个答案落到自己的项目证据。

工程底层题都要看吗?
采样、Prompt、幻觉、缓存和部署必看;Transformer、训练对齐、量化、MoE 可作为算法岗进阶。

怎样避免背完就忘?
每题录一次 90 秒口述,再让自己追问“为什么、怎么选、失败怎么办”。

简历与 STAR

先把简历写成可核验的工程证据,再把其中三个项目或场景整理成能口述的 STAR 稿。

基本信息

姓名:______ | 城市:______ | 电话:______ | 邮箱:______ | GitHub:______ | 学历:______

求职意向

目标职位:□ AI 应用开发工程师 □ AI Agent 开发工程师 | 期望城市:______ | 经验:______ | 到岗时间:______

核心优势 · 只写可验证事实

  • AI 编程实践 - ______(项目、频次或成果证据)
  • 全栈工程能力 - ______(前后端、部署或性能证据)
  • 产品思维 - ______(需求、用户反馈或转化证据)
  • 问题定位 - ______(故障、根因与修复证据)
  • 协作交付 - ______(角色、范围与按期结果)

AI 项目经历 · 每个项目复制一份

项目名:______ 时间:______ 角色:______

项目描述:用一句话写清用户、问题和产出:______

技术架构:接入层 ______ / 应用层 ______ / AI 能力层 ______ / 数据层 ______ / 基础设施 ______

关键技术决策:选择 ______ 而不是 ______,因为约束/指标是 ______

可量化结果:将 ______ 从 ______ 改善到 ______;测量口径 ______

LLM 应用模型 API:______
Prompt / 结构化输出:______
流式响应:______
Agent框架 / 状态图:______
Tool Calling:______
记忆 / 评测:______
RAG解析 / Chunking:______
Embedding / 向量库:______
召回 / 重排 / 引用:______
工程化TypeScript / Node.js:______
Docker / CI/CD:______
监控 / 测试:______

STAR 面试稿框架

  • S · Situation“当时 ______ 用户在 ______ 流程中遇到 ______,已有方案的限制是 ______。”
  • T · Task“我负责把目标定义为 ______,并在 ______ 约束下达到 ______ 验收指标。”
  • A · Action“我先用 ______ 验证根因,再选择 ______ 架构;关键取舍是 ______,因为 ______。”
  • R · Result“最终 ______ 指标从 ______ 变为 ______;我通过 ______ 验证,并复盘出 ______。”
准备 3 份不同 STAR 稿

至少覆盖:一个完整 AI 项目、一次复杂故障或失败修复、一次需求冲突或跨角色推进。每份准备 30 秒结论版、3 分钟展开版和追问证据。

完整简历模板与更多示例见:简历面试技巧 · AI Agent 简历模板

FDE 专题

一句话定位:以客户业务结果为目标、仍然直接写生产代码的工程岗位;核心是从模糊问题出发完成需求发现、方案设计、编码集成、生产上线、采用推广和反馈闭环。

能力维度面试官想确认什么
问题发现与拆解能否把“我们想用 AI”转成明确用户、现有流程、约束条件和可验证目标。
工程与 AI 系统设计能否设计可接入现有系统、可评测、可观测且可回滚的生产方案。
编码与调试能否在陌生代码、脏数据和变化需求下快速定位问题并可靠实现。
客户与项目推进能否管理承诺、冲突、范围、采用过程和高压事故,而不丢失业务目标。
产品化反馈能否区分一次性定制与可复用能力,并把现场反馈沉淀进产品。

高频题方向

  • FDE 与后端工程师、架构师、顾问有什么区别?答题思路:用结果责任、是否直接写生产代码、工作地点/协作边界、需求确定性四个维度对比;不要只讲岗位名称。
  • 客户只说“我们想用 AI”,你怎么拆解?答题思路:先找用户与高频流程,再建立基线、风险和约束,最后把目标改写成可测的任务成功率、时延、成本或采用率。
  • 如何设计可接入现有系统、又可回滚的生产方案?答题思路:画出现有接口与数据边界,采用小流量试点、版本化配置、降级路径、审计日志和回滚触发条件。
  • 进入陌生代码库后怎样调试线上问题?答题思路:先复现与收集 trace,缩小到输入、依赖、状态或基础设施,再做最小修复、回归测试和知识沉淀。
  • 客户现场发生范围冲突或高压事故,怎么处理?答题思路:先统一事实和影响,明确决策人、止损动作与时间盒;把范围、承诺和未决风险书面化,再持续同步。
  • 怎样区分一次性定制与可复用产品能力?答题思路:看需求是否跨客户重复、能否抽象稳定接口、维护成本与战略价值;对通用部分产品化,对例外保留配置或插件边界。

完整 30+ 道 FDE 面试题见:Forward Deployed Engineer 专题

题库导航

先按目标选择题库,再回到项目补证据。资源说明只做定位,不替代仓库原文。

资源名一句话介绍链接适合人群
guocong-bincai/ai-interview-guide720 道题 / 28 个专题,覆盖 AI 应用、RAG、Agent、FDE、多模态、安全评测,配图解。打开 GitHub需要系统刷题的人
bcefghj/ai-agent-interview-guide200+ Agent 面试题 + 企业级项目(Python / Java / Go 三版)+ 简历模板。打开 GitHub项目深挖和简历准备
zhouzhupianbei/AgentInterviewAIGC 开发者一站式面试准备资源。打开 GitHub想快速定位复习入口的人
didilili/ai-agents-from-zeroAI 智能体与大模型应用开发面试题库;本站面试备战已收录,列出以便对照。打开 GitHub已有本站基础内容、需要对照复习的人

28 个专题目录