用 Prompt 做 NLP 任务开发 —— 从零搭建一个模块化的 LLM NLP 系统

48 阅读6分钟

用 Prompt 做 NLP 任务开发 —— 从零搭建一个模块化的 LLM NLP 系统

前言

在传统的 NLP(自然语言处理)领域,构建一个情感分类、信息提取或文本摘要系统,往往需要熟练的机器学习工程师花费数天甚至数周的时间——收集标注数据、训练模型、调参、部署……门槛相当高。

而如今,随着大语言模型(LLM)的成熟,我们仅用几分钟、几十行代码,就能搭建起一套功能强大的 NLP 系统。本文将通过一个实际的项目示例,带你一步步了解如何使用 Prompt 工程来完成常见的 NLP 任务。


一、项目结构:模块化思想

先来看看这个小项目的目录结构:

nlp.demo/
├── readme.md          # 项目说明
├── client.mjs         # LLM 客户端对象
├── completion.mjs     # 任务完成函数
├── main.mjs           # 入口文件:文本摘要
└── main2.mjs          # 入口文件:情感分析 & 信息提取

这个结构体现了一个非常重要的工程思想——模块化

文件职责
client.mjs负责创建并导出 LLM 客户端实例(鉴权、配置)
completion.mjs封装通用的任务函数(发请求、拿结果)
main.mjs / main2.mjs单点入口,编写具体业务逻辑(Prompt + 调用)

为什么要模块化?

  • 可维护性:配置变更只需改 client.mjs,不会影响业务代码。
  • 可复用性getCompletion 函数一次编写,多个入口文件都能引入使用。
  • 关注点分离:鉴权、请求封装、业务逻辑各司其职,代码清晰易懂。

二、核心模块拆解

2.1 client.mjs —— 初始化 LLM 客户端

import { OpenAI } from 'openai';
import dotenv from 'dotenv';

dotenv.config();

const client = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: process.env.DEEPSEEK_API_BASE_URL
});

export default client;

这里做了三件事:

  1. 使用 dotenv 加载环境变量(.env 文件),敏感信息(API Key)不会硬编码在代码中。
  2. 创建 OpenAI 兼容的客户端实例(这里接入的是 DeepSeek,只要是 OpenAI 兼容接口的模型都可以用同样的方式接入)。
  3. 通过 export default 默认导出,方便其他模块引入。

小贴士export default 一个模块只能有一个,而 export(命名导出)可以有多个。如果模块只有一个核心对象需要对外暴露,用默认导出最简洁。

2.2 completion.mjs —— 封装通用请求函数

import client from './client.mjs';

export async function getCompletion(prompt) {
  const response = await client.chat.completions.create({
    model: process.env.DEEPSEEK_MODEL,
    messages: [
      { role: 'user', content: prompt }
    ]
  });
  return response.choices[0].message.content;
}

这个函数是整个系统的核心"发动机":

  • 接收一个 prompt(提示词),发送给 LLM。
  • 返回模型生成的文本内容。
  • async/await 异步处理,不阻塞主线程。

有了这个通用函数,后续无论做什么 NLP 任务,只需要专注于写好 Prompt 就够了。


三、ES6 语法特性速览

项目使用了 ES6(ES2015)模块语法,这也是现代 JavaScript 开发的基础。快速回顾几个关键特性:

3.1 let / const —— 块级作用域

// let 和 const 支持块级作用域,解决了 var 的声明提升问题
const apiKey = 'xxx';   // 简单数据类型不可重新赋值
let count = 0;           // 可以重新赋值

// const 修饰的对象可以修改属性,但不能改变指向
const obj = { name: '姚明' };
obj.city = '上海';  // ✅ 允许
obj = {};           // ❌ 报错,不能改变内存地址

3.2 解构赋值 —— 优雅提取数据

// 对象解构
let { name, city } = { name: '姚明', city: '上海' };
console.log(name, city);  // 姚明 上海

// 数组解构 + rest 运算符
let [coach, ...players] = ['111', '222', '333', '444', '555'];
console.log(coach);     // '111'
console.log(players);   // ['222', '333', '444', '555']

解构赋值不仅代码优雅,性能也比逐属性访问更好。

3.3 展开运算符(Spread)

let players1 = ['222', '333', '444'];
let players2 = ['123', '234', '345'];
let allPlayers = [...players1, ...players2];
// ['222', '333', '444', '123', '234', '345']

3.4 ESM 模块系统

import client from './client.mjs';         // 默认导入
import { getCompletion } from './completion.mjs'; // 命名导入
export default client;                      // 默认导出
export async function getCompletion() {}    // 命名导出

四、NLP 任务实战

下面我们来看看如何用 Prompt 完成四大经典 NLP 任务。核心思路:你不需要训练模型,你只需要设计好 Prompt。

4.1 情感分类(Sentiment Analysis)

