前两天在 Hacker News 上看到一个挺有意思的帖子:作者让 LLM 在多个热门开发者工具之间做选择,然后对比不同模型的偏好。这个思路看起来简单,但仔细想想很有意思——LLM 没有“亲手用过”这些工具,它的判断完全来自训练语料中的隐式评价、项目经验共享、文档措辞和社区讨论。换句话说,它给出的是“人类在互联网上对工具的集体印象”,而不是真实的性能测试结果。
所以我不只是想复现这个实验,更想把方法论沉淀下来:怎么设计提示词才能让 LLM 的选择更有参考价值?怎么避免模型被流行度带偏?怎么让评测结果可复现、可解释?这篇文章会从概念讲起,给出一套可运行的评测脚本,最后结合真实对比案例,聊聊 LLM 选型结果的正确打开方式。
本文适合对 LLM 应用开发、开发者工具选型感兴趣的读者。你不需要有很深的大模型基础,跟着步骤走就能跑通实验。通过全文,你可以掌握一种“用 LLM 做工具对比”的系统化方法,也能避开我在实际评测中踩过的几个坑。
1. 背景与核心概念
1.1 我们说的“让 LLM 选择工具”到底是什么
表面上看,这个实验很简单:把问题抛给 ChatGPT、Claude、Gemini 这类大语言模型,问它“Vim 和 VSCode 哪个更适合新手”,然后收集回答、汇总结果。
但严格来说,这不是“让 LLM 选择”,而是“观察 LLM 基于训练语料做加权归纳”。LLM 本身没有任何真实使用体验,它的回答建立在对海量文本的概率建模之上。当互联网上大量内容说“VSCode 对新手更友好、插件生态丰富”时,模型生成的回答自然会向这个方向倾斜。
这个区别非常重要。我们不是在做一个权威选型评测,而是在做一次“模型认知投影”。它反映的是:
- 工具在开源社区、技术博客、论坛中的讨论热度;
- 工具文档质量和相关教程数量的差异;
- 开发者群体在公共平台上表达的偏好。
1.2 实验的价值在哪里
既然 LLM 没有真实体验,那这个实验还有意义吗?我认为有,而且价值集中在三个方向。
第一,快速生成“候选清单 + 决策维度”。让 LLM 穷举某类工具的对比维度,往往比人肉搜索更高效。比如对比前端框架时,它可以快速列出学习曲线、包体积、SSR 支持、生态成熟度等维度。
第二,验证训练语料中的工具口碑。你在网上看到某个工具“很好用”,但信息零散。LLM 像是帮你做了一次文本检索与归纳,把分散的正面、负面评价压缩成一段话。
第三,暴露模型的偏好偏差。不同模型对同一组工具的选择可能完全不同。这种偏差本身就是值得分析的对象——比如某个模型是否过于偏好老牌工具,是否对新兴工具了解不足。
1.3 容易混淆的概念
做这个实验前,先区分几组概念,避免后面理解偏了。
- “LLM 选择”不等于“推荐系统”。推荐系统通常基于用户行为数据,而 LLM 基于文本统计。
- “模型偏好”不等于“工具优劣”。模型说 A 比 B 好,只能说明训练语料中有更多支持 A 的内容,不能代表 A 真的更适合所有项目。
- “评测结果”不等于“工程决策”。最终选型必须结合团队技术栈、维护成本、运行性能等实际数据。
2. 实验设计:从“随口问”到“可复现评测”
很多人做类似的实验,就是打开一个聊天窗口,输入“Python 和 JavaScript 哪个好?”。这种问法得到的答案随机性很大,而且没有统一衡量标准,结果不可复现,也很难横向对比不同模型。
要做一个像样的实验,需要先设计评测框架。整个流程可以分为四步。
2.1 确定工具对比分组
不要一次性让 LLM 对比十种工具,这样回答会很含糊。建议做两两对比,或者限定三个选项以内。
另外,对比工具时要考虑可比性。比如把 Vite 和 Webpack 放在一组,因为它们解决的问题相同,只是设计思路不同。把 VSCode 和 Docker 放在一组就不太合适,它们不是同类工具。
以下分组是我实验时用过的,供参考:
| 场景 | 对比工具 | 核心问题 |
|---|---|---|
| 代码编辑器 | VSCode vs Vim vs JetBrains IDEA | 新手友好度与扩展性取舍 |
| 包管理器 | npm vs pnpm vs Yarn | 依赖安装速度与磁盘占用 |
| Python 环境管理 | venv vs Conda vs Poetry | 环境隔离与依赖锁定 |
| 前端构建工具 | Vite vs Webpack | 开发体验与生产构建性能 |
| 关系型数据库 | PostgreSQL vs MySQL | 功能丰富度与运维成本 |
| 接口调试工具 | Postman vs Apifox vs Insomnia | 团队协作与本地效率 |
| 监控告警 | Prometheus vs Grafana vs Zabbix | 云原生支持与部署复杂度 |
2.2 设计统一的评判维度
要让不同模型、不同轮次的回答可比,必须给模型提供一套统一的评判维度。否则模型可能这次强调学习曲线,下次只谈插件生态。
建议的维度模板:
- 功能覆盖程度
- 易用性与上手门槛
- 生态与社区活跃度
- 性能与资源占用
- 团队协作与可维护性
- 长期演进风险
对于不同模型,保持完全相同的提示词。这是控制变量的核心。
2.3 固定提示词模板
提示词是实验里最关键的因素。同样的问题,换几个词,结果可能完全不一样。
我整理的基础模板如下:
你是一名资深开发者,正在帮助一位中等经验的开发团队做技术选型。 请从以下几个维度客观对比工具 A 和工具 B: 1. 功能覆盖程度 2. 易用性与上手门槛 3. 生态与社区活跃度 4. 性能与资源占用 5. 团队协作与可维护性 6. 长期演进风险 要求: - 每个维度用 3 条以内的要点说明; - 不要给出模糊的"都很好"式结论; - 最后必须选择一个工具,并给出选择理由; - 如果存在不同使用场景下的不同结论,请明确场景。注意几个设计细节:
- “中等经验的开发团队”既不是新手也不是专家,防止模型默认假设目标用户为零基础或资深极客。
- 要求“必须选择一个”,强制模型做出决策,避免各打五十大板的回答。
- 要求“明确场景”,允许模型在特定条件下给出不同结论。
2.4 控制对话温度与随机性
如果你通过 API 调用模型,需要设置参数。temperature 控制输出的随机性,数值越高回答差异越大。做评测时建议设置成 0 或接近 0,保证每次输出尽可能稳定。
需要留意的是,即便 temperature 设为 0,模型在推理时也可能存在非确定性,尤其是大规模模型和分布式推理服务。所以更稳妥的做法是:每组问题跑 3 次,记录 3 次结果,存在分歧时再人工判断。
3. 核心参数与实现原理
3.1 提示词中的关键要素
从上面的模板可以看出,提示词设计有几个核心变量,分别会直接影响输出质量。
角色设定。让模型扮演“资深开发者”还是“技术博主”,回答角度差别很大。资深开发者更关注工程约束,技术博主更关注可读性和新手指引。
受众描述。明确说“给中等经验的团队”,模型就会默认对方能理解专业术语;如果不说受众,模型倾向于面向通用读者,结论会比较保守。
输出约束。要求“每个维度 3 条以内要点”可以避免回答过长;要求“必须选择一个”可以防止模型只罗列优缺点不给出决策。
场景限定。工具选择永远离不开具体场景。比如“微服务架构下的服务发现选型”和“单机项目”的结论一定不同。提示词里最好写明场景。
3.2 API 调用参数的工程含义
如果你在写代码调 LLM API,以下几个参数非常关键。
temperature:控制采样随机性。0 到 1 之间调试,评测类任务建议 0。max_tokens:限制最大输出长度。如果回答经常被截断,需要调大。query或messages:消息结构。建议用 system 消息设定角色,用 user 消息放入具体对比任务。model:模型标识符。不同模型能力不同,对比时必须记录清楚。
下面是一段调用 OpenAI Python SDK 的示例框架:
# 文件路径:eval_llm_tool_choice.py from openai import OpenAI client = OpenAI() def ask_llm_compare(client, model: str, tool_a: str, tool_b: str, temperature: float = 0.0): system_prompt = "你是一名资深开发者,擅长技术选型与工程架构对比。" user_prompt = f""" 请从以下几个维度客观对比 {tool_a} 和 {tool_b}: 1. 功能覆盖程度 2. 易用性与上手门槛 3. 生态与社区活跃度 4. 性能与资源占用 5. 团队协作与可维护性 6. 长期演进风险 要求: - 每个维度用 3 条以内的要点说明; - 不要给出模糊的'都很好'式结论; - 最后必须选择一个工具,并给出选择理由; - 如果存在不同使用场景下的不同结论,请明确场景。 """ try: response = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], temperature=temperature, max_tokens=1200, ) return response.choices[0].message.content except Exception as e: return f"ERROR: {e}"这段代码只是一个基础框架。实际运行前需要安装依赖并配置 API Key:
pip install openai export OPENAI_API_KEY="你的API Key"如果你用的是其他模型服务,比如 Anthropic、DeepSeek 或本地部署的模型,只需要替换客户端初始化和对应 API 方法即可,提示词部分可以复用。
3.3 评测结果的解析思路
拿到模型的输出后,不要只盯着“最终选择了谁”。更好的做法是记录三组信息:
- 模型的选择结果;
- 各维度下的关键表述(胜出点、劣势点);
- 模型是否在回答中附加了场景条件。
只有结果没有过程,你无法判断一次选择是否只是因为提示词中的某个措辞诱导出来的。
我习惯用 Python 字典保存完整结果:
# 文件路径:collect_results.py results = { "group": "editor", "tool_a": "VSCode", "tool_b": "Vim", "model": "gpt-4o", "temperature": 0.0, "raw_output": response_content, "parsed_winner": "VSCode", "factors": ["插件生态丰富", "上手门槛低", "调试体验好"], }数据量大了以后,可以存成 JSON 文件或导入 SQLite 做进一步统计。
4. 完整实战:让 3 个模型在 5 组工具中做选择
接下来我们跑一个完整的实验。我会用同一个提示词,让 3 个模型分别对 5 组工具做对比,最后汇总结果。
4.1 准备评测脚本
先创建一个完整脚本,调用多轮对话,并把结果写入 JSON 文件:
# 文件路径:run_tool_eval.py import json import os import time from openai import OpenAI client = OpenAI() EVAL_PROMPT_TEMPLATE = """ 你是一名资深开发者,正在帮助一位中等经验的开发团队做技术选型。 请从以下几个维度客观对比 {tool_a} 和 {tool_b}: 1. 功能覆盖程度 2. 易用性与上手门槛 3. 生态与社区活跃度 4. 性能与资源占用 5. 团队协作与可维护性 6. 长期演进风险 要求: - 每个维度用 3 条以内的要点说明; - 不要给出模糊的'都很好'式结论; - 最后必须选择一个工具,并给出选择理由; - 如果存在不同使用场景下的不同结论,请明确场景。 """ def build_user_prompt(tool_a: str, tool_b: str) -> str: return EVAL_PROMPT_TEMPLATE.format(tool_a=tool_a, tool_b=tool_b) def ask_model(model: str, tool_a: str, tool_b: str, temperature: float = 0.0): messages = [ {"role": "system", "content": "你是一名资深开发者。"}, {"role": "user", "content": build_user_prompt(tool_a, tool_b)}, ] try: resp = client.chat.completions.create( model=model, messages=messages, temperature=temperature, max_tokens=1200, ) return resp.choices[0].message.content except Exception as e: return f"ERROR: {e}" def main(): models = ["gpt-4o", "gemini-2.0-flash", "claude-3-5-sonnet"] groups = [ ("代码编辑器", "VSCode", "Vim"), ("Python环境管理", "Conda", "Poetry"), ("包管理", "npm", "pnpm"), ("前端构建", "Vite", "Webpack"), ("关系型数据库", "PostgreSQL", "MySQL"), ] all_results = [] for group_name, tool_a, tool_b in groups: for model in models: print(f"[*] 正在评测: {group_name} - {model}") output = ask_model(model, tool_a, tool_b) all_results.append({ "group": group_name, "tool_a": tool_a, "tool_b": tool_b, "model": model, "temperature": 0.0, "raw_output": output, }) time.sleep(1) # 避免触发热点限制 os.makedirs("output", exist_ok=True) with open("output/results.json", "w", encoding="utf-8") as f: json.dump(all_results, f, ensure_ascii=False, indent=2) print("[*] 评测完成,结果已保存到 output/results.json") if __name__ == "__main__": main()这里用了time.sleep(1)来降低请求频率,实际使用时要根据 API 限制调整。
4.2 分析输出文本提取结论
从results.json中读取输出,并从中判断每个模型的选择。写一个简单的解析脚本:
# 文件路径:parse_results.py import json import re def extract_winner(model_output: str): # 简单的关键词提取,实际项目中建议更精确地解析 if model_output.startswith("ERROR"): return "ERROR" lines = model_output.strip().split("\n") for line in reversed(lines): if "选择" in line or "推荐" in line: return line.strip() return "UNKNOWN" def main(): with open("output/results.json", "r", encoding="utf-8") as f: results = json.load(f) for item in results: winner = extract_winner(item["raw_output"]) print(f"{item['group']} | {item['model']} | Winner Hint: {winner[:80]}") if __name__ == "__main__": main()严格来说,这个解析脚本比较简单,只适合快速查看。如果想做统计,最好用结构化输出模式,比如要求模型输出 JSON。
4.3 使用 JSON 模式提升解析鲁棒性
很多模型服务支持 JSON 输出模式,可以要求模型返回固定结构。提示词可以调整如下:
请对比工具 A 和工具 B,输出严格 JSON 格式,字段如下: { "winner": "A 或 B", "reason": "一句话总结选择理由", "dimensions": [ {"name": "功能覆盖程度", "a_score": 1-10, "b_score": 1-10, "note": "说明"}, {"name": "易用性与上手门槛", "a_score": 1-10, "b_score": 1-10, "note": "说明"} ] }这样解析时就非常简单,直接用json.loads读取。
4.4 预期结果与真实体验
我实际跑过类似的实验,以“代码编辑器”组为例,大多数模型的默认选择是 VSCode。理由集中在:插件生态最丰富、调试体验好、对前端和 Python 开发都很友好。但只要在提示词里加入“服务器环境、无图形界面”的限制,结果会迅速偏向 Vim 或 Neovim。
这说明 LLM 工具选择对场景约束非常敏感。这也是为什么我特别强调“固定提示词、固定场景、固定维度”这三板斧。
如果你是不同版本或不同厂商的模型,输出可能与我这里有差异。这本身不是 bug,而是模型训练数据、参数设置、服务端升级的差异造成的自然结果。
5. 实验结果怎么解读:注意事项与偏见分析
5.1 流行度偏置
LLM 选择热门工具的倾向很明显。比如让模型对比 npm 和 pnpm,结果常常偏向 npm,理由是“社区更成熟、使用更广泛”。但真实项目中 pnpm 的磁盘占用和安装速度优势已经被大量测评验证过。
问题在于:LLM 的学习材料中,npm 的教程、提问、讨论数量远比 pnpm 多。它的训练目标不是“判断工具实际性能”,而是“生成符合人类语料分布的回答”。所以热门程度高=语料多=模型更倾向推荐。
应对方式:
- 在提示词中把“性能与资源占用”放在靠前权重;
- 要求模型引用可验证的数据或版本信息;
- 加入“不考虑流行度,只从工程实践角度分析”。
5.2 模型自身的“幻觉”风险
LLM 在列举工具特性时,偶尔会编造不存在的功能、版本号或社区事件。比如它可能声称某工具“官方支持某语言”,但实际上并不支持。
怎么降低幻觉影响?
- 在提示词中要求“如果你不确定某个特性是否真实,请标注不确定性”;
- 对关键结论进行二次人工验证,尤其是引用了具体数字或版本号的部分;
- 把 LLM 输出当作“假设清单”,而不是“事实清单”。
5.3 对比组的选择会影响结论
同样是“前端构建工具”对比,如果选 Vite vs Webpack,LLM 通常认可 Vite 的开发体验更好;如果选 Webpack vs Rollup,LLM 可能觉得 Rollup 更适合库开发。也就是说,不同对比组会激活模型对不同语料区域的记忆。
一次实验里,对比组必须与真实决策场景匹配。不要用“Vite vs Webpack”的结果去推导“我的公司要不要迁移到 Vite”——你还需要了解现有构建配置的复杂度、插件依赖和浏览器兼容要求。
5.4 模型版本迭代的影响
大模型的服务端更新很快。三个月前的实验结果,换了新的模型版本后很可能不一样。如果你要拿这个实验做长期跟踪,建议每个评测记录里都保存模型标识符和评测日期,便于后续追溯。
我自己的一个记录格式是:
{ "model": "gpt-4o", "model_version": "2024-11-20", "test_date": "2025-01-15" }虽然有些服务不会暴露完整版本号,但至少要记录调用时使用的模型别名和日期。
6. 常见问题与排查思路
做这类实验时,会遇到一些典型问题。我整理了一个排查表格:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 模型回答与上次完全不一致 | 未固定 temperature,或模型服务端有概率采样 | 设置 temperature=0,每组问题跑 3 次取多数 |
| 回答长度超出预期,被截断 | max_tokens 设置太小 | 调大 max_tokens,比如 1200 或 2000 |
| 模型总是打太极,不给出明确选择 | 提示词没有强制决策 | 增加“必须选择一个工具”的约束 |
| 不同模型的结果很难横向对比 | 提示词不一致,或对比组不同 | 统一使用同一模板、同一组工具、同一场景 |
| 输出内容包含幻觉特性 | 模型对工具认知不准确 | 要求标注不确定性,二次人工核对 |
| 网上教程说某模型很强,但实测很弱 | 版本差异或使用姿势不同 | 查看官方文档,确认模型标识符是否过期 |
| JSON 输出解析失败 | 模型没有严格遵循 JSON 格式要求 | 改用支持 JSON mode 的接口,或让模型只输出 JSON 片段 |
还有一个容易忽略的问题:API 请求失败。如果评测脚本中断,不要直接重跑全部任务。建议把每个分组的输出实时写入文件,已经成功的结果不要重复请求,节省时间和费用。
# 文件路径:output_writer.py import json class ResultWriter: def __init__(self, path: str): self.path = path self.existing = [] try: with open(path, "r", encoding="utf-8") as f: self.existing = json.load(f) except FileNotFoundError: pass def is_done(self, group, model): return any(r["group"] == group and r["model"] == model for r in self.existing) def append(self, data: dict): self.existing.append(data) with open(self.path, "w", encoding="utf-8") as f: json.dump(self.existing, f, ensure_ascii=False, indent=2)在主脚本中,每次请求前检查is_done,请求成功后调用append,这样重跑时不浪费请求。
7. 最佳实践与工程建议
7.1 把 LLM 评测当成“预调研”,而不是“决策依据”
LLM 给出的工具选择结果,适合用来快速形成候选列表、识别潜在的优缺点、发现你没考虑过的决策维度。但最终选型必须结合:
- 团队现有技能栈;
- 项目实际性能和运维要求;
- 许可证、费用、安全合规因素;
- 可维护性和人才市场情况。
正确的使用方式是:先用 LLM 做发散和排除,再用真实数据进行收敛。
7.2 评测前先定义“什么是好的选择”
很多技术选型争论不是因为资料不足,而是因为没有统一的评价标准。你可以在评测前先写好权重:
- 学习成本权重: 30% - 生态成熟度权重: 20% - 运行性能权重: 20% - 协作与维护权重: 20% - 长期风险权重: 10%然后把权重放进提示词或后续分析脚本,让“选择结果”可解释。
7.3 保存原始输出,便于复盘
我建议所有轮次的原始输出都不要丢弃。它是模型行为分析的第一手资料。后续如果模型版本更新、行业讨论热度变化,你再跑一次同样的实验,就能看到“认知漂移”的过程。这种纵向对比比单次“谁选了谁”有价值得多。
7.4 注意成本控制
大规模评测时,API 调用成本不可忽视。控制成本的方式:
- 用小模型做粗筛,再用大模型做深度对比;
- 对比组不要超过 10 组;
- 每组只跑 1 次确保稳定,分歧大的再跑多次;
- 利用缓存或本地模型辅助。
7.5 结合自定义数据做增强
如果你的团队有内部技术评审记录、历史选型文档或故障复盘报告,可以把这些材料组织成上下文,一起发给 LLM。这时候 LLM 的选择会更贴近团队实际,而不是泛泛而谈。
一种简单的做法是用 LangChain 或自己写的检索脚本,从内部 Wiki 中检索相关段落,拼接进提示词。不过要注意安全,不要给模型发送敏感的内部信息。涉及权限边界时,建议在测试环境验证后再考虑。
8. 总结与下一步
这篇文章从一个 HN 上的实验出发,拆解了“让 LLM 在开发者工具之间做选择”的完整方法。你掌握了:
- 实验设计的基础框架:固定提示词、统一维度、保存原始结果;
- 一套可直接运行的 Python 评测脚本;
- 如何从输出中提取结论,以及如何用 JSON 模式提升解析效率;
- 实验结果解读的常见坑:流行度偏置、幻觉风险、版本差异;
- 实际工程中如何把 LLM 输出当作预调研信息,而不是最终决策依据。
如果你想进一步深入,可以往这几个方向继续探索:
- 让 LLM 为对比维度打分并排序,然后人工叠加权重,形成一套半自动选型流程;
- 用 Embedding 检索技术社区的实时讨论,把最新信息补充进提示词,缓解模型知识陈旧的问题;
- 把评测工具封装成一个小型 Web 服务或 CLI 工具,团队成员可以随时发起一次选型对比;
- 对比“带外部检索”和“纯 LLM 回答”两种模式下的结果差异,你会更直观地理解大模型的认知边界。
工具选型从来没有银弹,LLM 也不会替你拍板。但它能帮你把社区经验、文档要点、技术推演过程压缩成几条可讨论的结论——剩下的事情,还是得靠你在真实项目里做验证。
如果这篇文章对你有帮助,建议自己动手把评测脚本跑一遍。你会发现,同样的提示词,换一个模型、换一个场景、甚至换一个提问顺序,结果都可能变化。这本身,就是理解 LLM 工作原理的最好方式。