写在前面
随着ChatGPT、通义千问等大语言模型的兴起,越来越多的开发者希望在自己的应用中集成AI能力,我决定通过实践来学习大模型API的调用方法。
我使用Python作为开发语言,采用阿里云百炼的通义千问模型(qwen3-max)作为基础模型,同时也体验了本地Ollama部署的qwen3:4b模型。
主题:围绕"科技资讯"这个方向,一步步实现文本分类、信息抽取、语义匹配三个常见场景。
1.环境准备
1.1 安装python
验证 Python 环境是否安装成功
# 通用命令(推荐,所有系统兼容)
python3 --version
# 补充:Windows系统若已配置PATH,也可执行
python --version
验证 pip3(Python 包管理工具)pip3 是 Python3 默认的包管理工具类似于安装Nodejs中npm包管理工具,用于安装第三方库,验证命令:
# 通用命令
pip3 --version
# Windows补充命令
pip --version
安装成功
2. OpenAI 库的基础使用
Python 库已经成为了调用大模型的"事实标准"。不管是 OpenAI 官方、阿里云 DashScope、还是本地 Ollama,都提供了 OpenAI 兼容的接口。这意味着写一套代码,换个 base_url 就能切换模型。
2.1 安装openai
pip install openai
模型可以使用阿里云百炼,也可以使用 Ollama本地部署模型,先启动 Ollama 并拉取模型:
2.2 第一次调用OpenAI
最简单的调用只需要三步:创建 client → 构造 messages → 发起请求。
from openai import OpenAI
# 1. 创建 client 对象
client = OpenAI(
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
# api_key="YOUR_API_KEY" # 如果环境变量没配,这里需要手动传
)
# 2. 调用模型
response = client.chat.completions.create(
model="qwen3-max",
messages=[
{"role": "system", "content": "你是一个科技资讯助手"},
{"role": "user", "content": "2026年AI领域有哪些重要趋势?"},
]
)
# 3. 打印结果
print(response.choices[0].message.content)
api_key可以直接配置到全局环境变量中~./zshrc,就无需在创建client时明文传入了
export OPENAI_API_KEY="your key"
export DASHSCOPE_API_KEY="your key"
关键点拆解:
| 参数 | 作用 |
|---|---|
base_url | API 地址,换这个就能切换不同服务商 |
model | 模型名称,不同平台名称不一样 |
messages | 对话历史,system 设定人设,user 是用户输入,assistant 是模型回复 |
role | 三条消息角色:system(系统设定)、user(用户)、assistant(助手) |
system消息就像是给演员的"角色剧本",告诉模型"你是谁、该怎么做"。assistant消息可以用作 few-shot 示例,让模型"照着学"。
2.3 流式输出:让用户体验"打字机"效果
非流式调用会等模型全部生成完才返回,用户可能要等十几秒。开启 stream=True 后,模型每生成一段就返回一段,实现类似 ChatGPT 的打字机效果。
from openai import OpenAI
client = OpenAI(
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
response = client.chat.completions.create(
model="qwen3-max",
messages=[
{"role": "system", "content": "你是一个科技资讯助手,回答问题时请简洁"},
{"role": "user", "content": "什么是大语言模型?用一句话解释"},
],
stream=True # 开启流式输出
)
# 流式返回的结果需要遍历 chunk
for chunk in response:
print(chunk.choices[0].delta.content, end="", flush=True)
流式处理要点:
chunk.choices[0].delta.content是每一小段文本,可能是几个字或一个词end=""避免 print 自动换行,flush=True立即刷新到终端- 第一个 chunk 的
delta.content可能为None,生产环境需要做空值判断
2.4 附带历史消息:让模型"记住"上下文
大模型本身是无状态的,每次调用都是独立的。要实现多轮对话,需要把之前的消息一起发过去。
from openai import OpenAI
client = OpenAI(
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 把之前的对话历史全部带上
response = client.chat.completions.create(
model="qwen3-max",
messages=[
{"role": "system", "content": "你是科技资讯助手,回答简洁"},
{"role": "user", "content": "英伟达最新一代GPU是什么?"},
{"role": "assistant", "content": "英伟达最新一代数据中心GPU是Blackwell架构的B200。"},
{"role": "user", "content": "它比上一代性能提升多少?"}, # 这个问题依赖上文
],
stream=True
)
for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
为什么需要历史消息? 第二个问题"它比上一代性能提升多少?"中的"它"指代的是前面提到的 B200。如果不带上历史消息,模型就不知道"它"是谁。
3. 提示词优化实战
提示词(Prompt)决定了模型的输出质量。下面通过三个实战案例,展示几种最常用且效果立竿见影的提示词优化技巧。
统一主题:所有案例都围绕"科技资讯处理"场景,包括科技文章分类、关键信息抽取、语义相似度判断。
3.1 案例一:科技文本分类(Few-Shot 提示)
场景:给定一段科技相关的文本,自动判断它属于哪个类别。
核心思路:Few-Shot Learning
直接让模型分类,它可能按自己的理解来。但如果给它几个"输入→输出"的示例,它就能照葫芦画瓢,准确率大幅提升。这就是 Few-Shot Prompting。
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1" # 本地 Ollama
)
# 示例数据:类别 → 文本
examples_data = {
'AI前沿': 'OpenAI今日发布了GPT-5,该模型在多项基准测试中超越前代,特别是在数学推理和代码生成方面表现突出。',
'产品发布': '苹果公司在WWDC上正式推出了Vision Pro二代,售价降低至2000美元,新增手势追踪和眼动交互功能。',
'行业分析': '据IDC最新报告,2025年Q2全球AI芯片市场规模达到180亿美元,同比增长65%,其中GPU占比超过70%。',
'投融资': 'AI编程助手Cursor完成1亿美元C轮融资,估值达到100亿美元,红杉资本领投。'
}
# 待分类的文本
questions = [
"今日,华为在开发者大会上发布了HarmonyOS NEXT正式版,全面支持纯血鸿蒙应用生态,不再兼容Android应用。",
"据市场分析机构Gartner预测,到2026年全球企业级AI软件支出将突破5000亿美元,SaaS和AI Agent是主要增长点。",
"字节跳动旗下豆包大模型团队宣布开源新一代多模态模型,支持文本、图像和视频的联合理解与生成。",
"今天天气真不错,适合出去散步。" # 干扰项,不属于任何科技类别
]
# 构造 messages:system 说明任务 + 示例对话
messages = [
{
"role": "system",
"content": "你是科技资讯专家,请将文本分类为['AI前沿', '产品发布', '行业分析', '投融资']之一,不属于任何类别的回答'不清楚类别'。以下是示例:"
},
]
# 把示例数据转为 user/assistant 对话对
for category, text in examples_data.items():
messages.append({"role": "user", "content": text})
messages.append({"role": "assistant", "content": category})
# 批量分类
for q in questions:
response = client.chat.completions.create(
model="qwen3:4b",
messages=messages + [{"role": "user", "content": f"请分类:{q}"}]
)
print(f"文本: {q[:30]}... → 类别: {response.choices[0].message.content}")
消息构造示意图:
messages 最终结构:
┌─────────────────────────────────────────────────┐
│ system: 你是科技资讯专家,请分类为[...] │
│ user: OpenAI今日发布了GPT-5... │
│ assistant: AI前沿 │
│ user: 苹果公司在WWDC上正式推出... │
│ assistant: 产品发布 │
│ user: 据IDC最新报告... │
│ assistant: 行业分析 │
│ user: AI编程助手Cursor完成... │
│ assistant: 投融资 │
│ user: 请分类:今日,华为在开发者大会上... │ ← 实际要分类的
└─────────────────────────────────────────────────┘
3.2 案例二:科技信息抽取(结构化输出)
场景:从一篇科技新闻中提取关键字段(日期、公司名、产品名、金额等),并以 JSON 格式返回。
核心思路:JSON 结构化输出 + Few-Shot 示例
直接让模型"提取信息",它可能返回自然语言描述。但如果要求它输出 JSON,并在示例中展示格式,它就能稳定返回结构化数据,方便后续程序处理。
from openai import OpenAI
import json
client = OpenAI(base_url="http://localhost:11434/v1")
# 定义要抽取的字段
schema = ['日期', '公司名称', '产品名称', '融资金额', '投资方']
# 示例数据
examples_data = [
{
"content": "2025年3月15日,AI编程公司Cursor宣布完成1亿美元C轮融资,由红杉资本领投,Andreessen Horowitz跟投。该公司主打产品Cursor AI Editor已成为开发者热门工具。",
"answers": {
"日期": "2025年3月15日",
"公司名称": "Cursor",
"产品名称": "Cursor AI Editor",
"融资金额": "1亿美元",
"投资方": "红杉资本、Andreessen Horowitz"
}
},
{
"content": "昨日,华为在深圳总部正式发布Mate 70系列手机,搭载全新麒麟9100芯片,起售价5499元。",
"answers": {
"日期": "昨日",
"公司名称": "华为",
"产品名称": "Mate 70系列",
"融资金额": "原文未提及",
"投资方": "原文未提及"
}
}
]
# 待抽取的文本
questions = [
"2025年6月1日,月之暗面科技宣布完成10亿元人民币B轮融资,高榕资本独家领投,公司将用于Kimi智能助手的产品迭代。",
'特斯拉于上海超级工厂投产了搭载HW5.0芯片的新一代自动驾驶硬件,马斯克称这是"史上最大升级"。'
]
# 构造 messages
messages = [
{
"role": "system",
"content": f"你是信息抽取专家。请从文本中提取 {schema} 这些字段,以JSON格式输出。如果某字段信息不存在,填'原文未提及'。参考以下示例:"
},
]
# 追加示例
for example in examples_data:
messages.append({"role": "user", "content": example["content"]})
messages.append({
"role": "assistant",
"content": json.dumps(example["answers"], ensure_ascii=False)
})
# 批量抽取
for q in questions:
response = client.chat.completions.create(
model="qwen3:4b",
messages=messages + [{"role": "user", "content": f"请抽取以下文本的信息:{q}"}]
)
result = response.choices[0].message.content
print(f"原文: {q[:40]}...")
print(f"抽取结果: {result}\n")
关键技巧:
- 明确字段列表:在 system 消息中列出要抽取的所有字段
- JSON 格式约束:要求模型输出 JSON,方便后续
json.loads()直接解析 - 缺失值处理:约定"原文未提及"作为默认值,避免模型编造信息
- 示例中展示完整格式:模型会严格模仿示例的 JSON 结构
实际使用中,拿到结果后建议用 json.loads() 验证一下,确保返回的是合法 JSON:
parsed = json.loads(result)
print(parsed["公司名称"]) # 直接按 key 取值
3.3 案例三:科技文本匹配判断(对比推理)
场景:给定两句话,判断它们描述的是否是同一件事/同一主题。这在资讯去重、推荐系统等场景很常见。
核心思路:正负示例对比 + 格式化输入
让模型判断两句话是否"匹配",关键是要给它正面示例(匹配)和负面示例(不匹配),让它学会区分。
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1")
# 正负示例
examples_data = {
"是": [ # 这两句说的是同一件事
("英伟达发布Blackwell架构B200芯片,性能比H100提升30倍。", "英伟达推出新一代GPU B200,采用Blackwell架构,性能远超H100。"),
("字节跳动旗下豆包大模型团队宣布开源新一代多模态模型。", "豆包团队开源了多模态大模型。"),
],
"不是": [ # 两句话各说各的
("英伟达股价今日下跌5%。", "苹果宣布Vision Pro二代正式发布。"),
("特斯拉上海工厂开始生产HW5.0芯片。", "SpaceX星舰完成第六次试飞。"),
]
}
# 待判断的文本对
questions = [
("华为发布HarmonyOS NEXT正式版,不再兼容Android。", "纯血鸿蒙系统正式发布,安卓应用将无法运行。"),
("AI芯片市场Q2增长65%。", "AI编程助手Cursor完成1亿美元融资。"),
("小米SU7交付量突破10万台。", "小米汽车市场表现强劲,累计交付超10万辆SU7。"),
]
# 构造 messages
messages = [
{
"role": "system",
"content": "你帮我完成文本匹配判断。我会给你两个句子,用[]包围,请判断它们是否描述同一件事,只回答'是'或'不是'。参考以下示例:"
},
]
# 追加正负示例
for label, pairs in examples_data.items():
for s1, s2 in pairs:
messages.append({"role": "user", "content": f"句子1:[{s1}],句子2:[{s2}]"})
messages.append({"role": "assistant", "content": label})
# 批量判断
for s1, s2 in questions:
response = client.chat.completions.create(
model="qwen3:4b",
messages=messages + [{"role": "user", "content": f"句子1:[{s1}],句子2:[{s2}]"}]
)
print(f"句子1: {s1[:35]}...")
print(f"句子2: {s2[:35]}...")
print(f"匹配结果: {response.choices[0].message.content}\n")
关键技巧:
- 用
[]包裹句子:让模型清楚知道哪里是句子边界,避免混淆 - 正负示例均衡:匹配和不匹配的例子各给几个,防止模型偏向某一方
- 约束输出:要求"只回答'是'或'不是'",避免模型输出多余解释
4. 核心知识点总结
4.1 OpenAI SDK 调用流程
不管什么场景,调用流程都是固定的三步:
# 第一步:创建 client
client = OpenAI(base_url="xxx", api_key="xxx")
# 第二步:构造 messages 并调用模型
response = client.chat.completions.create(
model="模型名",
messages=[...],
stream=True/False
)
# 第三步:处理结果
# 非流式:response.choices[0].message.content
# 流式:遍历 response,每次取 chunk.choices[0].delta.content
4.2 提示词优化的三个实用技巧
| 技巧 | 适用场景 | 核心做法 |
|---|---|---|
| Few-Shot 示例 | 分类、抽取、判断 | 在 messages 中塞入几个"输入→输出"的示例对 |
| JSON 结构化 | 信息抽取、API 返回 | 要求模型输出 JSON,并在示例中展示格式 |
| 格式化输入 | 文本对比、多段输入 | 用特殊符号(如 [])标注边界,避免混淆 |
4.3 本地 vs 云端模型选择
| 本地 Ollama | 云端 DashScope | |
|---|---|---|
base_url | http://localhost:11434/v1 | https://dashscope.aliyuncs.com/compatible-mode/v1 |
| 模型示例 | qwen3:4b | qwen3-max |
| 优点 | 免费、隐私安全、离线可用 | 模型更强、无需本地算力 |
| 缺点 | 小模型能力有限 | 需要 API Key、按量计费 |
4.4 JSON 基础回顾
提示词优化案例中频繁用到 JSON 序列化/反序列化,这里简单回顾:
import json
# dict → JSON 字符串
data = {"name": "张三", "age": 25}
json_str = json.dumps(data, ensure_ascii=False)
# '{"name": "张三", "age": 25}'
# JSON 字符串 → dict
parsed = json.loads(json_str)
# {'name': '张三', 'age': 25}
# list → JSON 字符串
items = [{"name": "A"}, {"name": "B"}]
json.dumps(items, ensure_ascii=False)
# '[{"name": "A"}, {"name": "B"}]'
ensure_ascii=False很重要,否则中文会被转成\uXXXX编码,不方便阅读和调试。
写在最后
我们做的,并不只是“调用一个大模型 API”,而是在尝试一种新的开发方式。
过去,我们习惯用代码去精确描述规则,而现在,我们开始用自然语言去“定义能力”。
这两者的区别在于:
- 代码解决的是确定性问题
- 大模型更擅长处理模糊、复杂、难以穷举规则的问题