Skip to content

AI 辅助前端开发:从编码助手到 LLM 应用集成 ​

到 2026 年,AI 已经分成两条相对独立的前端实践:

  1. 用 AI 辅助开发:编码助手参与需求拆解、写代码、补测试、排查问题。
  2. 在产品中集成 AI:把 LLM 能力做成对话、总结、搜索、生成等用户功能。

两者的技术栈和风险完全不同。前者关注研发效率与代码质量,后者关注流式体验、成本、数据安全与提示词注入。

一、把 AI 变成可靠的工程协作者 ​

AI 编码助手最容易被误用的方式,是把它当成"许愿机":一句话生成整个模块,然后直接合并。这种方式在复杂项目里往往会引入难以察觉的问题。

更有效的工作方式是:

  • 提供充分上下文:相关文件、类型定义、现有约定、错误日志。
  • 拆小任务:一次改一个函数、一个组件、一个测试,避免大范围重写。
  • 让约束可执行:类型检查、ESLint、测试、构建必须能验证结果。
  • 人工审查关键路径:鉴权、支付、数据迁移、性能敏感代码不能只看"能跑"。

一个实用的仓库约定文件(例如 AGENTS.md)能显著提升输出质量:

markdown
# 工程约定

- 使用 pnpm workspace,新增依赖前先确认是否已有同类包。
- 组件使用 `<script setup lang="ts">`,样式使用 CSS Modules。
- 所有对外请求必须经过 `src/api/client.ts`,禁止直接使用 fetch。
- 新增逻辑需要补充 Vitest 测试,关键流程补充 Playwright 用例。
- 不要修改 CI 配置、密钥文件和发布脚本,除非明确要求。
- 提交前运行 pnpm lint && pnpm typecheck && pnpm test。

这比在每次对话里重复说明有效得多。AI 不擅长猜团队规范,但很擅长遵循明确写出的规则。

让测试成为护栏 ​

AI 生成代码的速度远高于人工审查速度,所以自动化验证必须跟上:

bash
pnpm typecheck
pnpm lint
pnpm test -- --run
pnpm build

在 CI 中把这些检查设为合并前置条件。对于 AI 大量参与的项目,测试覆盖率、类型严格度和构建检查比以往更重要,因为它们是唯一不依赖"逐行读代码"的质量门禁。

需要警惕的地方 ​

  • API 幻觉:模型可能编造不存在的配置项或库方法,必须以当前版本文档为准。
  • 过度自信的重构:让 AI 改一个 bug,它可能顺带重构半个文件。
  • 安全边界:不要把密钥、用户数据、内部地址贴进对话。
  • 许可证与来源:引入生成代码前确认没有复制受限代码。
  • 依赖膨胀:AI 倾向于引入新库解决问题,需要人工判断是否必要。

二、在产品中集成 LLM ​

产品级 AI 功能的基本架构:

text
浏览器
  → 自有后端(鉴权、限流、提示词、日志)
    → 模型服务商 / 自建推理
  → 流式响应(SSE / fetch stream)

API Key 绝不能放在浏览器。所有请求必须经过自己的后端,后端负责鉴权、限流、内容审核、日志和成本控制。

后端流式代理 ​

ts
// app/api/chat/route.ts
export const runtime = 'nodejs'

export async function POST(request: Request) {
  const { message } = await request.json()

  if (typeof message !== 'string' || message.length === 0 || message.length > 4000) {
    return Response.json({ error: '消息长度不合法' }, { status: 400 })
  }

  const upstream = await fetch(`${process.env.LLM_BASE_URL}/chat/completions`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.LLM_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      model: process.env.LLM_MODEL,
      messages: [
        { role: 'system', content: '你是一名严谨的前端技术助手,不确定时明确说明。' },
        { role: 'user', content: message }
      ],
      stream: true
    })
  })

  if (!upstream.ok || !upstream.body) {
    return Response.json({ error: '模型服务暂不可用' }, { status: 502 })
  }

  return new Response(upstream.body, {
    headers: {
      'Content-Type': 'text/event-stream; charset=utf-8',
      'Cache-Control': 'no-cache, no-transform',
      Connection: 'keep-alive'
    }
  })
}

生产环境还需要补充:用户鉴权、速率限制、输入审核、输出审核、请求超时、重试和成本统计。

前端处理流式响应 ​

ts
// hooks/useChatStream.ts
import { useCallback, useRef, useState } from 'react'

interface ChatState {
  text: string
  loading: boolean
  error: string | null
}

