基于 Prompt 工程的 NLP 系统开发实践 —— 以 DeepSeek 大模型为例

摘要 :传统自然语言处理(NLP)任务的开发往往需要机器学习专家耗费数天甚至数周的时间。如今,借助大语言模型(LLM)和精心设计的 Prompt,开发者仅需几分钟即可构建出功能强大的 NLP 系统。本文以 nlp-demo 项目为例,深入剖析如何通过模块化架构、Prompt 工程技巧与 DeepSeek 大模型,快速实现情感分析、信息提取、主题推断与文本总结等核心 NLP 能力。


一、项目概览与架构总览

1.1 为什么选择 Prompt 工程做 NLP

传统的 NLP 开发流程通常包括数据收集、数据清洗、特征工程、模型训练、评估调优等多个环节,门槛高、周期长。而基于 LLM 的 Prompt 工程直接通过自然语言指令驱动模型完成推理,核心转变在于:

传统方式 Prompt 工程方式
需要标注大量训练数据 零样本或少样本即可启动
训练专用模型(数天~数周) 编写 Prompt(数分钟)
每个任务一个模型 一个模型覆盖多种任务
需要 ML 专业知识 自然语言描述即可

1.2 项目架构图

本项目采用 ES Module 模块化架构,将 LLM 客户端、推理函数和业务入口清晰分离:

架构核心思想 :单向依赖,逐层抽象。底层不感知上层,上层通过 import 引入下层能力。

1.3 项目文件结构

bash 复制代码
nlp-demo/
├── .env                # API 密钥与模型配置
├── package.json        # 项目元信息与依赖声明
├── pnpm-lock.yaml      # 依赖锁定文件
├── client.mjs          # LLM 客户端模块(底层)
├── completion.mjs      # 通用推理函数(中间层)
├── main.mjs            # NLP 任务入口 1(上层)
├── main2.mjs           # NLP 任务入口 2(上层)
└── readme.md           # 项目说明

二、模块化设计:ES6 语法基石

在深入 NLP 任务之前,先理解支撑项目模块化的 ES6 关键特性。正如 readme.md 中所言:

"ES6 是 JavaScript 在 2015 年推出的新版本,变化比较大,目标是让 JS 成为一个企业级大型项目开发语言。"

2.1 let / const:块级作用域

关键规则:

  • letconst 不可重复声明
  • const 声明的简单数据类型不可重新赋值
  • const 声明的复杂数据类型(对象/数组)可以修改内部属性,但不可改变其指向的内存地址

2.2 解构赋值(Destructuring)

解构赋值是本项目中大量使用的语法糖,从对象或数组中直接提取值到变量中:

javascript 复制代码
// 对象解构 --- 简洁提取属性
let { name, city } = { name: "李四", city: "上海" };
// 等价于:
// let name = obj.name;
// let city = obj.city;

// 数组解构 + rest 运算符
let [coach, ...players] = ['范甘迪', '姚明', '麦迪', '穆托姆博', '弗朗西斯'];
console.log(coach);   // '范甘迪'
console.log(players); // ['姚明', '麦迪', '穆托姆博', '弗朗西斯']

为什么解构比点运算符性能更好? 解构一次性将属性值复制到局部变量,后续访问无需反复查找原型链,引擎优化空间更大。

2.3 ESM 模块化

