AI Agent 开发(03):LLM 调用基础

QuibblerAgentQuibblerAgent 2026-10-25 约 20 分钟 1 次阅读

上一篇我们把第一个 Agent 跑了起来:一个模型、一个工具、一个循环。跑通之后再回头看,你会发现 Agent 的每一种高级能力——规划、反思、调用工具——最终都落在同一个原子操作上:把一组消息发给大语言模型(LLM),拿回一条回复。这一篇我们下沉一层,把这个原子操作拆开讲透:模型怎么初始化、消息长什么样、参数怎么调、怎么流式输出、怎么算钱、怎么换供应商。

理解了这一层,后面 LangGraph 的图、MCP 的工具、多 Agent 的编排,本质上都只是往这个原子操作上叠流程。本文按"统一入口、消息类型、参数与容错、流式输出、多模态、计费与选型、多供应商切换"的顺序展开,代码基于 langchain 1.x(langchain-core 1.6),全部可运行。


1、统一模型调用入口

先看不用框架的处境。OpenAI、Anthropic、Google 三家 SDK 的认证方式、客户端类、消息格式、流式事件全都不一样,DeepSeek 和 Ollama 又各自一套。每接一家供应商就要学一遍新 API,换模型约等于重写调用层。

LangChain 的解法是 chat model 抽象:所有模型对外暴露同一套接口——invoke 发消息、stream 流式、bind_tools 绑工具,实例创建统一走 init_chat_model 入口。依赖安装如下:

# 系列项目用 uv 管理依赖,pip 等价
uv add langchain langchain-openai
# 其他供应商按需安装
uv add langchain-anthropic langchain-deepseek langchain-ollama

第一行装的是 init_chat_model 本体和 OpenAI 集成,其余按用到的供应商安装,不必把全世界的包都拖进项目。最基础的调用如下:

from langchain.chat_models import init_chat_model

model = init_chat_model("openai:gpt-5")
resp = model.invoke("用一句话解释什么是 Agent")
print(resp.content)

"openai:gpt-5" 是 provider:model 格式的模型串;只写裸模型名时 LangChain 会按前缀推断供应商(gpt- 推 openai、claude- 推 anthropic),官方建议显式写全,行为更可控。这段代码没有出现任何 API Key,因为集成包默认从 OPENAI_API_KEY 等环境变量读取。

这套抽象真正值钱的地方是运行时切换:

model = init_chat_model(
    "openai:gpt-5",
    configurable_fields=("model", "model_provider"),
)

q = "用一句话解释什么是 token"
print(model.invoke(q).content)  # 默认走 GPT-5

# 运行时切到 Claude,业务代码一个字不改
print(model.invoke(q, config={"configurable": {
    "model": "anthropic:claude-sonnet-5-5",
}}).content)

换模型只改一个字符串。同一个对象、同一份调用代码,配置不同就走不同供应商,返回值都是统一的 AIMessage。做 A/B 对比、按任务路由模型、线上降级切换,都建立在这个能力上。完整参数见官方参考文档。

还有一层价值容易被忽略:chat model 实现了 Runnable 协议,invoke、stream、batch 天然可组合,后面篇章里的链、图、事件流,都是把模型当标准积木拼出来的。上一篇里 LangSmith 能画出完整的调用链,靠的也是这套协议——四家供应商的请求在追踪里长一个样,排障成本被大幅摊平。

2、四种消息类型

模型 API 是无状态的:每次调用都要把完整对话历史发过去,所谓历史就是一张消息列表。langchain-core 用四个类覆盖一张对话里的全部角色,这是所有后续内容的地基。

消息类型 对应角色 用途
SystemMessage system 全局人设与规则,只出现一次且在最前
HumanMessage user 用户输入与外部指令
AIMessage assistant 模型回复,可携带 tool_calls
ToolMessage tool 工具执行结果,回传给模型

四种消息的构造示例如下,其中 AIMessage 和 ToolMessage 平时由框架在工具循环里自动产生,这里手工写出来便于看清结构:

from langchain_core.messages import (
    SystemMessage, HumanMessage, AIMessage, ToolMessage,
)