判断一段文本的情感倾向——正面、负面或中性。这在电商、客服预警、产品质检等场景中非常重要。

const prompt = `
以下用三个反引号分隔的产品评论的情感是什么?
用一个单词回答,正面或负面。
评论文本:```${lamp_review_zh}```
`;

还可以进一步细化——识别具体情绪类型:

const prompt = `
识别以下评论的作者表达的情感。
包含不超过五个项目。
将答案格式化为以逗号分隔的单词列表。
评论文本:```${lamp_review_zh}```
`;
// → 满意、感激、开心、认可、信任
const prompt = `
以下用三个反引号分隔的产品评论是否表达了愤怒情感?
给出是或否的答案。
评论文本:```${lamp_review_zh}```
`;

4.2 信息提取(Information Extraction)

从非结构化文本中提取结构化信息,比如商品名、品牌等:

const prompt = `
从评论文本中识别以下项目:
- 评论者购买的商品
- 制造该商品的公司
评论文本用三个反引号分隔。将你的响应格式以"物品"和"品牌"为键的JSON对象。
如果信息不存在,请使用"未知"作为值。
评论文本:```${lamp_review_zh}```
`;
// → {"物品": "卧室灯", "品牌": "Lumina"}

进阶:一次提取多个维度并格式化输出

const prompt = `
从评论文本中识别以下项目:
- 情绪(正面或负面)
- 是否表达了愤怒?(是或否)
- 评论者购买的商品
- 制造该物品的公司

将你的响应格式化为JSON对象,以"sentiment""anger""product""brand"为键。
如果信息不存在,请使用"未知"作为值。
将anger值格式化为布尔值。
评论文本:```${lamp_review_zh}```
`;

4.3 主题推断(Topic Detection)

判断一段文本在讨论哪些主题:

const prompt = `
确定一下给定文本中讨论的五个主题。
每个主题用1-2个单词概括。
输出时用逗号分隔。
给定文本:```${story_zh}```
`;
// → 员工满意度, NASA, 调查结果, 政府承诺, 工作环境

4.4 文本摘要(Summarization)

对长文本进行概括,提取关键信息——这个功能对行政岗、编辑、管理者特别实用:

const prompt = `
你的任务是从电子商务网站上的产品评论中提取相关信息。
请对三个反引号之间的文本进行概括,最多20个字符。
评论文本:```${review}```
`;

还可以让摘要聚焦于特定维度:

const prompt = `
你的任务是从电子商务网站上生成一个产品评论的简短摘要。
请对三个反引号之间的文本进行概括,最多30个词汇,并且聚焦在产品价格和质量上。
评论文本:```${prod_review_zh}```
`;

五、Prompt 设计技巧总结

从上面的示例中可以提炼出几个 Prompt 设计的关键原则:

原则说明示例
明确角色告诉模型它要完成什么任务"你的任务是从产品评论中提取相关信息"
限定格式指定输出格式,便于后续解析"用JSON对象回答"、"用一个单词回答"
用分隔符把输入文本用 ``` 或 """ 包裹,避免混淆评论内容
控制长度限制输出字数或条目数"最多20个字符"、"不超过五个项目"
处理缺失告诉模型数据缺失时怎么做"如果信息不存在,请使用'未知'"

六、完整工作流

把整个流程串起来:

┌─────────────┐     ┌──────────────────┐     ┌─────────────┐
│  main.mjs    │────▶│  completion.mjs   │────▶│  client.mjs  │
│  (业务逻辑)   │     │  getCompletion()  │     │  OpenAI API  │
│              │     │                   │     │              │
│  1.准备文本   │     │  接收 prompt       │     │  DeepSeek    │
│  2.设计Prompt │     │  调用 LLM          │     │  LLM 模型    │
│  3.处理结果   │◀────│  返回结果          │◀────│              │
└─────────────┘     └──────────────────┘     └─────────────┘
  1. 在 client.mjs 中配置好 API Key 和 Base URL,创建客户端。
  2. 在 completion.mjs 中封装通用的 getCompletion(prompt) 函数。
  3. 在 main.mjs 中编写 Prompt,调用 getCompletion,拿到结果。

七、结语

"仅用几分钟,我们就可以构建多个用于对文本进行推理的系统,而以前需要熟练的机器学习人员数天到数周的时间。"

这不仅仅是一句口号,而是正在发生的技术变革。Prompt 工程让 NLP 能力变得前所未有的平民化:

  • 不需要 收集标注数据
  • 不需要 训练模型
  • 不需要 懂机器学习算法
  • 只需要 会写 Prompt,会调 API

而一个良好的模块化架构(像本项目这样),则让这套系统更容易维护、扩展和复用。随着你对 Prompt 工程的深入理解,你可以轻松地接入更复杂的 NLP 流程——多轮对话、链式推理、工具调用等等。

这就是 AI 时代的软件开发——你不再是训练模型,而是在设计指令。