本项目使用 ES Module 语法代替传统的 CommonJS:

  • export default :每个模块只能有一个默认导出(如 client.mjs 导出 client 实例)
  • export(命名导出) :可以导出多个(如 completion.mjs 导出 getCompletiongenImage
  • import ... from:按需引入,支持解构式导入

三、核心组件深度解析

3.1 client.mjs --- LLM 客户端封装

代码解读(client.mjs):

javascript 复制代码
import { OpenAI } from 'openai';
import dotenv from 'dotenv';

dotenv.config();  // 加载 .env 文件中的环境变量

const client = new OpenAI({
    apiKey: process.env.DEEPSEEK_API_KEY,   // DeepSeek API 密钥
    baseURL: process.env.DEEPSEEK_BASE_URL  // https://api.deepseek.com/v1
});

export default client;  // 默认导出,全局唯一实例

设计要点

  • 使用 OpenAI 兼容 SDK,与 DeepSeek 服务无缝对接(DeepSeek 提供了与 OpenAI API 兼容的接口格式)
  • dotenv.config() 在模块顶层执行,确保环境变量在使用前加载
  • 单例模式:整个项目共享一个 client 实例,避免重复初始化连接

3.2 completion.mjs --- 通用推理函数

代码解读(completion.mjs):

javascript 复制代码
import client from './client.mjs';

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

export async function genImage(prompt) {
    // 预留:图像生成接口
}

设计要点

  • 将 "发送请求 → 获取回复" 封装为单一函数,上层调用者只需关心 Prompt 内容
  • 返回 choices[0].message.content 直接是文本内容,消除了上层解析 JSON 的需要
  • genImage 作为扩展点,展示了模块的可扩展性

3.3 .env --- 环境配置分离

ini 复制代码
DEEPSEEK_API_KEY=sk-xxx...
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_MODEL=deepseek-v4-flash

为什么将配置外置?

  • 🔒 安全性 :API Key 不入库,.env 加入 .gitignore
  • 🔄 可切换性:更换模型或服务商只需修改环境变量,无需改代码
  • 🚀 部署友好:不同环境(开发/测试/生产)可使用不同配置

四、NLP 任务实战

这是本文的核心章节。我们将逐一剖析四种 NLP 任务的 Prompt 设计与实现。

4.1 情感分析(Sentiment Analysis)

业务价值:电商评论情感分析可用于客服预警、产品质检、舆情监控等场景。

4.1.1 基础情感分类

main.mjs 中,我们首先尝试对一条中文产品评论做情感分类:

javascript 复制代码
const lamp_review_zh = `
    我需要一盏漂亮的卧室灯,这款灯具有额外的储物功能,价格也不算太高。
    我很快就收到了它。在运输过程中,我们的灯绳断了,但是公司很乐意寄送了一个新的。
    几天后就收到了。这款灯很容易组装。我发现少了一个零件,于是联系了他们的客服,
    他们很快就给我寄来了缺失的零件!
    在我看来,Lumina 是一家非常关心顾客和产品的优秀公司!
`;

Prompt 演进过程

阶段 Prompt 设计 特点
V1 基础版 以下用三个反引号分隔的产品评论的情感是什么? 开放式,回答不可控
V2 约束版 用一个单词回答:正面 或 负面 约束输出格式,结果可预期
V3 定向版 以下产品评论是否表达愤怒?给出是或否的答案 针对特定情感维度

💡 关键技巧 :通过 用一个单词回答 这样的格式约束,可以极大提升输出的一致性和可解析性。

4.1.2 复合信息提取

更高级的做法是一次性提取多个维度的信息:

javascript 复制代码
Prompt 设计:
从以下评论文本识别以下项目:
- 情绪(正面或负面)
- 是否表达了愤怒?(是或否)
- 评论者购买的商品
- 制造该物品的公司

将您的响应格式化为 JSON 对象,以 "sentiment"、"anger"、"product"、"brand" 为键。
将 anger 格式化为布尔值。
如果信息不存在,请使用 "未知" 作为值。

输出的结构化 JSON 可以直接被下游程序消费:

json 复制代码
{
    "sentiment": "正面",
    "anger": false,
    "product": "卧室灯",
    "brand": "Lumina"
}

4.2 信息提取(Information Extraction)

信息提取的目标是从非结构化文本中抽取结构化字段。本项目中展示了一个关键模式:

Prompt 设计要点

javascript 复制代码
从以下评论文本识别以下项目:
- 评论者购买的商品
- 制造该商品的公司

将你的响应格式以 "物品(product)" 和 "品牌(brand)" 为键的 JSON 对象。
如果信息不存在,请使用 **未知** 作为值。

三个关键技巧:

  1. 明确输出键名(中文+英文对照,消除歧义)
  2. 规定缺省值"未知" --- 避免模型胡编乱造)
  3. 使用分隔符(三个反引号包裹文本,防止注入攻击)

4.3 主题推断(Topic Inference)

主题推断用于从长文本中自动识别讨论的核心话题。在 main.mjs 中,我们使用了一段关于政府部门的调查新闻:

javascript 复制代码
const story_zh = `
    在政府最近进行的一项调查中,要求公共部门的员工对他们所在部门的满意度进行评分。
    调查结果显示,NASA 是最受欢迎的部门,满意度为 95%。
    ...
    调查还显示,社会保障管理局的满意度最低,只有 45%的员工表示他们对工作满意。
`;

Prompt 设计

bash 复制代码
确定以下给定文本中讨论的五个主题。
每个主题用一到两个单词概括。
输出时用逗号分隔。
给定文本:${story_zh}

预期输出示例:NASA, 员工满意度, 政府调查, 公共部门, 社会保障管理局

main2.mjs 中进一步展示了主题判断模式------给一个候选主题列表,让模型判断每个主题是否在文本中被讨论:

复制代码
判断主题列表中的每一项是否是给定文本中的一个话题。
以列表的形式给出答案,每个主题用 0 或 1。
主题列表:美国航天局, 地方政府, 工程, 员工满意度, 联邦政府

输出示例:[1, 0, 0, 1, 1]

两种模式的选择:开放发现适合探索性分析("这篇文章讲了什么");候选判断适合监控场景("这篇文章是否涉及我们关心的 N 个话题")。

4.4 文本总结(Text Summarization)

文本总结是最实用的 NLP 能力之一,可帮助行政、编辑、管理层快速从长文本中提取关键信息。

main2.mjs 中,使用一条熊猫公仔的产品评论作为示例:

javascript 复制代码
const prod_review_zh = `
    这个熊猫公仔是我给女儿的生日礼物,她很喜欢,去哪都带着。
    公仔很软,超级可爱,面部表情也很和善。但是相比于价钱来说,
    它有点小,我感觉在别的地方用同样的价钱能买到更大的。
    快递比预期提前了一天到货,所以在送给女儿之前,我自己玩了会。
`;

文本总结的三种聚焦模式

三种 Prompt 对比

聚焦维度 Prompt 关键指令 适用场景
通用总结 概要,最多30个词汇 快速浏览概况
聚焦运输 概要...聚焦于产品运输上 物流部门质检
聚焦价格和质量 概要...聚焦于产品价格和质量上 产品部门评估

批量处理多条评论

main2.mjs 末尾,展示了如何对 4 条不同类型产品的评论(公仔、灯具、牙刷、搅拌机)进行批量总结:

javascript 复制代码
const reviews = [review_1, review_2, review_3, review_4];
for (let review of reviews) {
    const prompt = `
        你的任务是从电子商务网站上的产品评论中提取相关信息。
        请对三个反引号之间的文本进行概括,最多20个字符。
        评论文本:\`\`\`${review}\`\`\`
    `;
    const response = await getCompletion(prompt);
    console.log(response, '\n');
}

五、Prompt 工程核心技巧总结

通过以上四个 NLP 任务的实战,我们可以归纳出以下 Prompt 工程的关键技巧:

5.1 技巧全景图

5.2 技巧详解

① 分隔符(Delimiters)

r 复制代码
评论文本:\`\`\`${user_input}\`\`\`

使用三个反引号(或其他清晰的分隔符如 ---、XML 标签)包裹用户输入:

  • 防止用户输入中的恶意指令覆盖系统指令(Prompt Injection 防御)
  • 让模型清晰分辨"指令"和"数据"

② 输出格式控制

从宽松到严格的输出控制谱系:

arduino 复制代码
宽松 ←------------------------------------------------------------------------------------→ 严格

"情感是什么?"    "用一个单词回答"    "以JSON格式输出"    "以{key:value}格式,布尔值"
(自由文本)      (词级约束)         (结构化)           (精确 Schema)

③ 聚焦式 Prompt

同一段文本,不同的聚焦指令得出完全不同的总结:

复制代码
聚焦运输    → 强调物流速度、包装完整性
聚焦价格    → 强调性价比、价格波动
聚焦质量    → 强调产品材质、做工细节

这对于企业中对不同部门(物流部、产品部、客服部)提供定制化信息非常有价值。

④ 鲁棒性兜底

markdown 复制代码
如果信息不存在,请使用 **未知** 作为值。

这条指令防止模型在信息缺失时"自由发挥",确保了输出的一致性和可解析性。


六、技术栈与依赖

组件 版本 作用
openai ^6.42.0 OpenAI 兼容 SDK,用于调用 DeepSeek API
dotenv ^17.4.2 .env 文件加载环境变量
DeepSeek v4-flash - 提供推理能力的大语言模型
Node.js - 运行时环境

七、总结与展望

核心收获

  1. 模块化架构 是工程化的基石------clientcompletionmain 的分层设计让代码可维护、可复用
  2. Prompt 即代码------Prompt 的质量直接决定了 NLP 系统的输出质量,需要像写代码一样精心设计和迭代
  3. 从几天到几分钟------借助 LLM,构建 NLP 系统的门槛从机器学习专家降低到任何会写自然语言的开发者

可扩展方向

🚀 下一步 :有了这个基础框架,你可以轻松添加更多 NLP 任务------如命名实体识别(NER)、文本翻译、代码生成、对话系统等。所有任务共享同一个 getCompletion 函数,唯一变化的是 Prompt 的内容和结构。


相关推荐
jimi11262 小时前
从零理解 Transformer
人工智能·深度学习·nlp
元拓数智1 天前
跨库NL2SQL可信落地的核心:用IntaLink破解数据关系“迷雾”
数据库·人工智能·ai·nlp·agent·llama
yuanyuan2o23 天前
Transformers NLP 任务:阅读理解问答
人工智能·算法·自然语言处理·nlp·github
m沐沐5 天前
【机器学习】NLP---用 Python+TF-IDF 给《红楼梦》自动提取关键词
人工智能·python·机器学习·自然语言处理·nlp·中文分词·tf-idf
「維他檸檬茶」5 天前
大模型算法学习2026.6.1
学习·算法·大模型·nlp
yuanyuan2o26 天前
模型预训练:Hugging Face Transformers 基础
算法·ai·语言模型·自然语言处理·nlp·深度优先
极光代码工作室9 天前
基于NLP的招聘信息关键词分析系统
python·深度学习·自然语言处理·nlp
YUDAMENGNIUBI12 天前
day30_fasttext分类任务
nlp
Lyn_Li13 天前
给句子做个“语义审计”:从词向量到句子向量的方法论
nlp·bert·词向量·sif·句子向量