很多人学 AI,一上来就去调各种框架,结果 Prompt 没写好,Agent 动不动就“胡说八道”。其实,Prompt Engineering 才是一切 AI 应用的起点。但传统的 Prompt 教程是什么呢?一段 Python 代码,改完参数重新运行,看终端输出。做前端的人肯定受不了这个 —— 太不直观了。今天,我就用前端最熟悉的 Next.js + LangChain.js,带你搭建一个 Prompt 实验室。你可以在网页上实时修改系统提示词、调整模型温度,然后马上看到 AI 的回复变化。整个过程你只需要懂 JS 和 React。- Prompt 的核心要素:系统消息、用户消息、温度参数。
- 如何用 LangChain.js 统一调用不同模型。
最终效果:一个左侧是调参面板,右侧是聊天窗口的 Prompt 实验工具。你可以:- 切换 AI 的角色(如“毒舌评论员”、“暖心助手”)
- 拖动滑块调整 temperature(控制创造性)
这本质上是一个“前端化”的 Prompt 教学工具,也是你后续打造复杂 Agent 的原型。System Message(系统提示词) 设定 AI 的全局人设、规则 像 CSS 全局样式,影响所有回复User Message(用户提示词) 单次提问的具体内容 像组件 props,每次不同另外还有一个重要参数:temperature(温度),范围 0~2。我们要做的界面,就是让你能动态调整系统提示词和温度,并观察效果。第二步:后端实现——用 LangChain.js 写一个“受控”的模型1. 初始化项目(注意: 1、langChain 要求 Node.js ≥ 20.x )npx create-next-app@latest prompt-lab --typescript --tailwindcd prompt-labnpm install langchain @langchain/openai
创建 app/api/chat/route.ts:import { NextRequest, NextResponse } from 'next/server';import { ChatOpenAI } from "@langchain/openai";import { HumanMessage, SystemMessage } from "@langchain/core/messages";export async function POST(req: NextRequest) { try { const { systemPrompt, userMessage, temperature } = await req.json(); // 初始化模型,temperature 由前端控制 const model = new ChatOpenAI({ modelName: "gpt-3.5-turbo", temperature: temperature ?? 0.7, maxRetries: 1, timeout: 30000, configuration: { baseURL: process.env.OPENAI_API_BASE || "https://api.openai.com/v1", }, }); // 构建消息数组:系统提示词 + 用户消息 const messages = [ new SystemMessage(systemPrompt || "你是一个有帮助的助手。"), new HumanMessage(userMessage || "你好"), ]; const response = await model.invoke(messages); return NextResponse.json({ success: true, reply: response.content, }); } catch(error) { return NextResponse.json({ success: false, error: "调用失败" }, { status: 500 }); }}
3. 配置环境变量(当调用 model.invoke(messages) 时,LangChain 底层会:1. 检查环境变量 OPENAI_API_KEY 2. 使用该密钥向 OpenAI API 发起请求 3. 如果密钥不存在或无效,会抛出认证错误)OPENAI_API_KEY=你的apiKeyOPENAI_API_BASE= api地址
(免费密钥获取,可关注公众号,点击下方免费密钥即可发送获取密钥方法)核心逻辑就这几行。LangChain.js 帮你把不同模型(OpenAI、Claude 等)封装成统一接口,以后切换模型不用改前端代码。第三步:前端实现——Prompt 实验室的交互灵魂我们要做一个左右分栏的布局,左边调参,右边展示对话历史。"use client";import { useState } from "react";export default function Home() { const [systemPrompt, setSystemPrompt] = useState("你是一个有帮助的助手。"); const [temperature, setTemperature] = useState(0.7); const [userInput, setUserInput] = useState(""); const [chatHistory, setChatHistory] = useState<{role: string; content: string}[]>([]); const [loading, setLoading] = useState(false); const sendMessage = async () => { if (!userInput.trim() || loading) return; setLoading(true); const newHistory = [...chatHistory, { role: "user", content: userInput } ]; setChatHistory(newHistory); setUserInput(""); try { const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ systemPrompt, userMessage: userInput, temperature }), }); const data = await res.json(); setChatHistory([...newHistory, { role: "assistant", content: data.reply } ]); } catch { setChatHistory([...newHistory, { role: "assistant", content: "请求失败" } ]); } finally { setLoading(false); } }; return ( <divclassName="flex h-screen max-w-6xl mx-auto p-4 gap-4"> {/* 左侧控制面板 */} <divclassName="w-80 bg-gray-50 p-4 rounded-lg space-y-4"> <h2className="font-bold text-lg">⚙️ Prompt 控制台</h2> <div> <labelclassName="block text-sm font-medium mb-1">系统提示词(人设)</label> <textarea className="w-full border rounded p-2 h-32 text-sm" value={systemPrompt} onChange={(e) => setSystemPrompt(e.target.value)} /> </div> <div> <labelclassName="block text-sm font-medium mb-1"> Temperature: {temperature} </label> <input type="range" min="0" max="2" step="0.1" value={temperature} onChange={(e) => setTemperature(parseFloat(e.target.value))} className="w-full" /> <divclassName="flex justify-between text-xs text-gray-400"> <span>严谨 (0)</span> <span>平衡 (1)</span> <span>狂放 (2)</span> </div> </div> {/* 预设模板 */} <div> <labelclassName="block text-sm font-medium mb-2">快速预设</label> <divclassName="space-y-1"> {[ { label: "毒舌评论员", prompt: "你是一个尖酸刻薄的评论员,喜欢吐槽一切。" }, { label: "暖心朋友", prompt: "你是一个温暖、鼓励人的好朋友,总是看到光明面。" }, { label: "代码导师", prompt: "你是一个资深前端工程师,回答简洁,直接给代码方案。" }, ].map((preset) => ( <button key={preset.label} onClick={() => setSystemPrompt(preset.prompt)} className="block w-full text-left px-3 py-2 bg-white border rounded text-sm hover:bg-blue-50" > {preset.label} </button> ))} </div> </div> </div> {/* 右侧聊天区域 */} <divclassName="flex-1 flex flex-col border rounded-lg"> <divclassName="flex-1 overflow-y-auto p-4 space-y-3"> {chatHistory.map((msg, i) => ( <divkey={i}className={`flex ${msg.role === "user" ? "justify-end" : "justify-start"}`}> <divclassName={`p-3rounded-lgmax-w-[70%] ${ msg.role === "user" ? "bg-blue-500 text-white" : "bg-gray-100" }`}> {msg.content} </div> </div> ))} {loading && <divclassName="text-gray-400 text-sm">AI 正在思考...</div>} </div> <divclassName="border-t p-4 flex gap-2"> <input className="flex-1 border rounded-lg p-3" value={userInput} onChange={(e) => setUserInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && sendMessage()} placeholder="试试问同一个问题,然后切换左侧人设..." /> <button className="bg-blue-500 text-white px-6 rounded-lg disabled:opacity-50" onClick={sendMessage} disabled={loading} > 发送 </button> </div> </div> </div> );}
现在运行 npm run dev,在浏览器打开 http://localhost:3000。先用温度 0 问:“解释一下 JavaScript 闭包”。再调到 2 问同样的问题。你会发现第二次回答可能突然跑题到“我奶奶的毛衣”,这就是 Temperature 太高带来的随机性。分别用“暖心朋友”和“毒舌评论员”的人设测试,观察回复风格的巨大差异。这就是 Prompt Engineering 的直观教学——不需要看终端,一切都在界面里实时反馈。- 用前端界面把 Prompt 调试变得可视化、可交互
基础打好了,下一步就是让 AI 能够“做事”。我会带你实现 Function Calling——让模型调用你定义的工具函数(查天气、算数学、搜资料),并且把这些调用过程在前端实时展示出来。那才是真正 Agent 的开始。如果这篇教程帮到了你,欢迎点赞、收藏,并且关注我的专栏《前端转 Agent 全栈工程师实战》,我们一起用代码为 AI 装上漂亮、好用的界面。