msgs = [
    SystemMessage(content="你是天气助手,回答控制在 50 字以内"),
    HumanMessage(content="北京今天天气怎么样?"),
    # 模型决定调工具:tool_calls 记录函数名与参数
    AIMessage(content="", tool_calls=[
        {"name": "get_weather", "args": {"city": "北京"}, "id": "call_001"},
    ]),
    # 你的代码执行工具后,用相同 id 把结果回传
    ToolMessage(content="晴,西北风 3 级,12℃", tool_call_id="call_001"),
    HumanMessage(content="那适合跑步吗?"),
]
print(model.invoke(msgs).content)

三点值得注意。第一,多轮对话的本质就是不断往列表尾部追加消息,所谓"记忆"就是这张列表本身。第二,ToolMessage 的 tool_call_id 必须与它响应的那条 tool_calls 里的 id 配对,模型靠它知道哪次调用对应哪份结果。第三,SystemMessage 是塑造 Agent 行为最便宜的杠杆,放在列表最前面,每次请求都会重发。工具调用的完整闭环是系列第 5 篇的主题,这里先记住消息的形状;调试时对每条消息调 pretty_print(),角色前缀一目了然,比裸打印清爽得多。

3、调用参数与容错

先说一个正在发生的变化:temperature 正在退出历史舞台。GPT-5 系列把 temperature 固定为 1,传其他值直接报错;Claude API 自 2026 年 10 月起对所有模型拒绝 temperature、top_p、top_k 参数。新一代推理模型靠思考深度控制输出风格,而不是采样随机性,写新代码别再把 temperature 当核心调参手段。真正需要记住的是这几个:

model = init_chat_model(
    "anthropic:claude-sonnet-5-5",
    max_tokens=1024,   # 输出上限,兼作单次成本上限
    timeout=30,        # 单次请求超时,单位秒
    max_retries=2,     # SDK 层对 429/5xx 自动重试
)

# Runnable 层重试:指数退避,最多 3 次
robust = model.with_retry(
    stop_after_attempt=3, wait_exponential_jitter=True,
)
print(robust.invoke("用三句话介绍 HTTP 重试的常见策略").content)

max_tokens 仍是各供应商通用的参数,生产上建议按任务类型给硬上限:分类抽取给 256,写代码给 8192,防止异常回复烧钱。timeout 和 max_retries 会透传给底层 SDK;with_retry 则是 LangChain Runnable 层的通用能力,可叠加指数退避。两层重试别同时拉满,总等待时间会变成超时乘重试次数的乘积,正常配置选一层做主力即可。跨模型兜底用 with_fallbacks([备用模型]),主模型报错时自动切换,容错一节到生产化篇再展开。

4、流式输出

非流式调用要等全部 token 生成完才返回,长回复动辄几十秒白屏。流式把生成过程切成小块,首 token 一到就上屏,打字机效果就是这么来的:

model = init_chat_model("anthropic:claude-sonnet-5-5", max_tokens=512)

for chunk in model.stream([HumanMessage(content="写一段 100 字的产品发布文案")]):
    print(chunk.content, end="", flush=True)  # 逐 token 上屏
print()  # 收尾换行

每个 chunk 是 AIMessageChunk,把所有块的内容拼接起来与非流式的 invoke 结果等价,只是拿到的时间粒度不同。两个实践要点:一是流式下 usage_metadata 挂在最后一个 chunk 上,但 OpenAI 需要显式传 stream_usage=True 才返回;二是生产服务建议默认走流式,长输出既改善体验,又天然规避了 HTTP 超时——异步接口是 astream,配合 FastAPI 的 StreamingResponse 即可。Agent 场景下,流式还是"过程可见性"的基础,后面讲 LangGraph 事件流时会反复用到。最后提醒一点:chunk 不等于 token,供应商通常按批次下发,"逐 token"只是体感描述,更细的控制粒度要到第 13 篇的事件流才有。

5、多模态图片输入

让模型"看图"不需要换接口,只是把 HumanMessage 的 content 从字符串换成内容块列表。URL 和 base64 两种传法如下:

import base64
from langchain_core.messages import HumanMessage

