第一章:大模型应用开发概述
1.1 大模型是什么大模型LLMLarge Language Model本质上是一个接受文本输入、返回文本输出的函数。从工程角度来说你不需要理解它内部的神经网络结构只需要知道给它一段文字Prompt它会返回一段文字Completion它的能力来自在海量文本上的预训练具备语言理解、推理、生成能力通过 HTTP API 调用和调用任何后端接口没有本质区别类比大模型就像一个能理解自然语言的超级函数你传入参数问题、指令它返回结果回答、代码、分析。大模型发展时间线时间模型关键意义2017Transformer奠定架构基础Attention is All You Need2018GPT-1预训练 微调范式确立2020GPT-31750亿参数Few-shot 能力出现2022ChatGPTRLHF 对齐对话能力质变2023GPT-4 / Claude 3多模态推理接近专业水准2024DeepSeek / Qwen / Llama 3开源生态崛起成本大幅下降作为前端开发者你需要关心什么不需要关心模型内部权重、训练过程、GPU 集群。需要关心API 接口怎么发请求、请求结构是什么、响应如何解析Token 和计费文本怎么计量成本怎么估算上下文窗口模型能记住多长的对话历史流式输出怎么实现打字机效果SSE 怎么用提示词设计怎么让模型输出你想要的格式和内1.2 核心概念1.2.1 TokenToken 是大模型处理文本的基本单位既不是字符也不是单词而是介于两者之间的词片。英文Hello World → [Hello, World] → 2 tokens 中文你好世界 → [你, 好, 世, 界] → 大约 4 tokens 代码const x 1 → [const, x, , 1] → 4 tokensToken 的工程意义API 按 Token 计费输入和输出分别计价模型有最大上下文长度限制DeepSeek-chat 支持 64K tokensToken 数量影响响应速度和成本快速估算规则不用装 tiktoken这个精度够用英文1 token ≈ 4 个字符中文1 个汉字 ≈ 1.5~2 tokens按 0.6 token/字估算代码比文字消耗更多 token1.2.2 上下文窗口Context Window大模型每次调用都是无状态的——它不记得上一次对话。要实现多轮对话需要每次都把完整的历史记录作为输入传给模型第一轮[用户:你好] → 模型回复 第二轮[用户:你好, 助手:你好, 用户:我叫张三] → 模型回复 第三轮[用户:你好, 助手:..., 用户:我叫张三, 助手:..., 用户:你还记得我叫什么吗] → 模型回复这就是上下文——每次请求都要携带完整的对话历史。上下文窗口的限制带来了一个工程问题对话太长超过限制时怎么处理这是第九章长期记忆要解决的问题。1.2.3 温度参数TemperatureTemperature 控制模型输出的随机性取值范围 0~2Temperature特点适用场景0确定性输出每次结果几乎相同代码生成、数据提取、分类0.2~0.5低随机结果稳定但有轻微变化客服问答、摘要0.7中等随机有创造性但不失控通用对话默认值1.0~1.5高随机创意性强但可能跑偏写作、头脑风暴1.2.4 消息角色MessagesOpenAI 兼容 APIDeepSeek、Qwen、Claude 等都支持使用三种角色messages: [ { role: system, content: 你是一位前端开发导师 }, // 系统提示定义角色和行为 { role: user, content: 什么是 Vue3 的响应式 }, // 用户消息 { role: assistant, content: ... }, // 模型的上一条回复 { role: user, content: 能举个例子吗 }, // 继续对话 ]1.3 第一个 API 调用1.3.1 申请 API Key以 DeepSeek 为例访问 platform.deepseek.com注册登录后进入「API Keys」页面点击「创建 API Key」复制保存只显示一次Key 格式类似sk-xxxxxxxxxxxxxxxxxxxxxxxx安全注意事项不要把 API Key 提交到 Git项目根目录创建.env文件存储.gitignore里加上.env1.3.2 原生 Fetch 调用DeepSeek 兼容 OpenAI API 格式Node.js 18 内置 fetch不需要安装任何包// nodejs-fetch.js import dotenv/config const API_URL https://api.deepseek.com/v1/chat/completions async function chat(userMessage) { const res await fetch(API_URL, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${process.env.DEEPSEEK_API_KEY}, }, body: JSON.stringify({ model: deepseek-chat, messages: [{ role: user, content: userMessage }], temperature: 0.7, max_tokens: 1024, }), }) if (!res.ok) { const err await res.json() throw new Error(API 错误 ${res.status}: ${err.error?.message}) } const data await res.json() const reply data.choices[0].message.content const usage data.usage // { prompt_tokens, completion_tokens, total_tokens } console.log(回复, reply) console.log(Token 用量, usage) return { reply, usage } } chat(用一句话解释什么是大模型).catch(console.error)响应结构{ choices: [{ message: { role: assistant, content: 大模型是基于海量文本训练的大规模语言模型... }, finish_reason: stop }], usage: { prompt_tokens: 12, completion_tokens: 38, total_tokens: 50 } }1.3.3 用 LangChain.js 调用实际项目里推荐用 LangChain.js好处是屏蔽底层 HTTP 细节、统一接口、换模型只改配置不改代码// langchain-basic.js import dotenv/config import { ChatOpenAI } from langchain/openai import { HumanMessage, SystemMessage } from langchain/core/messages const model new ChatOpenAI({ model: deepseek-chat, apiKey: process.env.DEEPSEEK_API_KEY, configuration: { baseURL: https://api.deepseek.com/v1 }, temperature: 0.7, }) const res await model.invoke([ new SystemMessage(你是一位耐心的前端开发导师擅长用类比解释技术概念。), new HumanMessage(用盖房子来类比解释什么是大模型应用开发), ]) console.log(res.content)换成 GPT-4o 只需改两行model: gpt-4o, apiKey: process.env.OPENAI_API_KEY, // configuration 里的 baseURL 删掉场景建议学习、脚本、一次性工具原生 fetch简单直观正式项目LangChain.js有链式调用、记忆、工具等生态需要频繁切换模型LangChain.js改配置不改代码1.4 流式输出Streaming非流式调用要等模型生成完整响应后才返回长文本场景下用户体验差。流式输出让内容像打字机一样逐字出现。1.4.1 服务端Express SSE浏览器的EventSourceAPI 只支持 GET 请求所以常见做法是前端用 fetch ReadableStream 发 POST服务端返回 SSE 格式的流。// server.js import dotenv/config import express from express import { ChatOpenAI } from langchain/openai import { HumanMessage } from langchain/core/messages const app express() app.use(express.json()) app.use(express.static(.)) const model new ChatOpenAI({ model: deepseek-chat, apiKey: process.env.DEEPSEEK_API_KEY, configuration: { baseURL: https://api.deepseek.com/v1 }, streaming: true, }) app.post(/api/chat/stream, async (req, res) { const { message } req.body // SSE 必须设置这三个响应头 res.setHeader(Content-Type, text/event-stream) res.setHeader(Cache-Control, no-cache) res.setHeader(Connection, keep-alive) try { // stream() 返回 AsyncIterable逐 chunk 推送 const stream await model.stream([new HumanMessage(message)]) for await (const chunk of stream) { if (chunk.content) { // SSE 格式event 行 data 行 空行 res.write(event: token\ndata: ${JSON.stringify({ token: chunk.content })}\n\n) } } res.write(event: done\ndata: {}\n\n) } catch (err) { res.write(event: error\ndata: ${JSON.stringify({ message: err.message })}\n\n) } finally { res.end() } }) app.listen(3000, () console.log(http://localhost:3000))1.4.2 Vue3 前端接收流template div textarea v-modelinput placeholder输入你的问题... rows3 / button clicksend :disabledloading{{ loading ? 生成中... : 发送 }}/button div classoutput {{ output }}span v-ifloading classcursor / /div /div /template script setup import { ref } from vue const input ref() const output ref() const loading ref(false) async function send() { if (!input.value.trim() || loading.value) return loading.value true output.value const res await fetch(/api/chat/stream, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ message: input.value }), }) const reader res.body.getReader() const decoder new TextDecoder() let buffer while (true) { const { value, done } await reader.read() if (done) break buffer decoder.decode(value, { stream: true }) const lines buffer.split(\n) buffer lines.pop() // 保留未完整的行等下一个 chunk 来拼 for (const line of lines) { if (line.startsWith(data: )) { try { const data JSON.parse(line.slice(6)) if (data.token) output.value data.token } catch {} } if (line event: done) loading.value false } } loading.value false } /script style scoped .cursor { display: inline-block; width: 2px; height: 1em; background: currentColor; vertical-align: text-bottom; animation: blink .7s infinite; } keyframes blink { 0%, 100% { opacity: 1 } 50% { opacity: 0 } } /style核心要点res.body.getReader()获取 ReadableStream 的读取器TextDecoder把 Uint8Array 转成字符串用buffer拼接跨 chunk 的数据防止 SSE 消息被截断每条 SSE 消息格式event: xxx\ndata: xxx\n\n1.4.3 React 版本// StreamChat.jsx import { useState } from react export default function StreamChat() { const [input, setInput] useState() const [output, setOutput] useState() const [loading, setLoading] useState(false) async function send() { if (!input.trim() || loading) return setLoading(true) setOutput() const res await fetch(/api/chat/stream, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ message: input }), }) const reader res.body.getReader() const decoder new TextDecoder() let buffer while (true) { const { value, done } 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: )) { try { const data JSON.parse(line.slice(6)) if (data.token) setOutput(prev prev data.token) } catch {} } if (line event: done) setLoading(false) } } setLoading(false) } return ( div textarea value{input} onChange{e setInput(e.target.value)} rows{3} / button onClick{send} disabled{loading} {loading ? 生成中... : 发送} /button div{output}/div /div ) }1.5 Token 计费与成本估算主流模型价格对比2024年末模型输入/1M tokens输出/1M tokens特点DeepSeek Chat$0.27$1.10性价比最高推荐学习和生产GPT-4o-mini$0.15$0.60轻量任务首选GPT-4o$2.50$10.00能力最强成本高Claude 3.5 Sonnet$3.00$15.00代码和推理能力强// cost-estimator.js export function estimateTokens(text) { const cnChars (text.match(/[\u4e00-\u9fff]/g) || []).length const otherChars text.length - cnChars // 中文约 0.6 token/字其他约 0.25 token/字符 return Math.ceil(cnChars * 0.6 otherChars * 0.25) } const PRICING { deepseek-chat: { input: 0.27, output: 1.10 }, gpt-4o: { input: 2.50, output: 10.0 }, gpt-4o-mini: { input: 0.15, output: 0.60 }, } export function estimateCost(inputTokens, outputTokens, model deepseek-chat) { const p PRICING[model] const total (inputTokens / 1e6) * p.input (outputTokens / 1e6) * p.output return { usd: total.toFixed(6), cny: (total * 7.2).toFixed(4), } }实际案例智能客服平均每次输入 800 tokens 输出 300 tokens每天 1000 次对话模型单次费用日费用月费用CNYDeepSeek$0.00055$0.55¥120GPT-4o-mini$0.00030$0.30¥65GPT-4o$0.005$5.00¥1,080DeepSeek 在能力相近的情况下成本比 GPT-4o 低 90%这是国内生产项目普遍选它的主要原因。1.6 技术选型层级技术原因运行时Node.js 20 ESM前端直接上手零编译AI 框架LangChain.js LangGraph生态成熟功能对标 Python 版大模型DeepSeek默认性价比最高兼容 OpenAI APIWeb 框架Express.js轻量代码量少便于理解原理前端示例Vue3 React两种主流框架均有示例向量库Chroma本地运行Docker 一键启动数据库MySQL国内项目最常用1.7 本章小结大模型应用开发对前端开发者来说入门门槛不高核心就是 HTTP 调用 响应解析 SSE 流式处理这些都是已经熟悉的东西。几个关键认知大模型本质是一个 HTTP 接口调用方式和普通后端接口没有本质区别Token 是计量单位成本要从第一天就重视不要等上线了才发现账单爆了每次调用都是无状态的多轮对话靠手动把历史记录拼进请求里Temperature 控制随机性生产环境用低值保稳定创意场景再调高用 LangChain.js 封装调用换模型只改.env里的配置