前端工程师进阶提示词工程实战

76 阅读6分钟

学会"问问题",让 AI 输出质量提升 10 倍!

前置知识

在开始之前,我们需要掌握以下技能:

  • 基础 JavaScript/TypeScript:能看懂 async/await 即可
  • LangChain 基础:了解 ChatOpenAI 和 PromptTemplate 的基本用法
  • Node.js 环境:已安装 Node.js 18+ 版本

如果对 LangChain 环境搭建还不熟悉,建议先看我之前的文章《LangChain JS 入门:前端快速搭建 AI 开发环境》。

为什么提示词工程如此重要?

很多人觉得 AI "不够聪明",其实往往是我们"问得不够好"。同样的模型,提示词的质量直接决定输出的质量。

提示词类型示例效果
❌ 糟糕提示词"帮我写个登录页面"输出随意,风格不一致,可能缺少关键功能
✅ 优质提示词"用 Vue3 + TypeScript 写一个登录页,包含邮箱/手机号验证、记住密码功能,样式用 Tailwind CSS"输出精准,符合技术栈要求,功能完整

核心观点:提示词工程不是魔法,而是一套可学习、可复用的方法论。

优质提示词的核心编写原则

原则一:清晰明确

AI 无法读取你的想法,提示词越清晰,输出越精准。

❌ 模糊版:"帮我解释一下响应式"
✅ 清晰版:"用前端开发者的视角,解释 Vue3 的响应式原理,包含 Proxy 的使用,300字以内"

原则二:角色设定

给 AI 一个身份,它能更好地进入角色。

// 角色设定模板
const rolePrompt = `你是一个资深的前端架构师,有 8 年大厂经验。
擅长技术选型、代码规范制定和性能优化。
回答要专业但易懂,可以适当举例说明。`;

原则三:具体输出格式

指定输出格式,让结果可预测、可解析。

请按以下 JSON 格式输出:
{
  "bugType": "类型(语法错误/逻辑错误/性能问题)",
  "cause": "可能原因",
  "solution": "解决方案",
  "codeExample": "修复后的代码示例(如果需要)"
}

原则四:场景化约束

结合具体场景,给 AI 更精准的限制。

约束维度示例
长度约束回复不超过 200 字
风格约束面向初级开发者,通俗易懂
技术栈约束只使用 Vue3 Composition API,不使用 Options API
禁止项不要使用 any 类型,不要忽略错误处理

LangChain PromptTemplate 使用详解

基础语法

PromptTemplate 是 LangChain 中最基础的提示词模板组件,使用 {变量名} 作为占位符。

import { PromptTemplate } from "@langchain/core/prompts";

// 基础用法
const simpleTemplate = PromptTemplate.fromTemplate(
  "你是一个{role},请用{style}的风格回答:{question}"
);

const formatted = await simpleTemplate.format({
  role: "前端技术专家",
  style: "幽默风趣",
  question: "解释一下闭包"
});

console.log(formatted);
// 输出:你是一个前端技术专家,请用幽默风趣的风格回答:解释一下闭包

多变量与默认值

import { PromptTemplate } from "@langchain/core/prompts";

const template = new PromptTemplate({
  inputVariables: ["bug", "lang"],
  template: `你是一个前端代码审查专家。

【Bug描述】:{bug}
【目标语言】:{lang}

请完成以下任务:
1. 分析 Bug 原因(用 {lang} 回复)
2. 提供修复方案
3. 给出修复后的代码示例

注意:如果没有明确要求,默认使用 JavaScript。`,
  // 可以为变量设置默认值(需要配合 partial 使用)
});

// 部分填充(预置默认值)
const partialTemplate = await template.partial({
  lang: "TypeScript"
});

对话模板 ChatPromptTemplate

适用于多轮对话场景,支持 system、human、ai 等角色。

import { ChatPromptTemplate } from "@langchain/core/prompts";

const chatPrompt = ChatPromptTemplate.fromMessages([
  ["system", "你是一个{role},回答要{style},不超过{maxLength}字。"],
  ["human", "{question}"],
  ["ai", "好的,我正在思考..."],
  ["human", "请直接给出答案,不要废话"]
]);

const formatted = await chatPrompt.format({
  role: "前端 bug 排查专家",
  style: "简洁明了",
  maxLength: "100",
  question: "Vue3 中 ref 和 reactive 有什么区别?"
});