# 方式一:图片 URL
url_msg = HumanMessage(content=[
    {"type": "text", "text": "这张截图里有什么问题?"},
    {"type": "image_url", "image_url": {"url": "https://example.com/ui.png"}},
])

# 方式二:本地文件转 base64 内嵌
with open("screenshot.png", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()
b64_msg = HumanMessage(content=[
    {"type": "text", "text": "这个页面上有几处按钮?"},
    {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
])

model = init_chat_model("google_genai:gemini-3.1-pro-preview")
print(model.invoke([b64_msg]).content)

这个块列表格式是 LangChain 的统一多模态格式,各家集成包会自动转换成自家协议,代码因此保持供应商无关。计费上图片按分辨率折算成 token,一张截图通常几百到一两千 token,直接计入输入成本。Agent 里读 UI 截图、看报错图片、分析图表都是常见用法;内网图片走 base64,公网可访问的走 URL。注意各家模型的多模态支持范围不同,有的能看图不支持音频,有的对图片尺寸和数量有上限,切换供应商前先查一遍支持矩阵,别等运行时才收到 400。

6、token 计费与选型

每次调用的用量藏在返回消息的 usage_metadata 里,langchain-core 把它标准化成统一字段,四家供应商同一个口径:

PRICE = {"input": 2.0, "output": 10.0}  # Sonnet 5.5,美元/百万 token

resp = model.invoke([HumanMessage(content="解释一下什么是上下文窗口")])
usage = resp.usage_metadata
# {'input_tokens': 15, 'output_tokens': 102, 'total_tokens': 117, ...}

cost = (usage["input_tokens"] / 1e6 * PRICE["input"]
        + usage["output_tokens"] / 1e6 * PRICE["output"])
print(f"本次调用成本约 ${cost:.6f}")

两个数字要刻进脑子里。第一,输出单价通常是输入的 5 倍,压缩输出长度比压缩输入更省钱;第二,缓存命中价更低,GPT-5 缓存输入只要 $0.125/百万 token,重复前缀(系统提示、工具定义)值得专门做缓存设计。

再拿 Agent 的成本结构算笔账:一个 10 轮的工具循环,固定前缀(系统提示加工具定义)2000 token、每轮新增 500 token,末轮单次输入就有 6500 token 上下,全程累计输入超过 4 万——这还只是一次任务。每轮工具往返都重发全部历史,成本随轮数线性上涨,所以长任务的省钱手段是缓存前缀、压缩历史,而不是简单换个便宜模型了事。

模型选型速查如下(价格为 2026 年 10 月官方定价快照,单位为美元/百万 token):

模型 输入 输出 定位
GPT-5 nano $0.05 $0.40 高频杂活:分类、抽取、路由
Claude Haiku 5.5 $0.10 $0.50 杂活,质量更稳
GPT-5 mini $0.25 $2.00 轻量任务
Gemini 3 Flash $0.50 $3.00 轻量任务,多模态强
DeepSeek Flash $0.15 起 $0.60 起 超低价,1M 上下文
GPT-5 $1.25 $10.00 主力
Claude Sonnet 5.5 $2.00 $10.00 主力,Agent 首选
Gemini 3.1 Pro $2.00 $12.00 主力,多模态
DeepSeek V4 Pro $0.66 起 $1.98 起 主力,性价比突出
Claude Opus 5.5 $4.00 $20.00 复杂决策
GPT-5.5 $5.00 起 $30.00 起 复杂决策

标"起"的条目存在分级或分时计价:DeepSeek 分峰谷时段,Gemini Pro 按上下文长度分档。各家调价频繁,落地前以 OpenAI、DeepSeek、Gemini 的定价页和 Claude 官方文档为准。

选型原则一句话:便宜模型干杂活,强模型做决策。Agent 的调用量里九成是简单步骤——路由、抽取、格式化、判断是否继续——一成的关键节点才需要强模型的推理能力。文档批量抽取用 Haiku,任务规划与代码生成用 Sonnet,两者单价差 20 倍,混合调用的总成本能比全用主力模型低一个量级。

7、多供应商切换

把前面的能力合起来:同一个函数,四类供应商的对照代码。差异全部收敛在模型串和环境变量里:

from langchain.chat_models import init_chat_model

# OpenAI:读环境变量 OPENAI_API_KEY
gpt = init_chat_model("openai:gpt-5")

# Anthropic:读环境变量 ANTHROPIC_API_KEY
claude = init_chat_model("anthropic:claude-sonnet-5-5")

# DeepSeek:读环境变量 DEEPSEEK_API_KEY
ds = init_chat_model("deepseek:deepseek-v4-pro")

# 本地 Ollama:不需要 Key,默认 http://localhost:11434
local = init_chat_model("ollama:qwen3:8b")

# 四个模型的调用方式完全一致
for m in (gpt, claude, ds, local):
    print(m.invoke("一句话自我介绍").content)

四段初始化之外没有一行分支代码,这正是第 1 节抽象的价值。三点补充:

  • Key 一律走环境变量,绝不写进代码和仓库
  • DeepSeek V4 Pro 输出价约为 GPT-5 的四分之一,高频任务迁移立省成本
  • ollama pull qwen3:8b 拉下模型即可离线推理,数据不出本机

企业自建的 vLLM 集群暴露 OpenAI 兼容接口,接入方式是 init_chat_model("openai:模型名", base_url="http://localhost:8000/v1", api_key="EMPTY")。本地模型的代价是效果上限和并发吞吐,拿它做开发调试、把强模型留给关键路径,是常见的组合。

8、总结

这一篇把 LLM 调用拆到了原子层面:init_chat_model 用一个字符串屏蔽供应商差异;四种消息类型表达完整对话;max_tokens 管成本、with_retry 管容错;stream 出打字机、usage_metadata 出账单;图片走内容块列表;选型上便宜模型干杂活、强模型做决策。这些是后面所有篇章的地基。

三个练习巩固一下:

  1. 把上一篇 Agent 的模型换成 deepseek:deepseek-v4-pro,验证只改一个字符串还能跑
  2. 给流式打字机加统计:结束后打印本次 usage_metadata 和估算成本
  3. 用 base64 传一张 UI 截图,让模型列出可访问性问题

对于刚开始写 Agent 的读者,建议先把 invoke、stream、usage_metadata 三件事用同一家供应商跑熟,再横向切两家对比效果与成本,形成手感。下一篇我们讲 Prompt 模板与结构化输出——让模型的输入可复用、输出可解析,Agent 的"血管"就从这里开始生长。

相关推荐

Hermes Agent 详解:自进化学习型 AI 代理
Agent

Hermes Agent 详解:自进化学习型 AI 代理

Hermes Agent 详解:自进化学习型 AI 代理Hermes Agent 是由 Nous Research 于 2026 年 2 月开源的自主 AI 代理框架,其核心定位是"与你共同成长的智能体"。与传统的 IDE 编程助手或单一 API 聊天机器人不同,Hermes 运行在你的服务器上,能够记住所学内容,运行时间越长能力越强。它采用 MIT 开源许可,支持 Linux、macOS 和 W

393
Hermes Agent:与你一起成长的开源 AI 代理
Agent

Hermes Agent:与你一起成长的开源 AI 代理

Hermes Agent:与你一起成长的开源 AI 代理大多数 AI 助手是"健忘的租客"——换个窗口就失忆、换个平台就重学、干过的活不留下任何沉淀。Nous Research 出品的 Hermes Agent 反其道而行,口号是"The Agent That Grows With You"(与你一起成长的代理):它是一个开源(MIT)、自托管的 AI 代理,具备持久记忆(学习你的项目、自动生成技

322
speech-to-speech 详解:HuggingFace 的开源语音 Agent 流水线
AI

speech-to-speech 详解:HuggingFace 的开源语音 Agent 流水线

speech-to-speech 详解:HuggingFace 的开源语音 Agent 流水线语音 Agent 的"全开源"路线一直有个尴尬:框架开源了,模型却离不开云端 API。huggingface/speech-to-speech 把这条路走通了:VAD → STT → LLM → TTS 四段低延迟流水线,每个组件都可换成开源模型本地跑——Apple Silicon 一台 Mac 就能撑起

264