export function useChatStream() {
  const [state, setState] = useState<ChatState>({
    text: '',
    loading: false,
    error: null
  })
  const controllerRef = useRef<AbortController | null>(null)

  const send = useCallback(async (message: string) => {
    controllerRef.current?.abort()
    const controller = new AbortController()
    controllerRef.current = controller
    setState({ text: '', loading: true, error: null })

    try {
      const response = await fetch('/api/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ message }),
        signal: controller.signal
      })

      if (!response.ok || !response.body) {
        throw new Error(`请求失败:${response.status}`)
      }

      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 events = buffer.split('\n\n')
        buffer = events.pop() ?? ''

        for (const event of events) {
          const dataLine = event
            .split('\n')
            .find((line) => line.startsWith('data:'))

          if (!dataLine) continue

          const data = dataLine.slice(5).trim()
          if (!data || data === '[DONE]') continue

          const payload = JSON.parse(data) as { delta?: string }
          if (!payload.delta) continue

          setState((current) => ({
            ...current,
            text: current.text + payload.delta
          }))
        }
      }
    } catch (error) {
      if (controller.signal.aborted) return

      setState((current) => ({
        ...current,
        error: error instanceof Error ? error.message : '未知错误'
      }))
    } finally {
      if (!controller.signal.aborted) {
        setState((current) => ({ ...current, loading: false }))
      }
    }
  }, [])

  const stop = useCallback(() => {
    controllerRef.current?.abort()
    setState((current) => ({ ...current, loading: false }))
  }, [])

  return { ...state, send, stop }
}

几个容易被忽略的细节:

  • SSE 事件可能被网络分片,必须用缓冲区拼接,不能假设一次 read() 就是一个完整事件。
  • 支持用户中止生成,AbortController 要同时中断前端请求和服务端上游请求。
  • 组件卸载时要取消请求,避免状态更新到已卸载组件。
  • 流式过程中出现错误,要保留已生成内容并显示可重试的提示。

Markdown 渲染必须清洗 ​

模型输出本质上不可信。直接渲染 HTML 会引入 XSS:

ts
import MarkdownIt from 'markdown-it'
import DOMPurify from 'dompurify'

const markdown = new MarkdownIt({
  html: false,
  linkify: true,
  breaks: true
})

export function renderModelOutput(text: string) {
  return DOMPurify.sanitize(markdown.render(text))
}

html: false 让 Markdown 中的原始 HTML 被转义,DOMPurify 再做一层兜底。外链要加 rel="noopener noreferrer",必要时限制链接协议。

三、RAG 与工具调用 ​

当模型需要基于内部知识回答时,用检索增强生成(RAG):

text
用户问题
  → 向量化
  → 检索内部文档(带权限过滤)
  → 拼接上下文与引用
  → 模型生成
  → 前端展示答案与来源

关键点:

  • 权限过滤在检索阶段完成,不能把无权限文档放进上下文。
  • 提示词要明确要求"只基于提供的资料回答,资料不足时说明不知道"。
  • 前端展示引用来源,用户可以核对。
  • 文档更新后要重建索引,否则答案会过期。

工具调用(Function Calling)让模型能查询订单、创建任务、调用内部 API。安全原则:

  • 工具在服务端执行,前端只接收结构化结果。
  • 每个工具做独立的权限校验,不信任模型传入的参数。
  • 破坏性操作需要用户确认,不能由模型直接执行。
  • 记录工具调用日志,便于审计。

MCP(Model Context Protocol)正在成为模型访问外部工具和数据的标准协议。它降低了集成成本,但也扩大了攻击面,接入内部系统时要严格控制权限范围。

四、成本、隐私与体验 ​

成本控制:

  • 限制单次输入和输出 token 数。
  • 对重复问题做缓存,例如相同检索结果加相同问题。
  • 简单任务使用小模型,复杂任务再路由到大模型。
  • 按用户或租户做配额,防止单用户刷爆额度。
  • 监控 token 用量、首 token 延迟和端到端延迟。

隐私与合规:

  • 明确告知用户数据会被发送给模型服务商。
  • 敏感字段做脱敏,或使用自建模型。
  • 不在日志中记录完整对话和密钥。
  • 提供数据删除和导出能力。

体验细节:

  • 首 token 时间比总时长更影响感知,优先优化首字节。
  • 提供停止、重试和重新生成。
  • 空状态和错误状态要可恢复,不要让用户面对一个永远转圈的界面。
  • 使用 aria-live 通知屏幕阅读器内容更新,但避免逐 token 播报。
  • 对 AI 生成内容做标识,避免用户误认为是确定事实。

五、团队落地清单 ​

  1. 约定仓库上下文:编写 AGENTS.md,明确技术栈、目录规范和验证命令。
  2. 先补护栏:类型检查、Lint、测试、构建必须在 CI 中稳定运行。
  3. 从低风险任务开始:测试补全、文案调整、类型修复、文档生成。
  4. 建立审查规范:AI 参与的代码同样走 Code Review,关键路径提高审查级别。
  5. 沉淀提示词与工具:把高频任务做成模板或内部工具,而不是每个人各写一套。
  6. 监控产品 AI 功能:延迟、失败率、token 成本、用户反馈和内容安全。
  7. 定期复盘:哪些任务 AI 做得好,哪些必须人工,形成团队自己的边界。

小结 ​

AI 对前端工程师的价值,不是替代判断,而是放大执行力。在开发侧,用清晰的约束和自动化验证把 AI 的输出纳入工程体系;在产品侧,把模型当作不可信的外部服务,做好流式体验、权限、安全和成本控制。能把这两件事做好,AI 才是真正的生产力。

基于 VitePress 构建 · 持续更新中