☰
用Python从零实现AI文案分析助手:情感识别、关键词提取与摘要生成
2026/10/19 6:27:29 网站建设 项目流程

先别急着写代码。很多人学Python最痛苦的一点,就是语法看了不少,但始终不知道能拿它做什么。这次我带你写一个真正能用的AI小工具,全程用Python,不涉及训练模型这类复杂操作,只要照着步骤走,就能在命令行里跑起来。这个工具叫AI文案分析助手:你把一段文字丢给它,它会自动给出情感倾向(正面、负面还是中性)、提取出这句话里的关键词、再浓缩成一句摘要。无论是分析评论区、整理用户反馈,还是想快速判断一篇文章的态度,它都能帮上忙。

这篇适合谁?适合已经学过Python基本语法、但还从未把AI能力接进自己程序里的初学者。如果你基础偏弱,也没关系,代码我会逐行解释,照着敲一遍也能跑通。整个过程分四块:先想清楚程序要做什么,再配置Python环境,然后实现在线AI模式,最后给一个不联网也能运行的离线保底方案,顺便把调试和排错方法演示一遍。整篇看下来,你对“AI小工具是怎么从零写出来的”会有一个清晰的画面。

1. 先想清楚再动手:这个AI小工具到底要做什么

1.1 新手做AI项目,最容易被忽略的是需求拆解

刚接触Python和AI的新手,最常见的路径是找到一个现成的demo,复制、运行、看到输出,然后……没有然后。因为demo是别人想清楚之后写出来的,你只是按了一下运行键。真正要自己动手做一个工具,第一步不是写代码,而是把“模糊想法”变成“明确需求”。

我给自己定了一个非常小的需求:做一个命令行程序,用户输入一段中文文字,程序输出三个结果——情感倾向、关键词列表、一句话摘要。为什么选这个需求?它足够小,不牵扯训练模型和调参;它足够实用,不管是分析商品评价、监控评论区还是整理用户反馈,都会用到;它还能覆盖一次完整AI开发的核心链路:调用接口、构造输入、解析输出、处理异常。新手把这些流程完整走一遍,以后再写别的AI小工具,其实就是套同一个壳。

顺便说一句,新手很容易在“做什么”上面摇摆,今天想做聊天机器人,明天想做图片识别,最后哪个都没做完。建议先克制住,挑一个两三小时能写完的项目。第一次成功跑通带来的正反馈,比项目本身有多大用处重要得多。

1.2 在线大模型接口和离线本地方案,怎么选

确定好需求之后,紧接着就是技术选型。这个工具的“AI核心”有两类实现路径:

方案效果联网要求成本配置难度
在线大模型接口理解力强,能处理复杂语义需要联网小额或免费额度低,申请密钥即可调用
离线轻量库只能做基础文本分析不需要免费低,安装依赖即可

在线模式的优势很明显:模型的理解能力强,一句话里隐含的态度、转折、反讽都能识别出来,而且不需要我们自己实现算法。离线模式的优势是稳定可控:不依赖网络、不消耗接口额度、隐私性好,但效果相对粗糙,遇到复杂表达容易判断不准。

我给这个工具的定位是“主在线、副离线”。也就是说,在线大模型作为主力,离线轻量库作为兜底,防止没有网络或者接口额度用尽的情况。两个方案都写出来,还有一个好处:可以对比学习同一个需求,用不同技术路线实现起来差别在哪里。这种对比带来的理解深度,是只看教程很难获得的。

1.3 画清楚功能清单和验收标准

需求拆完之后,把功能清单写清楚:

  • 输入:一段中文文本,建议长度在20字以上,太短的文本信息量不够,模型也不好判断
  • 输出:情感标签(positive / negative / neutral)、把握度(模型自评的置信程度)、Top 5关键词、不超过30字的一句话摘要
  • 交互方式:命令行工具,支持直接输入文本,也支持从文件读取内容

验收标准也很简单:输入“这家咖啡店氛围很好,店员热情,咖啡口感醇厚,价格也合理,我下次还会再来。”程序能输出合理的正面评价结果,关键词和摘要都能贴合原文。

这个验收标准非常重要。新手写代码容易陷入“无限优化”的状态,总觉得还能再改改,却说不清什么叫“完成”。有验收标准,你就知道自己跑到哪一步可以停下来,下一步该往哪个方向扩展。

