调用大模型的两种方法
调用方法一:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{
"role": "system",
"content": "你是 SQL 专家,只输出 JSON,不要输出其他内容。",
},
{"role": "user", "content": prompt},
],
temperature=0.2, # SQL 生成需要高度确定性
)
调用方法二:
intent = llm.invoke(classification_prompt).content.strip().lower()
两者本质都是调用大模型,最终发往模型服务的请求是完全一致的,区别只在于抽象层级和封装程度不同:
- 第一个是直接调用模型厂商的原生 API 接口,所有请求参数都需要手动组装,精细度高、可控性强;
- 第二个是调用 LangChain 框架封装好的模型对象,框架底层帮你自动完成了参数组装、格式转换,写法更简洁。
一.第一种:原生 SDK 调用(generate_sql 函数)
你看到的 client.chat.completions.create + 完整 messages 数组,是大模型厂商提供的标准 OpenAI 兼容接口的原生写法。
1. 为什么必须写完整的 messages 结构
这是大模型 Chat Completions 接口的强制要求:
- 模型本身是对话式的,必须通过
role角色来区分消息身份:system(系统指令 / 人设)、user(用户输入)、assistant(模型回复) - 原生接口不会帮你做任何格式封装,你必须严格按照接口规范,手动把所有对话内容组装成列表传进去
2. 为什么这个函数要用原生写法
这个场景有两个强需求,原生调用更合适:
- 强格式约束需求:生成 SQL 并输出 JSON,对输出稳定性要求极高,需要单独的
system角色来给模型强人设、强规则,单独的 system 指令比混在用户提示里的约束效果更好 - 精细参数控制:需要手动指定模型名称
kimi-k2.6、调低temperature=0.2,还要拿到最原始的响应结构,后续手动做 JSON 提取、异常捕获、格式清洗,原生调用能拿到完整的响应对象,灵活度最高
3.层级对应总结
client→OpenAI实例(主客户端,承载密钥、地址、http 会话)client.chat→Chat实例(聊天资源分组)client.chat.completions→Completions实例(对话补全资源对象)client.chat.completions.create()→Completions类里面的实例方法,发起 POST 请求访问/v1/chat/completions
4.极简记忆口诀
chat.completions = 映射接口路径 /v1/chat/completions
chat → 对话大类
completions → 生成补全任务
.create() → 创建一次对话请求
# 对话生成(日常聊天用)
client.chat.completions.create() # /v1/chat/completions
# 旧版文本续写(基本淘汰)
client.completions.create() # /v1/completions
# 向量嵌入接口
client.embeddings.create() # /v1/embeddings
# 图片生成
client.images.generate()
二.第二种:LangChain 封装调用(run_data_analysis 函数)
你看到的 llm.invoke(分类提示词),是 LangChain 框架对大模型调用的高层封装。
1. 为什么一句话就能完成调用
llm 是提前初始化好的 LangChain 模型对象(比如 ChatOpenAI / ChatMoonshot),它的 invoke() 方法底层帮你做了这些重复工作:
- 自动把你传入的字符串,包装成
[{"role": "user", "content": "你的提示词"}]的标准 messages 格式 - 自动携带初始化时配置好的模型名、temperature、API Key 等参数
- 自动解析响应,直接返回模型的文本内容,不用你再写
.choices[0].message.content去取值
相当于把原生调用里的「组装参数 → 发请求 → 提取结果」三步,封装成了一步。
2. 为什么这个函数可以用简化写法
意图分类是非常简单的单轮场景:
- 不需要复杂的多角色对话,分类规则直接写在用户提示词里也能正常工作
- 不需要精细控制每一个请求参数,复用全局初始化的模型配置即可
- 只需要拿到纯文本结果做分支判断,不需要处理复杂的响应结构
- 同时这个函数后续还要对接 LangChain 的 Agent,统一用 LangChain 的接口写法,和生态组件的兼容性更好
三.两种调用的写法对比与选型
| 维度 | 原生 SDK 调用 | LangChain 封装调用 |
|---|---|---|
| 抽象层级 | 底层,直接对接模型接口 | 高层,框架封装屏蔽细节 |
| 代码量 | 多,需手动组装所有参数 | 少,一行完成调用 |
| 可控性 | 极高,所有参数可精细调整 | 中等,常用参数已封装 |
| 格式灵活性 | 强,适合自定义解析、异常处理 | 弱,统一返回标准格式 |
| 适用场景 | 结构化输出、多轮对话、强格式约束 | 简单单轮请求、快速开发、对接 Agent 生态 |
四.两种调用方法的返回对比
① 原生 SDK(openai 包)
client.chat.completions.create() 返回的顶层对象叫 ChatCompletion
response = client.chat.completions.create(...)
response:顶层大对象,不存在.message属性response.choices:列表,用来存放一条 / 多条回答(由参数n控制数量)choices[0]:列表第 0 项,类型是Choice对象choices[0].message:消息对象ChatCompletionMessagechoices[0].message.content:真正的回答文字
# 原生
res = client.chat.completions.create(...)
res.choices[0].message.content
② LangChain ChatOpenAI 的 invoke
# LangChain 封装之后
res_msg = llm.invoke("你好")
res_msg.content
这里为什么不用写 choices [0]? LangChain 在内部已经帮你做完了
choices[0].message.content的提取, 返回给你的直接是AIMessage对象,顶层就带.content。
五.最后总结
没有哪种写法绝对更好,完全由场景决定:
- 需要稳定、精准、自定义解析 → 用原生调用
- 需要简洁、快速、和框架生态联动 → 用封装调用
- 两者底层最终发往大模型的 HTTP 请求,本质是完全一样的。
补充:LangChain内部的两种写法:
写法 1:最简字符串写法(单轮提问首选,最推荐)
直接传入用户问题字符串即可,Agent 内部会自动包装成标准消息格式,完全不用你手动写 messages:
response = sql_agent.invoke("公司一共有多少名员工?")
这是最简洁、最不容易出错的写法,你的 SQL 查询单轮场景,用这个就足够了。
写法 2:标准完整写法(多轮上下文场景用,分角色定义)
当需要传入历史对话、自定义系统消息时,使用 messages 键 + LangChain 标准消息对象:
from langchain_core.messages import HumanMessage, AIMessage
response = sql_agent.invoke({
"messages": [
HumanMessage(content="查询一下技术部有多少人"),
AIMessage(content="技术部共有12人"),
HumanMessage(content="公司一共有多少名员工?")
]
})
这是多轮对话场景的官方标准写法,类型安全、兼容性最好。
五种调用方法的使用规范
| 调用场景 | 标准输入格式 |
|---|---|
原生 SDK client.chat.completions.create |
必须传 [{"role": "user", "content": "..."}] 字典列表 |
LangChain llm.invoke 单轮 |
直接传字符串 |
LangChain llm.invoke 多角色 / 多轮 |
传 [SystemMessage(), HumanMessage()] 对象列表 |
LangChain agent.invoke 单轮 |
直接传字符串 |
LangChain agent.invoke 带上下文 |
传 {"messages": [消息对象列表]} |
更多推荐

所有评论(0)