调用方法一:

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.层级对应总结

  • clientOpenAI 实例(主客户端,承载密钥、地址、http 会话)
  • client.chatChat 实例(聊天资源分组)
  • client.chat.completionsCompletions 实例(对话补全资源对象)
  • 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:消息对象 ChatCompletionMessage
        • choices[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": [消息对象列表]}
    Logo

    汇聚全球AI编程工具,助力开发者即刻编程。

    更多推荐