用Python拆解Claude语言指纹:核心词汇分析与TF-IDF实践

📅 发布时间:2026/8/31 2:52:00
用Python拆解Claude语言指纹:核心词汇分析与TF-IDF实践
用 Claude 的次数多了你会慢慢发现一个现象它确实有一套非常固定的说话习惯。英文回复里总是出现 certainly、great question、Id be happy to中文回复里则是“当然可以”“这是一个很好的问题”“让我们一步一步来”。以前我们多半把这些话当作模型的礼貌表达很少深究。但如果你真的把所有回复拿去统计会发现这些词的复现频率稳定得惊人。一个相对可靠的判断是Claude 回复里的高频词并不是偶然而是训练偏好、系统提示和产品调教共同作用留下的痕迹。这些高频词就像一个人的口头禅组成了一组可以量化的“语言指纹”。Show HN 上出现过类似创意的项目名字就叫“克劳德的核心词汇”把 Claude 的回复文本做分词、做词频统计再和普通语料对比最终提炼出一份能够代表 Claude 风格的高频词汇表。这篇文章不聊怎么注册账号也不讲 Claude Code 的安装步骤而是从技术实现角度拆解这一类分析项目。你会看到如何用 Python 采集 Claude 的回复样本如何对中英文混合文本做分词和词频统计如何借助 TF-IDF 和对数似然比把“Claude 味”变成可量化的指标最终得到一份属于你自己的核心词汇表。读完以后你可以自己跑一遍流程也可以把词表用来优化提示词、识别模型回复、甚至给 Claude Code 定制团队风格。1. 为什么说核心词汇本质上是一套语言指纹要理解这个项目先要放下一个常见的误解AI 模型的“个性”不是一个抽象概念而是一种可观测的统计分布。同一个问题丢给不同模型它们的长度、结构、用词都会呈现出明显的差异。有的模型喜欢先总结再展开有的模型喜欢直接给结果还有的模型几乎每一轮都会先客气一遍再进入正题。语言指纹这个概念来自文本分析。每个人的用词习惯不同同样表达“好的”有人习惯说“行”有人习惯说“OK”有人习惯说“收到”。这些细微差异单独看没有意义累积到一定量级后就能形成一个稳定的识别特征。大模型同样如此。模型在训练数据里吸收了大量人类对话风格又在强化学习和产品对齐阶段被刻意塑造成某种“助手人格”。这种人格最终会固化到词汇选择上。Claude 的词汇特征之所以值得单独分析是因为它的风格相对鲜明。用户在日常对话中能明显感觉到它的礼貌感、结构化倾向和“小作文式”总结。这些特征的底层表现是什么是一堆高频率出现的连接词、情态动词、客套短语和过渡句式。它们不是模型偶尔用一次而是在绝大多数回复里反复出现。从这个角度看克劳德核心词汇项目真正做的事情不是简单的词云展示而是建立一套“模型行为跟踪系统”。你不需要读上千条回复去感受风格差异只需要跑一次统计脚本看哪些词显著超出普通语料就能快速理解这个模型在语言层面到底被训练成了什么样。这个思路也符合工程上的一个基本原则凡是反复出现的模式都应该被量化而不是停留在感觉层面。感觉会骗人词频不会。2. 核心词汇项目要完成哪些事情从功能角度拆解“克劳德的核心词汇”这个小项目并不复杂。它不需要训练模型也不需要搭建服务本质上是数据处理管线加统计对比。项目要完成的事情可以分成六步采集、清洗、分词、统计、对比、输出。第一步是采集。你需要拿到足够的 Claude 回复文本。这些文本可以来自自己调用 API 的返回结果也可以来自公开的对话数据集。关键是样本量要足够并且要覆盖不同类型的任务否则统计出来的词表会偏向某一种问答类型。第二步是清洗。原始回复里常常包含 Markdown 语法、代码块、列表符号和多余换行。如果直接拿来做词频统计像**、-、第1步这样的 token 会严重干扰结果。清洗的目标是保留自然语言部分去掉结构化噪音。第三步是分词。中文和英文的处理方式不同。英文可以用正则表达式按空格和标点切分中文必须借助分词工具。现实中 Claude 的中文回复往往夹杂英文技术词汇所以一个健壮的实现需要同时处理两种语言。第四步是统计。统计每个词出现的次数、频率、在文档集合中的逆文档频率把高频词按权重排序。这是整个项目最直白的输出。第五步是对比。单独看高频词没有太大意义因为“的”“了”“可以”在任何中文文本里都很常见。要让词汇表有信息量必须找一个基线语料做对照。常见做法是准备同等规模的其他模型回复、通用对话语料或公共语料库然后计算每个词在 Claude 样本中的相对增幅。第六步是输出。最终结果可以是一张词表也可以是一份 Markdown 报告。词表里不仅应该有词本身还应该包括频率、对比倍率、在句中的常见位置等信息方便后续使用。从项目结构看这是一条标准 NLP 分析管线。任何人只要掌握 Python 基础就能在本地复现。也正因为它足够轻量才适合作为研究模型行为的第一块敲门砖。3. 环境准备与前置条件动手之前先把环境准备好。本文演示以 Python 3.10 以上版本为基础操作系统不限Windows、macOS、Linux 都可以。如果你只是跑统计脚本不调用 API那一台普通开发机完全够用。需要安装的主要依赖如下# 文件路径requirements.txt anthropic0.40.0 jieba0.42.1 scikit-learn1.4.0 pandas2.2.0其中anthropic是官方 SDK只在采集样本时需要jieba负责中文分词scikit-learn用于 TF-IDF 对比pandas用来整理结果。安装命令pip install -r requirements.txt如果你的项目需要更干净的环境建议使用虚拟环境python -m venv .venv source .venv/bin/activate # Windows 下执行 .venv\Scripts\activate数据准备方面我建议把数据统一放在data/目录下里面至少包含两个 jsonl 文件一个存放 Claude 回复一个存放基线回复。格式如下{source: claude, task: 写一段 Python 快速排序, text: 当然我们可以从快速排序的基本思路开始……} {source: baseline, task: 写一段 Python 快速排序, text: 快速排序采用分治法选择一个基准元素……}这种格式的好处是很容易用 pandas 读入也能在后续分析里按任务类型分组。如果你打算调用 Anthropic API 采集数据需要提前配置环境变量export ANTHROPIC_API_KEY你的 API Key关于安全边界要提醒一句API Key 属于敏感凭据不要写进任何代码或提交到 Git 仓库。本文后续代码统一通过环境变量读取这也是生产环境的基本要求。如果只是学习统计方法你完全可以跳过 API 调用环节直接准备一批文本文件就能跑通。4. 核心流程拆解从回复文本到核心词汇现在进入主流程。我用一个最小的示例把整个分析链路串起来方便理解每一步的意义。4.1 采集 Claude 回复样本采集这一步有两种做法。第一种是调用 Anthropic API 实时生成回复第二种是直接读取已经存在的对话样本文件。第一种更适合自动化收集第二种更适合复现分析。这里给出第一种的完整示例同时把结果写入 jsonl 文件后续统计直接复用。# 文件路径collect_samples.py import os import json import anthropic client anthropic.Anthropic( api_keyos.getenv(ANTHROPIC_API_KEY) ) MODEL os.getenv(ANTHROPIC_MODEL, ) if not MODEL: raise ValueError(请通过环境变量 ANTHROPIC_MODEL 指定要分析的模型名称) QUESTIONS [ 请用中文解释什么是快速排序, 帮我写一个 Python 函数判断一个字符串是否是回文, 为什么微服务架构比单体架构更复杂, 请给出三个提高代码可读性的建议, ] output_path data/claude_samples.jsonl os.makedirs(data, exist_okTrue) with open(output_path, w, encodingutf-8) as f: for q in QUESTIONS: resp client.messages.create( modelMODEL, max_tokens1024, messages[{role: user, content: q}], ) text resp.content[0].text record {source: claude, task: q, text: text} f.write(json.dumps(record, ensure_asciiFalse) \n) print(已采集:, q)这段代码的关键在于将每次回复写入一行 JSON保持结构一致。ensure_asciiFalse是为了让中文以原始字符存储方便后期阅读和检查。实际项目中建议扩大问题集合覆盖代码解释、文案写作、逻辑推理等不同类型的任务减少单一任务带来的词频偏差。4.2 文本清洗与分词清洗这一步容易被忽略但它的影响比想象中大。Claude 的回复经常包含大量 Markdown 语法比如加粗、代码块、列表符号和标题。如果不做清洗高频词里会出现一坨**和###毫无分析价值。下面这段代码同时处理中英文中文用jieba.lcut分词英文用正则表达式提取单词最后统一过滤掉纯符号 token 和常见停用词。# 文件路径analyze_frequency.py import re import json import jieba from collections import Counter STOP_WORDS set(的 了 是 在 我 有 和 就 不 人 都 一 一个 上 也 很 到 说 要 去 你 会 着 没有 看 好 可以 吗 呢 吧 这 那 什么 我们 你们 它们 自己 因为 所以 但是 如果 然后.split()) def clean_text(text: str) - str: # 去掉代码块 text re.sub(r.*?, , text, flagsre.S) # 去掉行内代码 text re.sub(r[^]*, , text) # 去掉加粗和链接 text re.sub(r(\*\*|__)(.*?)(\*\*|__), r\2, text) text re.sub(r\[(.*?)\]\((.*?)\), r\1, text) # 去掉列表符号和多余空白 text re.sub(r^\s*[-*]\s, , text, flagsre.M) return text.strip() def tokenize(text: str): tokens [] # 提取英文单词和数字 for en in re.findall(r[a-zA-Z][a-zA-Z-]*, text): tokens.append(en.lower()) # 中文分词 for zh in jieba.lcut(re.sub(r[a-zA-Z0-9], , text)): zh zh.strip() if len(zh) 1 and zh not in STOP_WORDS: tokens.append(zh) return tokens def load_samples(path: str): with open(path, r, encodingutf-8) as f: for line in f: yield json.loads(line) if __name__ __main__: counter Counter() total_docs 0 for sample in load_samples(data/claude_samples.jsonl): counter.update(tokenize(clean_text(sample[text]))) total_docs 1 print(样本数量:, total_docs) print(去重单词数量:, len(counter)) for word, freq in counter.most_common(50): print(word, freq)这里的停用词表只是一个起点。真实的“核心词汇”恰恰可能藏在“可以”“我们需要”这些看似普通的中文结构里所以停用词表不宜过大。宁可先保留再在对比阶段用统计方法过滤通用词。仅用固定的停用词表会把真正的风格词一起删掉这是新手最容易踩的坑。4.3 和基线语料做对比词频只能告诉你 Claude 爱用什么词不能告诉你哪些词是 Claude 特有的。要回答“这个模型为什么有味道”必须引入基线语料。一个简单有效的做法是准备另一批基线回复来源可以是通用对话数据集也可以是另一个模型对相同问题的生成结果。然后分别统计两组语料的词频计算每个词的相对倍率。这个倍率越大的词越能体现 Claude 的特征。这里提供一份基于 TF-IDF 的实现。将 Claude 回复和基线回复看成两个文档集合使用 TfidfVectorizer 抽取特征再比较同一个词在两个集合中的 TF-IDF 均值差。# 文件路径compare_vs_baseline.py import json import re import jieba import pandas as pd from collections import Counter from sklearn.feature_extraction.text import TfidfVectorizer def load_samples(path: str): with open(path, r, encodingutf-8) as f: for line in f: yield json.loads(line) def normalize_text(text: str) - str: text re.sub(r.*?, , text, flagsre.S) text re.sub(r[^]*, , text) # 中文词之间加空格方便 TfidfVectorizer 按空格切词 text .join(jieba.lcut(re.sub(r[a-zA-Z0-9], , text))) return text if __name__ __main__: texts [] labels [] task_keys [] for sample in load_samples(data/claude_samples.jsonl): texts.append(normalize_text(sample[text])) labels.append(claude) task_keys.append(sample.get(task, )) for sample in load_samples(data/baseline_samples.jsonl): texts.append(normalize_text(sample[text])) labels.append(baseline) task_keys.append(sample.get(task, )) df pd.DataFrame({label: labels, task: task_keys, text: texts}) vec TfidfVectorizer(ngram_range(1, 2), max_features2000) matrix vec.fit_transform(df[text]).toarray() feature_names vec.get_feature_names_out() claude_mask (df[label] claude).to_numpy() baseline_mask (df[label] baseline).to_numpy() claude_mean matrix[claude_mask].mean(axis0) baseline_mean matrix[baseline_mask].mean(axis0) diff pd.DataFrame({ word: feature_names, claude_tfidf: claude_mean, baseline_tfidf: baseline_mean, diff: claude_mean - baseline_mean, }).sort_values(diff, ascendingFalse) print(diff.head(30).to_string(indexFalse))从工程角度看TF-IDF 在这里的价值是降低“的”“了”“在”这类无用词的权重让真正具有区分能力的词汇浮出来。项目的最终产物就可以是这份按照 diff 降序排列的词表排序靠前的词就是“克劳德核心词汇”的候选词。5. 完整项目结构与运行方式为了让整个项目更清晰我把文件组织成下面的结构claude-core-vocab/ ├── data/ │ ├── claude_samples.jsonl │ └── baseline_samples.jsonl ├── collect_samples.py ├── analyze_frequency.py ├── compare_vs_baseline.py └── requirements.txt如果你想跳过 API 采集可以直接准备两个 jsonl 文件放进data/目录然后只运行后两个脚本。如果你需要通过 API 收集 Claude 回复再运行采集脚本。整体执行顺序是python collect_samples.py python analyze_frequency.py python compare_vs_baseline.py第一步可选第二步验证基础词频第三步输出核心词表。整个过程没有任何复杂的并行计算也不依赖 GPU非常适合当作入门 NLP 分析的小项目。这里特别解释一下为什么把对比放到最后。因为核心词汇的定义是相对的只有和通用表达拉开差距的词才值得进入词表。“快速排序”这类和任务强相关的词会同时出现在 Claude 和基线语料里在对比排序中会自然靠后这正好过滤掉了任务内容带来的噪音。6. 运行结果与效果验证统计脚本跑完以后你会得到类似下面这样结构的结果。需要说明的是以下数字是演示样例不是某个特定版本 Claude 的官方统计。不同模型版本、不同提示词、不同样本量结果会有明显差异。wordclaude_tfidfbaseline_tfidfdiff我们可以0.04520.01120.0340一个很好的0.03810.00650.0316希望0.03450.00820.0263当然0.03190.00740.0245让我们0.02980.00610.0237简单地说0.02870.00530.0234具体来说0.02730.00680.0205怎么判断这份词表是否有效我的建议是拿词表反向验证。从排序靠前的词里挑出 5 到 10 个回到原始样本里人工看一遍确认这些词是否真的被反复使用、是否出现在多数回复中。如果词表里都是一些任务关键词比如“排序”“函数”“代码”说明样本任务太单一需要扩大问题集合。如果词表和人工感受高度一致说明本次采集和统计基本可信。常见的第一步失败点也在这里。如果你的compare_vs_baseline.py输出为空或者大量词是单字先去检查分词结果再检查停用词表。中文分词的颗粒度会直接影响词表质量jieba默认模式会把“我们可以”切成“我们”和“可以”如果核心词表里都是单字词考虑改用jieba.analyse或者引入自定义词典。7. 常见问题与排查思路问题现象可能原因排查方式解决方案词表里全是任务关键词没有风格词样本任务类型单一不同任务内容重复按 task 字段分组查看词频差异扩充问题集合覆盖代码、写作、推理、翻译等类型中文分词结果混乱词表多为单字jieba 默认词典颗粒度低打印分词后的样本加入自定义词表或调整 jieba 模式“的”“了”“在”排在最前面没有做基线对比或基线语料太少检查 baseline 样本数量增加基线样本使用 TF-IDF 或频率比过滤API 采集时返回文本为空模型输出被截断或触发安全过滤查看返回对象的 stop_reason增加 max_tokens调整提示词词表在不同批次之间差异很大样本量不足随机性太强对比两次采样的词表重合度增加样本量建议至少 300 到 500 条回复英文单词全部变成小写导致信息丢失正则提取时做了 lower()查看 tokenizer 输出根据分析目标决定是否保留大小写这些坑基本都是文本分析入门时一定会遇到的。词表项目的核心难点从来不是代码本身而是让数据说话的时候能分辨哪些信号是真实的哪些只是数据处理方式带来的假象。8. 核心词汇表能用来做什么拿到一份“克劳德核心词汇表”之后它能落地的方向比想象中多。第一个方向是提示词工程。很多人在设计提示词时只关注任务描述忽略了模型的表达惯性。如果你知道 Claude 倾向于使用“我们可以”“让我们”“当然”这类短语那么在系统提示里就可以主动说明“不要使用客套语不要总结直接给出答案”压制高风格词的出现。反过来如果你希望低成本模仿 Claude 的风格把这份词表当作关键词插入提示词也能起到类似效果。这比一句“请模仿 Claude 的风格”要具体得多因为模型对抽象风格词的理解远不如对具体词汇的响应稳定。第二个方向是 Claude Code 和自动化编码场景。在编程任务里Claude 也会经常输出“当然可以”“这是一个很好的问题”“希望这些建议对你有帮助”等干扰性语句。如果你在项目指导文件例如 Claude Code 中的 CLAUDE.md里明确写上“回复中不要出现以下表达”并把统计得到的高频客套语列进去能明显减少无用输出让代码审查体验更干净。这也算是把语言指纹分析直接转化为工程生产力的方式。第三个方向是模型回复识别与越权检测。虽然不建议把词表当成模型身份的绝对依据但在自动化测试中它可以作为辅助特征。比如你在做模型网关的日志分析发现某一段回复中 Claude 风格词权重极高就可以把它标记为“疑似该模型生成”再结合其他特征做进一步判断。本质上是把文本分类模型的特征工程简化成了一个词表查表。第四个方向是团队写作风格对齐。如果在实际项目里把所有成员的 AI 回复都做一次词频统计就会发现不同模型产出的文案有完全不同的语言习惯。把核心词表整理成团队内部规范比如“对外文档尽量少用‘我们可以’‘让我们’这类词”能有效统一 AI 生成内容的语言风格。9. 最佳实践与工程建议如果你想把这个项目做得更扎实有几条工程建议值得记下来。第一样本多样性永远是第一位的。不要只采样编程问题也不要只采样开放闲聊。最好按任务类型分层采样例如代码任务 30%、文案任务 30%、逻辑推理任务 20%、生活对话 20%。这样统计出的词表才不会偏向某一类场景。第二注意提示词对回复词汇的污染。同一个问题加一句“请详细解释”和加一句“请简短回答”会让词频发生巨大变化。如果要分析模型本身的语言习惯应该统一提示词模板或者在同一模板下采集多轮结果。否则你统计出来的不是模型的风格而是提示词的风格。第三中文和英文要分开分析。Claude 的中文回复和英文回复并不是翻译关系它们的词汇特征很可能不同。对中文样本做分词统计对英文样本做 tokenizer 统计最后保留两种语言各自的词表而不是混在一起。第四模型版本变化会导致词汇漂移。今天统计出的高频词在下一个版本可能完全消失。如果你拿它去做长期监控建议每周或每月定期重新采样一次观察词表变化趋势。这比一次性结论有价值得多。第五不要把核心词汇表用作断言模型身份的证据。词汇只是风格特征可以被提示词覆盖也可以被其他模型刻意模仿。在安全或合规场景里它只能作为辅助线索不能作为唯一判定依据这一点必须明确。10. 总结“克劳德的核心词汇”这个项目看起来是一个趣味数据分析实际上它的价值在于提供了一种非常轻量的模型行为研究方法。不需要训练模型不需要 GPU只需要几百条文本、一个分词器、一份对比语料就能把一个模型的说话习惯拆解成可量化、可复用、可监控的词表。对普通开发者来说这套方法最大的意义不在词表本身而在于“把感觉变成指标”的思考方式。下次你调用任何模型时如果觉得它“回答风格怪怪的”不要停留在印象层面直接抓几百条回复跑一次词频分析。你会发现所有的风格问题最终都会落到具体的词汇选择上。如果你正在使用 Claude Code可以先尝试把高频客套语整理进项目规范观察代码回复的简洁度变化如果你在做提示词工程也可以把词表当作反向控制指令的素材库。技术本身不复杂真正有价值的是持续观察和对比的习惯。建议把这套流程收藏起来等你积累了自己的数据重新跑一遍会有新的发现。