跳转到内容

搭建服务器

刀刀
0字
0分钟
2026/8/17

前言

前端虽然可以直接请求大模型接口,但存在以下问题:

  1. API Key 写在前端容易暴露
  2. 无法鉴权和拦截攻击
  3. 后续操作如定义工具、MCP 等无法进行

OpenAI

主流编程语言都有专门调用 AI 接口的库,且基本遵循同一套标准。如 Node.jsOpenAI 库。

server.js
js
const express = require('express')
const cors = require('cors')
const OpenAI = require('openai') // 专门用来按标准请求大模型接口的一个sdk库
const app = express()
app.use(cors())

const openai = new OpenAI({
  apiKey: 'sk-xxxxxx',
  baseURL: 'xxx',
})

app.get('/llm', async (req, res) => {
  const keyword = req.query.keyword
  const aiRes = await openai.chat.completions.create({
    model: '',
    messages: [{ role: 'user', content: keyword }],
  })
  res.json(aiRes.choices[0].message.content)
})

app.listen(3000, () => {
  console.log('Server is running on port 3000')
})

详解 OpenAI

  1. OpenAI 库使用的地址通常与直接 HTTP 请求不同
  2. OpenAI 库相对于 HTTP 请求,提供了规范,更便于使用 Function CallMCP 接入,代码更简洁,优先选用。此外还提供其他方法用于其他场景,等同于一个专门用来请求大模型接口的 SDK
  3. OpenAI 库请求接口本质上仍用 fetch 发送 HTTP 请求,可拦截其发出的 HTTP 请求和响应

重写 fetch 查看请求和响应

server.js
js
const express = require('express')
const cors = require('cors')
const OpenAI = require('openai') // 专门用来按标准请求大模型接口的一个sdk库
const app = express()
app.use(cors())

// 重写 fetch,让整个项目通过 fetch 发请求时会打印一些东西
const originalFetch = global.fetch 
global.fecth = async function (...args) {
  const [url, options = {}] = args 
  console.log('fetching', url, options) 
  try {
    const response = await originalFetch.apply(this, args) // 调用原始 fetch
    const clonedResponse = response.clone() // 克隆响应以便读取body而不影响原始响应
    for (const [key, value] of Object.entries(options.headers)) {
      console.log(`${key}: ${value}`) 
    } 
    const result = await clonedResponse.json() // 尝试读取响应体
    return response 
  } catch (error) {
    console.error(`Error: ${error}`) 
  } 
} 

const openai = new OpenAI({
  apiKey: 'sk-xxxxxx',
  baseURL: 'xxx',
})

app.get('/llm', async (req, res) => {
  const keyword = req.query.keyword
  const aiRes = await openai.chat.completions.create({
    model: '',
    messages: [{ role: 'user', content: keyword }],
  })
  res.json(aiRes.choices[0].message.content)
})

app.listen(3000, () => {
  console.log('Server is running on port 3000')
})

携带历史

接口是无状态的,每次请求携带上下文有助于模型理解用户意图。实现一个简单的携带历史功能。

server.js
js
const express = require('express')
const cors = require('cors')
const OpenAI = require('openai') // 专门用来按标准请求大模型接口的一个sdk库
const app = express()
app.use(cors())

// 重写 fetch,让整个项目通过 fetch 发请求时会打印一些东西
const originalFetch = global.fetch

global.fecth = async function (...args) {
  const [url, options = {}] = args
  console.log('fetching', url, options)

  try {
    const response = await originalFetch.apply(this, args) // 调用原始 fetch
    const clonedResponse = response.clone() // 克隆响应以便读取body而不影响原始响应

    for (const [key, value] of Object.entries(options.headers)) {
      console.log(`${key}: ${value}`)
    }
    const result = await clonedResponse.json() // 尝试读取响应体
    return response
  } catch (error) {
    console.error(`Error: ${error}`)
  }
}

const messageList = [] // 存储历史消息

const openai = new OpenAI({
  apiKey: 'sk-xxxxxx',
  baseURL: 'xxx',
})

app.get('/llm', async (req, res) => {
  const keyword = req.query.keyword
  const queryObj = {
    role: 'user', 
    content: keyword, 
  } 
  messageList.push(queryObj) // 每次提问保存上下文
  const aiRes = await openai.chat.completions.create({
    model: '',
    messages: messageList,
  })
  messageList.push(aiRes.choices[0].message) // 每次回答保存上下文
  res.json(aiRes.choices[0].message.content)
})

app.listen(3000, () => {
  console.log('Server is running on port 3000')
})

总结

alt text

贡献者

The avatar of contributor named as duyidao duyidao
The avatar of contributor named as 刀刀 刀刀
The avatar of contributor named as Copilot Copilot

页面历史

刀刀博客累计访客 人;文档累计访问量共