Qwen 流式输出与工具调用:Agent 框架接入实战
Qwen 流式输出与工具调用:Agent 框架接入实战
流式输出让 AI 回复不再「等半天再吐一段」,而是像打字一样逐字显示;工具调用让模型不再只能「说」,还能真正调用函数、执行代码、查询数据。这两个能力是 Agent 应用的基础。本文讲清 Qwen 的流式输出怎么实现、工具调用的格式约定、以及怎么把 Qwen 接进 LangChain、LlamaIndex 等 Agent 框架。
30 秒版本
流式输出:设置 stream=true,逐块读取 choices[0].delta.content。工具调用:在请求里定义 tools,模型返回 tool_calls 字段,你的代码执行对应函数再回传结果。两者都通过 OpenAI 兼容端点实现,本地和云端一样。
流式输出怎么实现
流式输出通过 SSE(Server-Sent Events)实现。设置 stream=true 后,服务端会逐块返回增量内容,客户端逐块渲染。
流式与非流式的区别
非流式:客户端等全部生成完再收到完整响应。流式:客户端边生成边收到增量,体验更自然。代价是连接要保持更长时间,中间不能断。
Python 流式调用
stream = client.chat.completions.create(
model="qwen3.8-flash",
messages=[{"role": "user", "content": "写一个快速排序的 Python 实现"}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="", flush=True)
Node.js 流式调用
const stream = await client.chat.completions.create({
model: "qwen3.8-flash",
messages: [{ role: "user", content: "写一个快速排序的 Python 实现" }],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0].delta;
if (delta.content) process.stdout.write(delta.content);
}
流式调用的常见坑
- 流式连接要保持,中间断了需要重试或降级为非流式
- 部分代理/CDN 会缓冲流式响应,导致「流式」变成「一次性返回」
- 流式模式下 finish_reason 可能出现
length,意味着被截断,不是完整回答
工具调用怎么实现
工具调用(function calling / tool calling)让模型能调用外部函数。流程是:
- 在请求里定义
tools,说明每个工具的名称、参数和用途 - 模型返回
tool_calls,告诉客户端要调用哪个工具、传什么参数 - 客户端执行对应函数,把结果回传
- 模型继续生成,基于工具结果给出回答
工具调用是 Agent 的核心
没有工具调用,模型只能「说」;有了工具调用,模型能「做」。Agent 框架就是围绕工具调用循环构建的。
定义工具
{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的天气",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名称" }
},
"required": ["city"]
}
}
}
]
}
处理工具调用
resp = client.chat.completions.create(
model="qwen3.8-flash",
messages=[{"role": "user", "content": "北京今天天气怎么样"}],
tools=tools,
)
msg = resp.choices[0].message
if msg.tool_calls:
# 模型要求调用工具
for tc in msg.tool_calls:
name = tc.function.name
args = json.loads(tc.function.arguments)
result = execute_tool(name, args) # 你的函数实现
# 把结果回传给模型
messages.append({"role": "tool", "content": str(result), "tool_call_id": tc.id})
# 继续让模型基于工具结果生成回答
resp2 = client.chat.completions.create(
model="qwen3.8-flash",
messages=messages,
)
print(resp2.choices[0].message.content)
else:
print(msg.content) # 模型直接回答了
工具调用的格式约定
不同模型对工具调用的格式约定可能不同。Qwen 的 OpenAI 兼容端点遵循标准 tools / tool_calls 格式。接入前先用几个样本验证模型返回的格式是否符合你的预期。
接入 Agent 框架
LangChain 和 LlamaIndex 都支持自定义 OpenAI 兼容端点,接入 Qwen 只需改 base_url。
LangChain
from langchain_community.llms import OpenAI
llm = OpenAI(
model="qwen3.8-flash",
api_key="YOUR_EXAIV_API_KEY",
base_url="https://exaiv.com/api/ai/v1",
streaming=True,
)
LlamaIndex
from llama_index.llms.openai import OpenAI
llm = OpenAI(
model="qwen3.8-flash",
api_key="YOUR_EXAIV_API_KEY",
base_url="https://exaiv.com/api/ai/v1",
)
Agent 框架的接入方式
LangChain 和 LlamaIndex 都通过「自定义 OpenAI 端点」接入 Qwen。改 base_url 和 api_key 即可,框架的其他能力(工具调用、记忆、检索)都自动支持。
工具调用循环的工程细节
工具调用的循环看起来简单,但工程上有几个细节需要注意:
1. 最大轮次限制。 没有限制的话,模型可能反复调用工具而不给出最终答案。设置 5-10 轮的上限是必要保护。
2. 工具执行失败的处理。 工具执行失败时,把错误信息回传给模型,而不是吞掉异常。模型能根据错误信息调整策略,或者告知用户。
3. 工具结果的大小控制。 如果工具返回的内容很大(比如整个数据库查询结果),需要在回传给模型之前做摘要或截断,避免上下文爆炸。
4. 并发工具调用。 如果模型一次返回多个 tool_calls,可以并行执行这些工具,提升效率。
5. 超时与降级。 工具调用循环如果某一步卡住(比如外部 API 无响应),需要有超时和降级策略,不要让整个 Agent 卡死。
Agent 框架已经处理了大部分细节
LangChain、LlamaIndex 等框架内部实现了最大轮次、工具错误处理、结果摘要等逻辑。自己写 Agent 时容易忽略这些细节,接入框架可以省很多事。
本地与云端的配合策略
本地跑流式,云端跑工具调用
本地 GGUF 适合跑流式对话(低延迟、隐私),云端 API 适合跑复杂工具调用(多工具、长循环、弹性扩容)。两者配合:本地做实时交互,云端做 Agent 编排。
| 场景 | 建议 | 原因 |
|---|---|---|
| 流式对话、实时交互 | 本地 GGUF | 低延迟、数据不出机 |
| 多工具调用、复杂循环 | 云端 qwen3.8-max | 模型质量高、支持长循环 |
| 对外 Agent 服务 | 云端 API | 弹性扩容、多人共享 |
常见问题
Q: 流式调用和工具调用能同时用吗?
能。在支持流式的端点上,工具调用也可以流式返回。但要注意:流式模式下 tool_calls 是增量返回的,需要拼接完整内容再执行。
Q: 工具调用失败怎么办? 工具执行失败时,把错误信息回传给模型,让模型知道工具调用失败,可以尝试其他方式或告知用户。不要把失败信息直接吞掉。
Q: 工具调用循环会无限吗? 不会,但需要设置最大轮次限制(如 5-10 轮),防止模型反复调用工具而不给出最终答案。
Q: 本地 GGUF 支持工具调用吗? 本地 GGUF 支持工具调用,但工具调用的格式约定可能和云端不完全一致。接入前用样本验证格式。
Q: 流式输出会卡住吗? 如果中间网络断了或代理缓冲了,流式输出可能「卡住」直到超时。设置合理的超时时间,并在超时后降级为非流式或重试。
相关阅读
- Qwen 本地编码工作流 — 本地与云端配合
- Qwen 成本优化实战 — 降低工具调用循环的成本
- Qwen API 排障 — 调用问题排查
- Qwen 模型架构 — 理解架构
- Qwen 系列怎么选 — 模型选型