2. 5分钟搭好Python环境和项目骨架

2.1 安装Python和代码编辑器,别在这一步纠结

Python 3.9以上的版本都够用,建议直接选择最新稳定版。去Python官方网站下载安装包,安装时勾选“Add Python to PATH”,其他选项按默认来。装完在终端里执行python --version,能输出版本号就说明装好了。

编辑器选一个自己用着顺手的免费编辑器就行,带语法高亮和代码补全的最好。如果你之前完全没用过编辑器,也不用纠结太久,先装一个常见的免费编辑器,打开项目目录,开始写代码。编辑器只是工具,写代码本身才是核心,等用熟了再换也不迟。

2.2 虚拟环境:给项目单独开一个房间

项目到手,第一件事是创建一个虚拟环境。虚拟环境相当于给这个项目单独开一个房间,你在里面安装的任何依赖都不会影响系统全局,其他项目的依赖也不同担心被覆盖。

在终端里执行以下命令:

mkdir ai-text-assistant cd ai-text-assistant python -m venv venv

目录创建好之后需要激活虚拟环境。Windows PowerShell里激活命令是venv\Scripts\Activate.ps1,macOS或Linux是source venv/bin/activate。激活成功之后,终端提示符前面会出现(venv)标记,这时你运行Python、安装库,都会在这个虚拟环境里进行。

有个小坑提醒一下:Windows PowerShell默认可能会禁止执行脚本,如果激活时报错,可以在PowerShell里运行一次Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser,之后再激活就正常了。这台机器只针对当前用户放开,安全性不会有问题。

2.3 安装项目依赖

接下来安装依赖。这个工具用到的第三方库不多:

pip install requests jieba snownlp python-dotenv

每个库干什么的,先心里有数:

  • requests:Python里最常用的HTTP请求库,用来调用在线大模型接口
  • jieba:中文分词库,离线模式提取关键词要用
  • snownlp:轻量级中文情感分析库,离线模式判断情感倾向要用
  • python-dotenv:读取.env文件里的配置,用来管理密钥,避免把密钥硬编码进代码

装完之后可以用pip list验证,看到这几个包出现在列表里,环境就准备好了。

2.4 项目文件结构,提前分好工

项目里文件比较多,如果全部堆在同一个文件里,后面改起来会很痛苦。我习惯从一开始就把代码按职责拆开:

ai-text-assistant/ ├── main.py # 程序入口,负责和用户交互 ├── ai_analyze.py # 在线大模型分析逻辑 ├── local_analyze.py # 离线本地分析逻辑 ├── config.py # 统一读取配置和选择分析模式 ├── .env # 存放密钥和配置项 └── requirements.txt # 依赖清单

这个结构不复杂,但能让你一眼看清每个文件负责什么。以后想加功能,知道该改哪个文件;出问题了,也知道该去哪个文件排查。

3. 在线AI模式:让大模型帮你分析文本

3.1 申请密钥和准备接口信息

在线模式的原理很简单:你本地写一个请求,发给远端的大模型服务,拿到返回结果之后解析出来。整个过程不需要你理解模型内部怎么运作,只需要按照接口规则把请求发对。

你需要准备三样东西:一个能提供大模型接口的服务商账号、一个API Key、以及对应的接口地址和模型名称。现在很多平台都提供免费额度,学习阶段完全够用,不用急着花钱。

把密钥和配置写进.env文件:

API_KEY=你的密钥 BASE_URL=https://api.your-provider.com/v1 MODEL_NAME=your-model-name

注意,.env文件不要提交到公开的代码仓库里。密钥一旦泄露,别人就能用你的额度,甚至产生额外费用。用环境变量管理密钥是安全底线,也是职业习惯。

3.2 封装一个通用的聊天请求函数

在线大模型接口的调用方式,本质上就是一个HTTP POST请求。我们先用requests写一个通用的请求函数:

