从零到一,10分钟搭建我们的第一个 AI 对话应用!
前置知识
在本篇开始之前,我们可能需要具备以下知识:
- 会写基础的 JavaScript/node.js代码(能看懂 async/await 即可)
- 了解环境变量的概念(不知道也没关系,跟着做就行)
万事开头难
现在处于AI发展风口期,部分人可能会觉得:这个东西好高大上,我不会...。其实任何一件事,只要开始做了,就一定能成!
第一步:注册 DeepSeek API 账号
本文以 DeepSeek 为例,实现注册我们自己的 API 账号。
为什么选 DeepSeek?
- 国内出品,注册简单,不需要魔法
- 价格便宜,对新手小白和开发者都比较友好
- 社区活跃
注册步骤
- 打开 DeepSeek 开放平台
- 手机号注册
- 进入控制台,选择“API Keys”,创建 API Key
- 复制 API Key!复制 API Key!复制 API Key!
这里一定要注意:为了防止 API Key 被泄漏,所有 API Key 只有在创建的时候才可以复制! 这里一定要注意:为了防止 API Key 被泄漏,所有 API Key 只有在创建的时候才可以复制! 这里一定要注意:为了防止 API Key 被泄漏,所有 API Key 只有在创建的时候才可以复制!
从上述截图中可以看到,保存之后的 API Key只有开头和结尾几个字母,中间是一大串 * ;如果创建时没保存,那么这个 API Key 只能删除重建了!
第二步:项目初始化- 创建Node.js项目
创建项目
我虽然是 Vue 开发者,但我们的第一个实验,还是用最简单的 Node.js 脚本跑通,排除前端复杂度的干扰:
# 创建项目文件夹
mkdir AiAgentLearning
cd AiAgentLearning
# 初始化npm项目
npm init -y
# 安装依赖
npm install dotenv
创建.env文件 - 存放 API Key
在项目根目录创建 .env 文件,由于我个人习惯,我是倾向把 API Key 和 请求 URL 放在一起的,方便后续成套修改:
DEEPSEEK_API_KEY=你自己的key
DEEPSEEK_BASE_URL=完整请求url
创建.gitignore - 防止 API Key 误上传
.env
node_modules
对于需要上传 GIT 的同学来说,还是得先建
.gitignore再写代码,以免 API Key 泄漏,比较现在爬虫还是挺多的!!! 当然,如果不用上传 GIT,此步骤可以省略!
验证环境变量
一切完成之后我们可以先验证自己的环境变量是否设置成功:
require('dotenv').config()
console.log('API Key前5位:', process.env.DEEPSEEK_API_KEY?.substring(0, 5))
如果能看到 API Key 的前几位正常打印输出就说明成功了!
第一个非流式请求 - 先跑通
前期不用追求太完美,先跑通一个最简单的非流式调用,建立信心。
创建 simple-request.js
require('dotenv').config()
async function callDeepSeek() {
try {
const response = await fetch(process.env.DEEPSEEK_BASE_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: 'hello' }
],
stream: false // 非流式
})
})
const data = await response.json()
console.log('AI回复:')
console.log(data.choices[0].message.content)
} catch (error) {
console.error('请求失败:', error)
}
}
callDeepSeek()
如果一切顺利,我们就会看到 AI 的回复,这就成功调用了一次 API:
这只是问答示例,除了
hello,也可以输入任何我们想输入的内容!在收到回复之前,这个过程可能要等几秒钟。
实现流式问答
为什么需要流式?
| 模式 | 体验 | 适用场景 |
|---|---|---|
| 非流式 | 等待5-10秒,然后一次性看到全部内容 | 后台批处理、代码生成 |
| 流式 | 像打字机一样逐字出现,用户体验好 | 聊天机器人、实时交互 |
核心区别:流式回答用 stream: true,数据是分块(chunk)返回的。
创建stream-request.js - 第一个流式问答
require('dotenv').config()
async function streamChat() {
try {
const response = await fetch(process.env.DEEPSEEK_BASE_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.DEEPSEEK_API_KEY}`
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [
{ role: 'system', content: '你是一个前端开发助手' },
{ role: 'user', content: '解释一下Vue3的ref和reactive的区别,用生活类比' }
],
stream: true // 关键:开启流式
})
})
// 处理流式响应
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
let buffer = '' // 用于处理可能的不完整数据块
console.log('AI回复:\n')
while (true) {
const { done, value } = await reader.read()
if (done) break
// 解码收到的数据块
const chunk = decoder.decode(value)
buffer += chunk
// SSE格式是以\n\n分隔的
const lines = buffer.split('\n\n')
buffer = lines.pop() || '' // 最后一行可能不完整,留到下次处理
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6) // 去掉 'data: ' 前缀
// 跳过 [DONE] 消息
if (data === '[DONE]') continue
try {
const parsed = JSON.parse(data)
const content = parsed.choices[0]?.delta?.content || ''
if (content) {
process.stdout.write(content) // 逐字打印,不换行
}
} catch (e) {
// 忽略解析错误(有时会有空行)
}
}
}
}
console.log('\n\n 流式响应完成')
} catch (error) {
console.error('请求失败:', error)
}
}
streamChat()
如果一切顺利,我们就会看到 AI 的回复:
这个过程基本不需要我们等多久,就有结果开始输出了!而且流式版本就像打字机一样,逐个字符输出,用户体验好太多了。
深入理解 Token
什么是 Token
Token 是 AI 模型处理文本的基本单位,可以简单理解为:
- 英文:1个单词 ≈ 1-2 个 token
- 中文:1个汉字 ≈ 1-2 个 token
- 标点符号:也算 token
举个例子:
"我爱编程"可能被切分为:["我", "爱", "编", "程"] 或 ["我爱", "编程"],大概是 8 个 token。我们向 AI 提问,AI 再返回给我们结果,这中间的所有过程,都是需要计算 token 的!
Token 计费示例
以 DeepSeek 的计价为例:
| 项目 | 价格 |
|---|---|
| 百万tokens输入(缓存命中) | 0.2元 |
| 百万tokens输入(缓存未命中) | 2元 |
| 百万tokens输出 | 3元 |
我们写一个简单点的问答,大概消耗:
- 问题:20-50 tokens
- 回答:100-500 tokens
总成本:约 0.00005 元(5分钱能玩100次!)
token 使用注意事项
- 提示器和问题不要太复杂,精简系统提示
- 控制回复长度,限制最大输出长度
- 控制创造性,创造性越高越耗token
结语
技术的门槛往往不在技术本身,而在于我们是否愿意迈出第一步,希望这篇文章能帮我们跨过这个坎!
对于文章中错误的地方或有任何疑问,欢迎在评论区留言讨论!