一、文章摘要
本文解决什么问题?
很多同学在入门 LangChain 时都会遇到这些问题:
- 模型初始化搞不定,官方文档看不懂
- Agent 和 Tool 的关系理不清
- 消息结构混乱,不知道怎么传参
- 提示词写得烂,AI 输出不理想
- 动态模型选择不会用,成本控制不了
为什么值得读?
这不是一篇翻译官方式的教程,而是我在真实项目中踩过坑、改过 Bug、熬过夜后总结出来的实战经验。我会用最通俗的语言,带你从零开始搭建一个完整的 LangChain Agent。
你能获得什么?
看完这篇文章,你将能够:
- 独立搭建一个 LangChain Agent
- 理解 Agent、Tool、Message 的核心概念
- 掌握生产级的代码写法(不是 Demo 级别)
- 避开我踩过的那些坑
二、开头
最近做 AI 项目时, 我遇到一个离谱问题:
明明代码跑起来了, 但 AI 回答像在"一本正经胡说八道"。
一开始我以为是模型太菜, 后来发现:
锅居然在工具定义上。
说实话,刚开始学 LangChain 时,我也和很多人一样:
看了官方文档 → 觉得懂了 → 写代码 → 报错 → 放弃 😅
但这次不一样。
我把所有踩过的坑、所有的调试过程、所有的最佳实践,全部整理成了这篇保姆级教程。
承诺:看完就能上手,复制就能运行。
三、问题背景
但实际开发中,我遇到了这些问题:
问题 1:模型初始化就卡住了
官方文档默认用的是 OpenAI,但我用的是通义千问。怎么配?base_url 写哪?model_provider 选什么?
翻了一下午文档,才搞明白 init_chat_model 的正确用法。
问题 2:Agent 和 Tool 的关系搞不清
什么是 Agent?什么是 Tool?它们怎么配合?
官方文档讲得太抽象,我看了三遍都没理解。
问题 3:消息结构一团糟
HumanMessage、AIMessage、SystemMessage、ToolMessage... 这么多类型,什么时候用哪个?怎么传参?
问题 4:工具定义报 TypeError
这是最离谱的一个坑:
@tool
def get_weather(location: str) -> str:
return f"今天{location}的天气是晴朗"
看起来没问题对吧?
结果运行时报错:
TypeError: got an unexpected keyword argument 'resource_url'
我排查了两小时,最后发现是 args_schema 参数的已知 Bug...
问题 5:提示词写得烂
一开始我是这样写的 System Prompt:
你是一个智能助手
结果 AI 回答得像机器人,完全没有灵魂。
后来我才明白,提示词工程是一门艺术。
四、正文内容
第一章:环境准备与模型初始化
1.1 安装依赖
首先,我们需要安装 LangChain 和阿里模型的依赖:
# 安装 LangChain 核心库
uv add langchain
# 安装阿里通义千问的依赖
uv add dashscope
💡 小贴士:这里用的是
uv(Python 包管理器),比 pip 快 10 倍以上。如果你还在用 pip,建议换一下。
1.2 三种模型初始化方式
LangChain 提供了三种方式来初始化模型,我分别介绍一下:
方式一:使用 ChatTongyi(推荐新手)
from dotenv import load_dotenv
from langchain_community.chat_models.tongyi import ChatTongyi
load_dotenv()
# 直接初始化
model = ChatTongyi(model="qwen-turbo")
# 调用模型
response = model.invoke("你好")
print(response.content)
优点:简单直接,适合快速上手
缺点:灵活性不够,无法自定义参数
方式二:使用 langchain-qwq 包(官方推荐)
from dotenv import load_dotenv
from langchain_qwq import ChatQwen
load_dotenv()
model = ChatQwen(
model="qwen-turbo",
max_tokens=3000,
)
response = model.invoke("你好")
print(response.content)
# 流式输出
streamR = model.stream("给我写一个爱情诗")
for chunk in streamR:
print(chunk.content, end="", flush=True)
优点:功能更强大,支持更多配置
缺点:需要额外安装包
方式三:使用 init_chat_model(通用方式,⭐ 推荐)
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
load_dotenv()
# 通过 OpenAI 兼容模式调用阿里百炼
model = init_chat_model(
model="qwen-turbo",
model_provider="openai", # 关键:指定为 openai 兼容模式
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
response = model.invoke("你好")
# 流式输出
for chunk in model.stream("给我写一个爱情诗"):
print(chunk.content, end="", flush=True)
print(response.content)
优点:
- ✅ 统一的 API,支持多种模型
- ✅ 可以灵活切换模型提供商
- ✅ 生产环境推荐使用
关键点:
model_provider="openai"表示使用 OpenAI 兼容接口base_url必须指向阿里的兼容模式端点- 国内用户必须配置国内端点,否则会超时
1.3 使用模型实例创建 Agent(更精细控制)
有时候我们需要对模型参数进行更精细的控制,比如设置 temperature、timeout 等:
from langchain.agents import create_agent
from langchain_community.chat_models.tongyi import ChatTongyi
# 初始化模型实例,设置详细参数
model = ChatTongyi(
model="qwen-turbo",
temperature=0.1, # 低温度,更确定性的输出
max_tokens=1000, # 限制最大输出长度
timeout=30, # 请求超时时间
max_retries=3 # 失败重试次数
)
# 使用模型实例创建 Agent
agent = create_agent(model=model, tools=[])
这种方式的好处是:在创建 Agent 之前就能完全控制模型的行为。
1.4 常用 kwargs 参数速查表
在实际项目中,我们经常需要调整模型参数。这里整理了一份最常用的参数表:
| 参数 | 类型 | 常见值 | 作用 | 使用建议 |
|---|---|---|---|---|
temperature | float | 0~2 | 控制回答随机性 | 写代码 0~0.3,日常 0.7,创意 1+ |
max_tokens | int | 500~4000 | 限制最大输出长度 | 回复被截断时调大 |
top_p | float | 0~1 | 控制采样范围 | 一般默认 1 即可 |
frequency_penalty | float | -2~2 | 降低重复概率 | 文案重复时调 0.3~0.8 |
presence_penalty | float | -2~2 | 鼓励生成新内容 | 创意写作可适当增加 |
stop | list[str] | ["结束"] | 遇到指定字符停止 | Agent 中常用 |
seed | int | 42 | 固定随机结果 | 调试时很有用 |
timeout | int | 30~300 | 请求超时时间 | 本地模型建议 120+ |
max_retries | int | 2~6 | 失败重试次数 | 网络差建议 6 |
⚠️ 踩坑提醒:
temperature不是越大越好!写代码时建议设为0~0.3,否则模型会"放飞自我"。
第二章:Agent 入门 - 让 AI 变成"智能体"
2.1 什么是 Agent?
说白了,Agent 就是一个能够自主决策的 AI 助手。
普通 AI 模型:
用户提问 → 模型回答
Agent:
用户提问 → 理解意图 → 决策(是否需要调用工具)→ 执行工具 → 整合结果 → 回答
区别在于:Agent 可以主动调用外部工具,而不只是被动回答。
2.2 创建第一个 Agent
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
# 初始化模型(必须手动指定,create_agent 默认用 OpenAI)
model_qwen_turbo = init_chat_model(
model="qwen-turbo",
model_provider="openai",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 创建 Agent
agent = create_agent(model=model_qwen_turbo)
# 调用 Agent
response = agent.invoke({
"messages": [{"role": "user", "content": "你是谁?"}]
})
# 返回结构
print(response) # 完整的响应对象
print(response["messages"][-1].content) # 只拿最后一句话
返回结构说明:
{
'messages': [
HumanMessage(content='你是谁?', ...),
AIMessage(content='我是通义千问...', ...)
]
}
2.3 流式输出(打字机效果)
在生产环境中,流式输出是标配。用户不想等 10 秒才看到结果。
方式一:使用 stream_mode="messages"
streamR = agent.stream(
{"messages": [{"role": "user", "content": "给我写一个爱情诗"}]},
stream_mode="messages", # 指定流式模式
version="v2", # 使用 v2 版本格式
)
for chunk in streamR:
if chunk["type"] == "messages":
token, metadata = chunk["data"]
if token.content_blocks:
for block in token.content_blocks:
if block.get("type") == "text":
print(block["text"], end="", flush=True)
方式二:更简洁的方式(⭐ 推荐)
for token, metadata in agent.stream(
{"messages": [{"role": "user", "content": "月亮的首都是哪里?"}]},
stream_mode="messages"
):
if token.content:
print(token.content, end="", flush=True)
方式三:使用 updates 模式(官方示例)
response = agent.stream(
{
"messages": [HumanMessage(content="北京的天气怎么样?")]
},
stream_mode="updates"
)
for chunk in response:
if isinstance(chunk, dict):
for node_name, state_update in chunk.items():
if "messages" in state_update:
last_msg = state_update["messages"][-1]
if hasattr(last_msg, 'content') and last_msg.content:
print(last_msg.content, end="", flush=True)
💡 三种方式对比:
messages模式:适合需要精细化控制的场景updates模式:官方推荐,适合大多数场景- 选择哪种取决于你的需求
第三章:工具(Tools)- 让 Agent 拥有"超能力"
3.1 什么是 Tool?
Tool 就是 Agent 的外部能力扩展。
比如:
- 查天气 → 调用天气 API
- 搜索信息 → 调用搜索引擎
- 查数据库 → 执行 SQL 查询
没有 Tool 的 Agent,就像没有手的超人——只能动嘴,不能干活。
3.2 定义工具的三种方式
方式一:基础版(⚠️ 有坑)
from langchain.tools import tool
@tool("获取天气的工具", description="获取指定位置的天气")
def get_current_weather(location: str) -> str:
return f"今天{location}的天气是晴朗,温度是25摄氏度"
# 构建 Agent
agent = create_agent(
model=model_qwen_turbo,
system_prompt="你是一个智能助手,擅长回答用户问题",
tools=[get_current_weather],
)
# 测试
response = agent.stream(
{
"messages": [HumanMessage(content="北京的天气怎么样?")]
},
stream_mode="updates"
)
问题:这种写法虽然简单,但参数描述不够清晰,AI 可能会调用错误。
方式二:使用 Annotated + Field(⭐⭐⭐ 强烈推荐)
from typing import Annotated
from pydantic import Field
from langchain.tools import tool
@tool(
name_or_callable="get_weather_tool", # 自定义工具标识
description="获取指定城市的实时天气状况,包括温度、湿度、风力等信息。当用户询问天气相关问题时调用此工具。",
return_direct=False, # 天气信息可能需要进一步处理
response_format="content", # 返回纯文本即可
parse_docstring=True, # 启用文档解析
)
def get_current_weather(
location: Annotated[str, Field(description="需要查询的城市名称,例如:北京、上海、广州")]
) -> str:
"""
获取指定位置的实时天气数据
根据城市名称查询当前天气状况,返回温度、天气现象、湿度等详细信息。
Args:
location: 目标城市名称,支持中文城市名
Returns:
格式化的天气信息字符串
Example:
>>> get_current_weather("北京")
'北京今天: 晴朗, 温度25°C, 湿度45%, 微风'
"""
# TODO: 接入真实天气API(如和风天气、OpenWeatherMap)
return f"今天{location}的天气是晴朗,温度是25摄氏度"
优点:
- ✅ 参数描述清晰,AI 更容易正确调用
- ✅ 自动校验参数,减少报错
- ✅ 更适合复杂业务场景
- ✅ 可维护性更强
方式三:使用 Pydantic Model 定义复杂参数(官方推荐)
当工具需要多个参数,且参数之间有依赖关系时,可以使用 Pydantic Model:
from pydantic import BaseModel, Field
from typing import Literal
from langchain.tools import tool
class WeatherInput(BaseModel):
"""天气查询输入参数"""
location: str = Field(description="城市名称或坐标")
units: Literal["celsius", "fahrenheit"] = Field(
default="celsius",
description="温度单位偏好"
)
include_forecast: bool = Field(
default=False,
description="是否包含5天预报"
)
@tool(args_schema=WeatherInput)
def get_weather(location: str, units: str = "celsius", include_forecast: bool = False) -> str:
"""获取当前天气和可选预报。"""
temp = 22 if units == "celsius" else 72
result = f"当前{location}的天气: {temp}度"
if include_forecast:
result += "\n未来5天: 晴朗"
return result
# 创建带工具的 Agent
agent = create_agent(model_qwen_turbo, tools=[get_weather])
这种方式特别适合:
- ✅ 参数较多的工具
- ✅ 需要参数验证的场景
- ✅ 参数有默认值或可选值的情况
方式四:使用预定义工具(Tavily 搜索)
LangChain 提供了很多现成的工具,比如搜索工具:
# 安装依赖
# uv add langchain-tavily
from langchain_tavily import TavilySearch
# 初始化搜索工具
tavily_search = TavilySearch(
max_results=5,
topic="general",
)
# 测试搜索
test_result = tavily_search.invoke({"query": "长沙未来7天的天气怎么样?"})
print(test_result)
注意:
- 需要在 Tavily 官网 获取 API Key
- 环境变量必须配置
TAVILY_API_KEY - 适合需要实时信息的场景
3.3 @tool 装饰器参数速查表
| 参数 | 类型 | 默认值 | 推荐场景 | 重要程度 |
|---|---|---|---|---|
name_or_callable | str | Callable | 函数名 | 自定义工具标识 | ⭐⭐⭐⭐⭐ |
description | str | docstring | 优化 LLM 理解 | ⭐⭐⭐⭐⭐ |
return_direct | bool | False | 终止性工具 | ⭐⭐⭐⭐ |
args_schema | BaseModel | None | 复杂参数验证 | ⭐⭐⭐⭐ |
infer_schema | bool | True | 精确控制 schema | ⭐⭐⭐ |
response_format | Literal | "content" | 返回复杂数据 | ⭐⭐⭐ |
parse_docstring | bool | False | 自动提取文档 | ⭐⭐⭐ |
⚠️ 踩坑提醒:不要使用
config和runtime作为参数名!这两个是保留字段。
3.4 工具定义最佳实践(避坑指南)
根据官方文档和实战经验,总结以下最佳实践:
from langchain.tools import tool
@tool(parse_docstring=True) # 启用 docstring 解析
def search_orders(
user_id: str,
status: str,
limit: int = 10
) -> str:
"""搜索用户订单。
当用户询问订单历史或想查看订单状态时使用此工具。
始终按提供的状态进行过滤。
Args:
user_id: 用户的唯一标识符
status: 订单状态:'pending'、'shipped' 或 'delivered'
limit: 返回的最大结果数量
"""
# 实现代码...
pass
关键点:
- 启用
parse_docstring=True:自动从 docstring 提取参数描述 - 写清楚工具用途:第一段说明什么时候调用这个工具
- 详细的 Args 说明:每个参数都要有清晰的描述
- 使用类型注解:帮助 LangChain 自动生成参数 schema
第四章:消息(Messages)- Agent 的"记忆系统"
4.1 消息类型一览
在 LangChain 中,消息是最基本的交互单位。常见的消息类型有:
| 类型 | Role | 用途 | 示例 |
|---|---|---|---|
SystemMessage | system | 设定角色和背景 | "你是一个诗人" |
HumanMessage | user | 用户输入 | "写一首诗" |
AIMessage | assistant | AI 输出 | "好的,这是一首诗..." |
ToolMessage | tool | 工具返回结果 | "今天北京晴天" |
4.2 消息的基本结构
每条消息都包含以下内容:
{
"role": "user", # 角色
"content": "你好", # 内容(文本/图片/视频)
"name": "张三", # 发送者名称(可选)
"id": "123456", # 消息 ID(可选)
"metadata": {}, # 元数据(时间戳等)
}
4.3 实战示例:构建多轮对话
from langchain.messages import SystemMessage, HumanMessage, AIMessage
from langchain.agents import create_agent
# 初始化模型
model_qwen_turbo = init_chat_model(
model="qwen-turbo",
model_provider="openai",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 构建消息列表
system_message = SystemMessage(content="你是一个诗人叫小礼拜,你会模仿诗仙李白风格的诗")
human_message_1 = HumanMessage(content="你叫什么名字?")
ai_message_1 = AIMessage(content="我是张三")
human_message_2 = HumanMessage(content="写一首表白的诗")
# 创建 Agent 并调用
agent = create_agent(model=model_qwen_turbo)
response = agent.invoke({
"messages": [
system_message,
human_message_1,
ai_message_1,
human_message_2
]
})
# 打印结果(更友好的方式)
for message in response["messages"]:
message.pretty_print()
# 获取最终回复
print(response["messages"][-1].content)
输出效果:
================================ System Message =================================
你是一个诗人叫小礼拜,你会模仿诗仙李白风格的诗
================================ Human Message =================================
你叫什么名字?
================================== Ai Message ===================================
我是张三
================================== Ai Message ===================================
张三啊,张三,你来得真好!
我这诗仙小礼拜,最爱与人对酒当歌。
...
4.4 使用字典格式创建消息(更简洁)
除了使用消息对象,还可以使用字典格式,更简洁直观:
# 使用字典格式
messages = [
{"role": "system", "content": "你是一个 helpful 的助手"},
{"role": "user", "content": "你好,请介绍一下自己"},
{"role": "assistant", "content": "你好!我是一个 AI 助手..."},
{"role": "user", "content": "你能做什么?"}
]
response = agent.invoke({"messages": messages})
两种方式等价,选择你喜欢的即可。
4.5 高级用法:多模态支持
现代大模型(如 GPT-4o、Qwen-VL)支持图片输入:
human_message = HumanMessage(content=[
{'type': 'text', 'text': '识别图片中的内容'},
{'type': 'image', 'url': 'https://example.com/image.jpg'}
])
response = agent.invoke({
"messages": [
SystemMessage(content="你是一个图像识别专家"),
human_message
]
})
适用场景:
- OCR 文字识别
- 图像内容分析
- 身份证/护照识别
- 医疗影像诊断
第五章:提示词工程(Prompt Engineering)
5.1 什么是提示词工程?
说白了,就是通过优化 SystemPrompt,让 AI 输出更符合预期的结果。
好的提示词 = 身份 + 说明 + 示例 + 限制 + 格式
5.2 提示词的核心要素
system_prompt = """
# 身份
- 你是一个科幻作家,根据用户的要求创建一个太空之都。
# 说明
请详细描述这个城市的地理位置、建筑风格、科技特点、文化特色等。
# 示例
user:月球的首都是什么?
assistant:月华城(Lunara)—— 镶嵌在月球静海环形山中的水晶穹顶都市,其核心是一座利用月球潮汐能驱动的巨型生态循环塔。
user:火星的首都是什么?
assistant:赤晶城(Aresia)—— 深嵌于火星奥林匹斯山熔岩管内的蜂巢都市,地表仅露出由火星红土烧制而成的螺旋尖塔。
# 限制
- 不要超过 200 字
- 必须包含城市名称的中英文
- 要有科幻感
# 格式
请按照以下格式输出:
**城市名(英文名)** —— 详细描述
"""
5.3 动态提示词(Dynamic Prompt)
有时候我们需要根据运行时上下文动态生成 System Prompt,比如根据用户角色调整回答风格:
from typing import TypedDict
from langchain.agents import create_agent
from langchain.agents.middleware import dynamic_prompt, ModelRequest
class Context(TypedDict):
user_role: str
@dynamic_prompt
def user_role_prompt(request: ModelRequest) -> str:
"""根据用户角色生成系统提示词"""
user_role = request.runtime.context.get("user_role", "user")
base_prompt = "你是一个 helpful 的助手。"
if user_role == "expert":
return f"{base_prompt} 提供详细的技术性回答,使用专业术语。"
elif user_role == "beginner":
return f"{base_prompt} 用简单易懂的方式解释概念,避免使用专业术语。"
return base_prompt
# 创建 Agent,传入动态提示词中间件
agent = create_agent(
model="gpt-5.4",
tools=[web_search],
middleware=[user_role_prompt],
context_schema=Context
)
# 调用时传入上下文
result = agent.invoke(
{"messages": [{"role": "user", "content": "解释一下机器学习"}]},
context={"user_role": "expert"}
)
适用场景:
- 根据用户角色调整回答风格
- 根据环境(开发/生产)调整安全策略
- 根据用户偏好调整语言风格
5.4 结构化输出(Pydantic 模型)
有时候我们需要 AI 返回结构化数据,而不是纯文本:
from pydantic import BaseModel
class CapitalInfo(BaseModel):
name: str # 城市名称
location: str # 城市位置
vibe: str # 城市氛围
economy: str # 经济情况
agent = create_agent(
model=model_qwen_turbo,
system_prompt=SystemMessage(content="你是一个专业的导游"),
response_format=CapitalInfo # 设置结构化输出格式
)
response = agent.stream(
{
"messages": [HumanMessage(content="帮我介绍一下长沙")]
},
stream_mode="updates"
)
for chunk in response:
if isinstance(chunk, dict):
for node_name, state_update in chunk.items():
if "messages" in state_update:
last_msg = state_update["messages"][-1]
if hasattr(last_msg, 'content') and last_msg.content:
print(last_msg.content, end="", flush=True)
输出效果:
Returning structured response:
name='长沙'
location='长沙位于中国湖南省东部,湘江之滨,是湖南省的政治、经济、文化中心。'
vibe='长沙是一座充满活力和魅力的城市,既有悠久的历史文化,又有现代化的城市风貌,是一个宜居宜业的地方。'
economy='长沙是湖南省的省会,经济实力在中部地区处于领先地位,以制造业、电子信息、文化创意等产业为主。'
优点:
- ✅ 返回的数据可以直接用于程序逻辑
- ✅ 字段含义明确,便于后续处理
- ✅ 减少解析错误
第六章:动态模型选择 - 成本控制的艺术
6.1 为什么需要动态模型选择?
在实际项目中,我们会遇到这种情况:
- 简单问题(如"你好"):用便宜快速的模型(如 qwen-turbo)
- 复杂问题(如"帮我写一个排序算法"):用强大的模型(如 qwen-plus)
如果所有请求都用贵模型,成本会爆炸 💰
如果都用便宜模型,质量会下降 😅
所以,我们需要根据任务复杂度,自动选择合适的模型。
6.2 实现动态模型路由(基于消息数量)
官方推荐的方式是使用 @wrap_model_call 装饰器:
from collections.abc import Callable
from langchain.agents.middleware import ModelRequest, ModelResponse, wrap_model_call
from langchain.chat_models import init_chat_model
# 初始化不同级别的模型
simple_model = init_chat_model("qwen-turbo") # 便宜快速
standard_model = init_chat_model("qwen-plus") # 标准模型
advanced_model = init_chat_model("qwen-max") # 强大但贵
@wrap_model_call
def dynamic_model(
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
"""根据对话长度选择模型"""
message_count = len(request.messages)
if message_count > 20:
# 长对话用高级模型(需要更强的上下文理解)
model = advanced_model
elif message_count > 10:
# 中等对话用标准模型
model = standard_model
else:
# 短对话用简单模型(省钱!)
model = simple_model
# 使用选择的模型处理请求
return handler(request.override(model=model))
# 创建 Agent,传入动态模型中间件
agent = create_agent(
model=simple_model, # 默认模型
tools=[get_weather],
middleware=[dynamic_model]
)
6.3 基于对话复杂度的智能路由
除了消息数量,还可以根据其他因素选择模型:
@wrap_model_call
def smart_model_selection(request: ModelRequest, handler) -> ModelResponse:
"""智能模型选择策略"""
messages = request.messages
last_message = messages[-1]["content"] if messages else ""
# 根据问题复杂度选择模型
complex_keywords = ["算法", "代码", "编程", "优化", "架构"]
is_complex = any(keyword in last_message for keyword in complex_keywords)
if is_complex:
model = advanced_model
logger.info("检测到复杂问题,使用高级模型")
elif len(messages) > 15:
model = standard_model
logger.info("对话较长,使用标准模型")
else:
model = simple_model
logger.info("使用简单模型")
return handler(request.override(model=model))
agent = create_agent(
model=simple_model,
tools=tools,
middleware=[smart_model_selection]
)
6.4 成本监控与日志
建议加上成本监控,实时了解模型调用情况:
from loguru import logger
import sys
# 配置日志
logger.remove()
logger.add(sys.stdout, level="INFO")
@wrap_model_call
def model_with_cost_tracking(request: ModelRequest, handler) -> ModelResponse:
"""带成本追踪的模型选择"""
message_count = len(request.messages)
if message_count > 10:
model = advanced_model
model_name = "qwen-max (高级)"
estimated_cost = 0.02 * message_count
else:
model = simple_model
model_name = "qwen-turbo (经济)"
estimated_cost = 0.002 * message_count
logger.info(f"选择模型: {model_name}, 消息数: {message_count}, 预估成本: ${estimated_cost:.4f}")
return handler(request.override(model=model))
第七章:常见问题修复
7.1 ❌ 流式输出解析错误
问题描述:
for chunk in agent.stream(...):
print(chunk) # 输出乱七八糟的字典
原因:
不同 stream_mode 的返回格式不同,需要正确解析。
解决方案:
参考第二章的 2.3 节,选择正确的解析方式。
7.2 ❌ 模型无法识别
问题描述:
agent = create_agent(model="qwen-turbo") # 直接写字符串
报错:
ValueError: Unknown model: qwen-turbo
原因:
create_agent 默认只识别 OpenAI 的模型名称。对于其他模型,需要先初始化再传入。
解决方案:
# ❌ 错误写法
agent = create_agent(model="qwen-turbo")
# ✅ 正确写法
model = init_chat_model(
model="qwen-turbo",
model_provider="openai",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
agent = create_agent(model=model)
7.3 ❌ 流式输出无反应
问题描述:
for chunk in agent.stream(...):
print(chunk) # 什么都不输出
原因:
不同 stream_mode 的返回格式不同,需要正确解析。
解决方案:
参考第二章的 2.3 节,选择正确的解析方式。
7.4 ❌ 工具调用参数错误
问题描述:
AI 调用工具时传错参数,或者根本不调用工具。
原因:
工具描述不够清晰,AI 不知道什么场景下该调用。
解决方案:
- 使用
Annotated+Field清晰定义参数 - 在
description中明确说明调用场景 - 启用
parse_docstring=True自动提取文档
五、总结与最佳实践
📋 核心要点回顾
1️⃣ 模型初始化
- ✅ 推荐使用
init_chat_model(统一 API) - ✅ 国内用户必须配置
base_url - ✅ 合理设置
temperature、max_tokens等参数 - ✅ 需要精细控制时,先初始化模型实例再传入
2️⃣ Agent 创建
- ✅ 使用
create_agent创建智能体 - ✅ 支持同步(invoke)和异步(stream)调用
- ✅ 流式输出提升用户体验
3️⃣ 工具定义
- ✅ 使用
@tool装饰器定义工具 - ✅ 推荐使用
Annotated+Field定义参数(避免 TypeError) - ✅ 复杂参数使用 Pydantic Model(
args_schema) - ✅ 编写清晰的
description帮助 AI 理解 - ✅ 启用
parse_docstring=True自动提取文档
4️⃣ 消息管理
- ✅ 熟练使用
SystemMessage、HumanMessage、AIMessage - ✅ 支持字典格式创建消息(更简洁)
- ✅ 支持多模态输入(文本 + 图片)
- ✅ 合理使用
name和id元数据
5️⃣ 提示词工程
- ✅ 遵循「身份 + 说明 + 示例 + 限制 + 格式」原则
- ✅ 使用
@dynamic_prompt实现动态提示词 - ✅ 使用 Pydantic 模型实现结构化输出
- ✅ 提供 Few-shot 示例提升质量
6️⃣ 动态模型选择
- ✅ 使用
@wrap_model_call实现模型路由 - ✅ 根据消息数量、问题复杂度选择模型
- ✅ 平衡成本和质量
- ✅ 添加成本监控和日志
📌 写文不易,Bug 更不易。
如果这篇文章对你有帮助,可以搜一搜:空门技术栈
这里分享:
- ✅ Java / Spring AI / 企业级项目实战
- ✅ Docker / RAG知识库 / 微服务踩坑
- ✅ Python、前端、AI应用落地
- ✅ 偶尔分享一些「头发保卫战」经验 😆
一个热爱技术、持续填坑的开发者, 陪你一起少踩坑,少加班,多写优雅代码。
📖 推荐阅读
💬 想要源码?
别光收藏不点赞,评论区喊一声「源码」,我私发你!