import os import requests from dotenv import load_dotenv load_dotenv() API_KEY = os.getenv("API_KEY") BASE_URL = os.getenv("BASE_URL", "https://api.your-provider.com/v1") MODEL_NAME = os.getenv("MODEL_NAME", "your-model-name") def chat_completion(messages, temperature=0.3): url = f"{BASE_URL}/chat/completions" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", } payload = { "model": MODEL_NAME, "messages": messages, "temperature": temperature, } resp = requests.post(url, headers=headers, json=payload, timeout=30) resp.raise_for_status() data = resp.json() return data["choices"][0]["message"]["content"]

几个关键点说一下。Authorization请求头里放的是密钥,这是服务商识别你身份的凭证;timeout=30防止网络卡住时程序无响应;resp.raise_for_status()会在请求状态码不是2xx时直接抛异常,省去手动判断的麻烦。

这里有个新手很容易踩的坑:大模型接口的返回,最外层是一个HTTP响应的JSON结构,里面有choices、usage等字段,而模型真正回复的内容在data["choices"][0]["message"]["content"]。这点我们等下解析时还会碰到,代码写多了自然就熟悉了。

3.3 设计Prompt:让模型按固定JSON格式返回

请求函数写完,接下来是整个在线模式最核心的部分:Prompt设计。很多新手以为Prompt就是随便写一句话让AI做件事,但真正做工程化工具,Prompt必须严谨,因为它直接影响程序能否正确解析结果。

我设计的系统提示词长这样:

ANALYZE_SYSTEM_PROMPT = """你是一个文本分析助手。请分析用户输入的中文文本,并只输出一个JSON对象,不要输出任何额外文字,不要使用代码块标记。 JSON格式如下: { "sentiment": "positive/negative/neutral", "confidence": 0.0~1.0, "keywords": ["关键词1", "关键词2"], "summary": "不超过30字的一句话摘要" } """

你在提示词里告诉模型“只输出JSON”,模型大概率会遵守,但有时候还是会在JSON前后加一句解释,或者用代码块把它包起来。所以代码里必须做容错解析:

import json import re def parse_json(raw): raw = raw.strip() if raw.startswith("```"): raw = re.sub(r"^```[a-zA-Z]*\s*|\s*```$", "", raw) return json.loads(raw)

这个函数先把代码块标记剥掉,再交给json库解析。别小看这几行,实际用下来,模型偶尔不守规矩的时候,这个函数能帮你避免很多崩溃。

3.4 在线分析的完整封装

把请求函数和解析函数组合起来,就得到在线分析函数:

def analyze_with_llm(text): messages = [ {"role": "system", "content": ANALYZE_SYSTEM_PROMPT}, {"role": "user", "content": f"请分析这段文本:{text}"}, ] raw = chat_completion(messages, temperature=0.3) return parse_json(raw)

这里用了temperature=0.3。temperature控制生成结果的随机性,数值越高越有创造力,越低越稳定。分析文本、结构化输出这类任务,稳定比创造性重要,所以调低一点。

3.5 把三大功能串进命令行主程序

在线分析函数封装好之后,写一个最小可用的命令行入口:

def main(): text = input("请输入要分析的文本:").strip() if not text: print("文本不能为空") return result = analyze_with_llm(text) print(f"情感:{result.get('sentiment')}(把握度 {result.get('confidence')})") print(f"关键词:{'、'.join(result.get('keywords', []))}") print(f"摘要:{result.get('summary')}") if __name__ == "__main__": main()

运行时,程序会先等待你输入文本,然后调用大模型接口,最后把情感、关键词、摘要格式化打印出来。输入前面那句咖啡店的例子,理想输出大概是:

情感:positive(把握度 0.91) 关键词:咖啡店、氛围、店员、热情、口感 摘要:顾客对咖啡店环境和服务给出好评,表示还会再来。

到这里,在线模式已经能完整跑通了。

4. 离线AI模式:没网也能完成的保底方案

4.1 snownlp怎么判断情感

在线模式需要网络和密钥,如果这两个条件都不满足,你的工具就废了。所以我写了离线模式,用轻量级本地库实现同样的三大功能。

离线情感分析用snownlp:

from snownlp import SnowNLP def offline_sentiment(text): s = SnowNLP(text) score = s.sentiments rounded = round(score, 2) if score > 0.6: return "positive", rounded if score < 0.4: return "negative", rounded return "neutral", rounded