Few-Shot 示例提示词实战

Few-Shot(少样本学习)是通过提供几个示例,让模型理解你的期望输出格式和风格。

场景:前端代码生成

import { ChatOpenAI } from "@langchain/openai";
import { SystemMessage, HumanMessage } from "@langchain/core/messages";
import dotenv from "dotenv";

dotenv.config();

async function fewShotDemo() {
  // 1. 定义 Few-Shot 示例
  const examples = [
    {
      input: "写一个函数,判断是否为回文数",
      output: `/**
 * 判断一个整数是否为回文数
 * @param {number} x - 待判断的整数
 * @returns {boolean} - 是回文数返回 true,否则返回 false
 */
function isPalindrome(x: number): boolean {
  if (x < 0) return false;
  const str = x.toString();
  return str === str.split('').reverse().join('');
}`
    },
    {
      input: "写一个函数,实现防抖",
      output: `/**
 * 防抖函数
 * @param {Function} fn - 需要防抖的函数
 * @param {number} delay - 延迟时间(毫秒)
 * @returns {Function} - 防抖后的函数
 */
function debounce<T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timer: NodeJS.Timeout;
  return (...args: Parameters<T>) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}`
    }
  ];

  // 2. 手动构建示例文本
  const formattedExamples = examples.map(ex => 
    `用户需求:${ex.input}\n代码输出:\n${ex.output}`
  ).join("\n\n");

  // 3. 构建消息
  const systemPrompt = `你是一个资深前端工程师,擅长编写高质量、有注释的 TypeScript 代码。

以下是几个优秀的代码示例,请参考它们的风格:

${formattedExamples}

要求:
- 必须有 JSDoc 注释
- 使用 TypeScript 类型
- 考虑边界情况
- 代码风格保持一致`;

  const userPrompt = "写一个函数,实现数组去重(支持对象数组根据指定字段去重)";

  // 4. 调用模型
  const model = new ChatOpenAI({
    apiKey: process.env.DASHSCOPE_API_KEY,
    configuration: {
      baseURL: process.env.DASHSCOPE_API_URL,
    },
    model: "qwen-turbo",
    temperature: 0.3,
  });

  const messages = [
    new SystemMessage(systemPrompt),
    new HumanMessage(userPrompt)
  ];
  
  const response = await model.invoke(messages);

  console.log("生成的代码:\n");
  console.log(response.content);
}

fewShotDemo();

国内模型提示词优化技巧

技巧一:中文优先,避免中英混杂

❌ 避免:"Please write a function to 处理数组数据"
✅ 推荐:"请用 JavaScript 写一个函数,处理数组数据"

技巧二:控制提示词长度

阿里云百炼的 qwen-turbo 模型上下文窗口为 1M tokens,但提示词过长会影响响应速度。

// 将复杂提示词拆分为多个小提示词
const shortPrompts = [
  "角色设定:你是一个前端专家",
  "任务:分析以下代码的性能问题",
  "代码:" + codeSnippet
];

// 或者使用精简表达
const concisePrompt = `【角色】前端专家 | 【任务】分析性能问题 | 【代码】${codeSnippet}`;

技巧三:使用分隔符结构化

const structuredPrompt = `
=== 角色设定 ===
你是一个严谨的前端代码审查专家

=== 输入代码 ===
\`\`\`typescript
${userCode}
\`\`\`

=== 输出要求 ===
请按以下格式输出:
1. 严重程度:【高/中/低】
2. 问题描述:【具体问题】
3. 修复建议:【方案】
4. 优化后代码:【代码示例】

=== 注意事项 ===
- 不要输出多余的解释
- 优先关注性能和安全问题
`;

实战:前端 Bug 排查助手

完整代码实现

创建 src/bug-assistant.ts

import { ChatPromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import dotenv from "dotenv";

dotenv.config();

async function bugAssistant() {
  // 定义系统提示词 - 转义 JSON 中的花括号
  const systemPrompt = `你是一个专业的前端 Bug 排查助手。

【你的能力】
- 快速定位 JavaScript/TypeScript 代码中的问题
- 分析 Vue3 / React 框架相关的常见坑点
- 提供可执行的修复方案

【输出格式】
请严格按照以下 JSON 格式返回:
{{"bugType": "类型(语法错误/逻辑错误/性能问题/兼容性问题/框架使用错误)",
  "severity": "严重程度(高/中/低)",
  "rootCause": "根本原因分析(一句话概括)",
  "solution": "解决方案(具体步骤)",
  "fixedCode": "修复后的代码(如适用)",
  "prevention": "如何避免类似问题"}}

【注意事项】
- 如果无法确定,请说明需要更多信息
- 不要猜测,基于代码本身分析
- 保持专业、简洁`;

  // 定义用户提示词模板 - 使用转义的花括号
  const userPromptTemplate = `
【Bug 描述】
{description}

【相关代码】
\`\`\`{lang}
{code}
\`\`\`

【错误信息(如有)】
{errorMsg}

【环境信息】
- 框架:{framework}
- 版本:{version}
`;

  const prompt = ChatPromptTemplate.fromMessages([
    ["system", systemPrompt],
    ["human", userPromptTemplate]
  ]);

  const model = new ChatOpenAI({
    apiKey: process.env.DASHSCOPE_API_KEY,
    configuration: {
      baseURL: process.env.DASHSCOPE_API_URL,
    },
    model: "qwen-turbo",
    temperature: 0.2,  // Bug 排查需要准确,降低温度
  });

  const chain = prompt.pipe(model);

  // 测试用例:Vue3 响应式丢失问题
  const testCase = {
    description: "修改 reactive 对象后,页面没有更新",
    lang: "typescript",
    code: `const state = reactive({ count: 0 });

function increment() {
  // 这样做页面不会更新
  state = { count: state.count + 1 };
}`,
    errorMsg: "没有报错,但页面没反应",
    framework: "Vue3",
    version: "3.3.0"
  };

  console.log("🔍 正在分析 Bug...\n");
  console.log(`【Bug 描述】${testCase.description}`);
  console.log(`【代码】\n${testCase.code}\n`);

  const response = await chain.invoke(testCase);
  
  console.log("📋 分析结果:\n");
  console.log(response.content);
  
  // 尝试提取和解析 JSON
  try {
    // 从响应中提取 JSON 对象
    const content = response.content as string;
    const jsonMatch = content.match(/\{[\s\S]*\}/);
    if (jsonMatch) {
      const result = JSON.parse(jsonMatch[0]);
      console.log("🔖 Bug 类型:", result.bugType);
      console.log("⚠️ 严重程度:", result.severity);
      console.log("🎯 根本原因:", result.rootCause);
      console.log("💡 解决方案:", result.solution);
      if (result.fixedCode) {
        console.log("📝 修复代码:\n", result.fixedCode);
      }
      console.log("🛡️ 预防建议:", result.prevention);
    } else {
      // 如果不是 JSON 格式,直接输出
      console.log(content);
    }
  } catch (error) {
    console.error("JSON 解析失败,直接显示原始输出:");
    console.log(response.content);
  }
}

bugAssistant();

提示词效果对比:优化前 vs 优化后

对比测试

维度❌ 优化前✅ 优化后
提示词"帮我修复这个 bug" + 代码结构化提示词(角色+格式+示例+约束)
输出结构随意段落,可能遗漏关键信息固定 JSON 格式,包含 type/severity/solution/fixedCode
代码质量可能没有注释,类型不完整有 JSDoc,使用 TypeScript,考虑边界情况
响应时间较长(需要自己组织输出)更快(格式约束后生成效率高)
可用率~60%(需要人工二次处理)~95%(可直接解析使用)

实际输出对比

优化前输出:

你的代码有问题,state 不能重新赋值,要用 state.count++ 来修改。建议了解一下 Vue3 的响应式原理...

优化后输出(JSON):

{
  "bugType": "框架使用错误",
  "severity": "高",
  "rootCause": "reactive 对象被整体重新赋值,导致响应式连接丢失",
  "solution": "直接修改对象属性,不要重新赋值整个对象",
  "fixedCode": "const state = reactive({ count: 0 });\n\nfunction increment() {\n  state.count++;\n}",
  "prevention": "避免直接重新赋值 reactive 对象,而是修改其属性或使用 ref 来包装对象"
}

结语

提示词工程是 AI 应用开发中投入产出比最高的技能之一。掌握本文介绍的编写原则和 LangChain PromptTemplate 用法,你就能让 AI 输出质量提升 10 倍。

对于文章中错误的地方或有任何疑问,欢迎在评论区留言讨论!