snownlp的sentiments属性返回一个0到1之间的值,越接近1越正面,越接近0越负面。我设置0.6和0.4两个阈值,大于0.6判正面,小于0.4判负面,中间算中性。这个阈值你可以根据自己的语料调整,并没有标准答案。

snownlp对短文本很敏感,一句话如果太短,比如“还行”两个字,它基本判断不了。所以离线模式下,建议输入有个二三十字以上,效果会稍微稳定一些。

4.2 提取关键词:停用词过滤加简化版TextRank

离线模式没有大模型帮你“理解”文本,只能靠统计规律。最朴素的思路是词频:出现次数多的词重要。但高频词往往是“这”“的”“是”这类没有实际含义的词,所以要先过滤停用词,再统计。

更进一步,我用了一个简化版TextRank思路。它比纯词频好在哪里?纯词频只关心“这个词出现了几次”,而TextRank会考虑“这个词和哪些词一起出现”。比如一段食品评价里,“口感”和“醇厚”经常一起出现,那么它们就会互相加分,最终筛选出来的关键词更能代表文本主题。

import jieba import re from collections import defaultdict STOP_WORDS = {"的", "了", "是", "在", "我", "有", "就", "不", "也", "都", "很", "这", "那", "你", "它", "啊", "呀", "呢", "吧"} def token_filter(text): words = jieba.lcut(text) result = [] for word in words: word = word.strip() if len(word) < 2: continue if not re.fullmatch(r"[\u4e00-\u9fa5A-Za-z0-9]+", word): continue if word in STOP_WORDS: continue result.append(word) return result def extract_keywords(text, top_k=5): words = token_filter(text) if not words: return [] graph = defaultdict(lambda: defaultdict(int)) for i in range(len(words) - 1): w1, w2 = words[i], words[i + 1] graph[w1][w2] += 1 graph[w2][w1] += 1 scores = {word: 1.0 for word in graph} d = 0.85 for _ in range(10): new_scores = {} for word in scores: rank_sum = 0 neighbors = graph[word] total = sum(neighbors.values()) for next_word, weight in neighbors.items(): rank_sum += (weight / total) * scores[next_word] new_scores[word] = (1 - d) + d * rank_sum scores = new_scores ranked = sorted(scores.items(), key=lambda item: item[1], reverse=True) return [word for word, _ in ranked[:top_k]]

代码里构建了一个词与相邻词的共现图,然后迭代更新每个词的权重,权重收敛后取Top N。这一段对新手来说可能偏算法,但你把它当成一个黑盒其实问题不大:给一段文本进去,返回几个关键词出来。真正想深入理解,能看懂最好,看不懂先把代码存着,后面慢慢消化也一样能用。

4.3 简易抽取式摘要:给句子打分

摘要生成有两种思路。一种是生成式,由模型重新写一句,在线大模型就是这样做的。另一种是抽取式,从原文里挑最重要的句子,离线模式用这个思路。

实现逻辑很直观:把文本按句号、感叹号等切成句子,然后给每个句子打分。关键词出现在哪个句子里,那个句子就加分;首句天然有引导作用,也加一点位置权重。最后取分数最高的句子作为摘要:

TOP_SUMMARY_SENTENCE_MAX_LEN = 50 def make_summary(text): sentences = re.split(r"[。!?!?]", text) sentences = [s.strip() for s in sentences if len(s.strip()) >= 5] if not sentences: return text[:30] keywords = extract_keywords(text, top_k=5) scored = [] for idx, sentence in enumerate(sentences): score = 0 for keyword in keywords: if keyword in sentence: score += 1 if idx == 0: score += 0.5 scored.append((score, idx, sentence)) scored.sort(key=lambda item: (-item[0], item[1])) return scored[0][2][:TOP_SUMMARY_SENTENCE_MAX_LEN]

这段代码很粗糙,但足够体现抽取式摘要的核心思想。想让摘要质量更高,可以继续优化打分规则,比如把句子长度、关键词密度加进权重,甚至把句子的位置信息做成可配置的权重。我建议新手先把粗糙版跑通,再一点点调,不要一上来就追求完美。

4.4 在线和离线双模式统一封装

三个离线函数都有了,接下来把在线和离线统一成一个入口。在config.py里做一个简单的模式选择:

import os from dotenv import load_dotenv load_dotenv() AI_MODE = os.getenv("AI_MODE", "online")

然后在本地分析文件里,把离线功能组装成和在线模式相同的返回结构:

def analyze_local(text): sentiment, confidence = offline_sentiment(text) keywords = extract_keywords(text, top_k=5) summary = make_summary(text) return { "sentiment": sentiment, "confidence": confidence, "keywords": keywords, "summary": summary, }

为什么要求返回结构一致?因为main.py只关心结果格式,不关心它来自在线还是离线。在线返回什么样,离线也返回什么样,之前写好的打印代码就完全不用改。这种“接口统一”的思想,在工程里非常重要,它让不同的底层实现可以互相替换,模块之间不会互相牵连。

5. 从能跑到好用:完整跑通记录与调优

5.1 先让程序骨架能跑,再验证AI能力

我第一次做这类工具的时候,没有一上来就接大模型接口,而是先写了一个“假”的分析函数,固定返回一组数据:

def fake_analyze(text): return { "sentiment": "positive", "confidence": 0.95, "keywords": ["测试", "关键词"], "summary": "这是测试摘要", }

用这个函数把输入、打印、格式化整条链路全部跑通,确认命令行交互没毛病之后,再把它替换成真实AI调用。这个习惯叫“先搭骨架,再填肉”。好处是每一层都能单独验证,出问题的时候能立刻判断是哪一层的问题,而不是等所有代码拼起来之后,对着一大坨代码干瞪眼。

很多新人上来就想着一步到位,结果网络请求、JSON解析、中文编码、命令行参数全部缠在一起,错了半天找不到根源。先跑通骨架,后面的每一步都会轻松很多。

5.2 接上在线大模型,验证Prompt和解析

把fake_analyze换成analyze_with_llm,再跑同一段输入。我第一次跑的时候,模型除了JSON还输出了一堆解释文字,parse_json直接抛了异常。

我做了两处调整。第一处是系统提示词里加了一句“不要输出任何额外文字,不要使用代码块标记”。第二处是写了容错解析函数,先把代码块围栏剥掉再解析。两处下来,JSON解析失败的概率大大降低。

这个过程中我最大的体会是:Prompt不是写一次就完事,它需要针对实际输出做迭代。每一版Prompt里遗漏的边界条件,都会在返回结果里变成一次报错。所以调试Prompt时,一定要把模型原始返回打出来看,别只盯着报错信息猜。看到原始输出,你往往一眼就能发现问题是出在格式、字段名还是语义理解上。

5.3 加上异常降级:在线挂了也能用

模型接口再稳定,也有网络波动、额度用尽的时候。我后来给程序包了一层保护逻辑:

def safe_analyze(text): try: return analyze_with_llm(text) except Exception as exc: print(f"在线分析失败,降级到本地模式:{exc}") return analyze_local(text)

这个函数的设计思路很朴素:主用在线,如果任何异常发生,就自动退回到离线模式,保证用户仍然能拿到一个结果。对一个工具来说,“大多数时候好用”和“偶尔完全不可用”之间的差距很大,异常降级就是填补这个差距最简单有效的方式。

5.4 让工具支持文件输入和命令行参数

命令行交互适合单次测试,真正要分析一批评论时,一条条复制粘贴实在太痛苦。我给工具加了一个能力:支持把文件路径当参数传进去,程序读取文件后整体分析。

import sys def read_text_from_args(): if len(sys.argv) > 1: path = sys.argv[1] with open(path, encoding="utf-8") as f: return f.read() return None text = read_text_from_args() if text is None: text = input("请输入要分析的文本:")

注意open(path, encoding="utf-8")这里的编码参数,在Windows上读取中文文件,如果不指定UTF-8,很容易出现乱码。这个小细节能省掉你很多烦恼。

5.5 把分析结果保存成文件

分析完文本,顺手把结果存下来,这样你能积累数据,后面可以做统计分析。保存格式我推荐JSON Lines,每行一个JSON对象:

import json def save_result(text, result): with open("results.jsonl", "a", encoding="utf-8") as f: f.write(json.dumps({"text": text, "result": result}, ensure_ascii=False) + "\n")

ensure_ascii=False的作用是让中文正常显示,不加这个参数的话,保存的中文会被转成\uXXXX形式的转义字符,读起来十分头疼。有了这个保存功能,每次分析的结果自动累积到一个文件里,后面拿去做统计报表或者训练自己的分类器,都有现成数据可用。

6. 新手高频报错与排查实录

6.1 网络请求报错,先从三个地方查起

最常见的报错是requests.exceptions.ConnectionError或者超时。遇到这种错误,先不要怀疑代码逻辑,按顺序查三件事:

第一,本机网络是否正常,目标接口是否可达。第二,API Key有没有填错,BASE_URL和MODEL_NAME是否符合服务商文档要求。第三,接口返回的状态码,401通常表示密钥无效,429表示请求太频繁或额度超限。

新手经常把网络问题当成代码问题,对着函数反复看,却漏掉了最基本的排查项。建议养成一个习惯:先把接口返回的状态码和响应体用print打印出来,很多时候问题看一眼就明白了。

6.2 中文乱码问题,编码统一是治本方案

Windows的cmd或PowerShell里运行程序,输出中文乱码很常见,根因是终端默认编码和Python的UTF-8不一致。两个快速解决办法:一是运行前执行chcp 65001切换终端到UTF-8代码页;二是用python -X utf8 main.py启动Python,强制使用UTF-8处理输入输出。

写文件乱码则一般是open没指定编码,统一在打开文件时加上encoding="utf-8",几乎不会再出现乱码。小工具开发中,编码问题最消磨耐心,早处理早轻松。

6.3 JSON解析失败,用一条提示词加一个容错函数

json.JSONDecodeError也是高频报错。原因往往离不开两点:模型输出带了代码块标记,或是在JSON前后加了额外文字。解决办法在前面已经写了,再加一条:如果要彻底治本,可以在系统提示词里给模型一个具体输出示例,叫作few-shot。模型看过你期望的格式,比只读文字描述更容易遵守。

如果用了这些手段还是偶尔报错,那就接受一个现实:在线模型的输出天生带有随机性,完全杜绝异常不现实。解法就是把异常处理写好,让程序在解析失败时能自动降级到本地模式,而不是直接崩溃。

6.4 情感分析结果不准,先看输入长度和阈值

离线模式下,情感判断不准通常有两个原因。一是输入太短,snownlp对十几个字的短文本几乎无能为力,这种情况可以提示用户加长输入。二是阈值设置不合适,你可以把离线模式的0.4和0.6阈值调一调,观察不同阈值在自己语料上的表现。

在线模式判断不准,通常不是模型能力问题,而是Prompt里没有把判断标准说清楚。比如“客服回复很快”这句话,既有对效率的褒义,也有偏中性的客观描述,如果不给模型明确的判断规则,它的输出就会摇摆。在系统提示词里补充一句“当文本同时包含褒义和中性内容时,以整体态度倾向为准”,结果会稳定很多。

6.5 快速排查速查表

症状可能原因解决办法
连接报错网络不通、接口地址错误检查本机网络、确认BASE_URL
401/403API Key错误或权限不足检查.env配置、确认账号状态
429请求频率过高或额度超限降低调用频率或切本地模式
中文乱码终端编码或文件编码不统一chcp 65001、open(...encoding="utf-8")
JSONDecodeError模型输出格式不合法容错解析、Prompt加few-shot示例
离线情感总是中性输入过短、阈值不当调阈值、增加输入文本长度
提取出常见无意义词停用词表不够完整扩充停用词表

排查顺序建议:先看错误信息,再检查调用参数,最后怀疑算法逻辑。顺序反了就会在一个不可能有bug的地方反复踱步。

我做完这个小工具最大的感受是:AI开发并没有想象中那么神秘,它就是把你脑海里的一个需求,翻译成“输入—处理—输出”的代码流程,再用接口或模型完成“处理”这一环。新手跑通之后,可以继续把玩下去:把命令行改成带输入框的桌面应用或网页小工具,把单条分析改成批量处理文件,把结果存进表格里生成统计报表。每次只加一个功能,你对“从需求到工具”的掌控感就会强一分。

最后分享一个个人习惯:做这种练习时,我会把每一次报错和解决过程记在一个文档里,攒一段时间再看,那些你反复踩的坑,其实就是你进步最快的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询