Qwen 已返回 `tool_calls`,为什么你的行情回答仍可能不可信?
作者: TickDB Research · 发布: 2026/5/27 · 阅读: 3
标签: C 类, 掘金, AI 工具
摘要:
Qwen 返回 tool_calls,不代表行情查询已经成功。本文从掘金开发者常见排错场景出发,拆解“触发工具、应用执行、结果校验、工具回填、最终回答”的最小可信链路,并给出未触发工具、查询失败或空结果、回填错误三个必须阻断价格回答的失败分支。TickDB 仅作为应用端只读行情数据路径示例出现。
你在日志里看到了这样的返回:
{
"tool_calls": [
{
"function": {
"name": "get_market_snapshot",
"arguments": "{\"symbol\":\"AAPL.US\"}"
}
}
]
}
这只能说明一件事:模型提出了一个工具调用请求。
它不等于应用程序已经拿到行情,不等于返回内容有效,也不等于最终回答确实基于这次查询结果生成。对于“当前价格是多少”这类问题,只要工具链路没有完整闭环,模型就不应该继续输出确定价格。
tool_calls 不是行情结果
百炼官方文档给出的 Function Calling 流程,本质上由应用程序完成闭环:
| 步骤 | 发生了什么 | 能否证明行情可信 |
|---|---|---|
| 1 | 应用把用户问题和工具定义发给 Qwen | 不能 |
| 2 | Qwen 返回工具名称与参数,即 tool_calls | 不能 |
| 3 | 应用实际执行只读行情查询 | 仍需检查结果 |
| 4 | 应用把工具输出作为 role="tool" 消息回填 | 形成回答依据 |
| 5 | 应用再次请求 Qwen 生成自然语言答案 | 仅在前面成功时可回答 |
容易忽略的是第 3、4 步:真正访问行情数据的是你的应用,而不是 tool_calls 本身。
最小可信调用链路
对于必须依赖当前行情的数据问题,可以把链路收紧为以下五步:
| 环节 | 最小要求 | 失败时的动作 |
|---|---|---|
| 识别问题 | 判断请求是否必须读取当前行情 | 不能把静态知识当当前价格 |
| 触发工具 | 对已确认需要行情的分支要求工具调用 | 未触发则停止报价格 |
| 执行查询 | 应用调用只读行情适配器,例如接入 TickDB REST 查询快照 | 请求失败或空结果则停止 |
| 回填证据 | 将成功结果绑定原始 tool_call_id 回填给模型 | 未正确回填则停止 |
| 生成回答 | 仅基于工具返回组织自然语言 | 不扩展为交易判断 |
这里 TickDB 的位置很简单:它可以是应用端行情适配器实际查询的数据路径之一。Qwen 负责决定要调用什么工具并整理回答,应用负责执行和校验查询,两者不能混写成“模型自己知道行情”。
核心代码片段:重点在阻断条件
以下为教学用核心片段,不是完整可运行代码。Qwen 的 OpenAI 兼容调用形式依据百炼官方文档;
query_snapshot_via_readonly_adapter()为故意省略的应用端执行器,可在审核后接入并核验 TickDB REST 请求、鉴权、字段与异常处理。
import json
import os
from openai import OpenAI
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
tools = [{
"type": "function",
"function": {
"name": "get_market_snapshot",
"description": "读取指定标的的当前行情快照,只用于只读查询。",
"parameters": {
"type": "object",
"properties": {"symbol": {"type": "string"}},
"required": ["symbol"],
},
},
}]
messages = [{"role": "user", "content": "查询 AAPL.US 当前价格"}]
first = client.chat.completions.create(
model="qwen3.6-plus",
extra_body={"enable_thinking": False},
messages=messages,
tools=tools,
tool_choice="required", # 仅用于已确认必须查行情的分支
)
assistant_msg = first.choices[0].message
if not assistant_msg.tool_calls:
raise RuntimeError("未触发行情工具,禁止输出当前价格")
messages.append(assistant_msg)
call = assistant_msg.tool_calls[0]
args = json.loads(call.function.arguments)
# 应用端只读数据适配器:可接 TickDB REST;此处省略真实网络实现。
result = query_snapshot_via_readonly_adapter(args)
if not result.get("ok") or not result.get("quote"):
raise RuntimeError("查询失败或无可验证行情,禁止输出当前价格")
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})
final = client.chat.completions.create(
model="qwen3.6-plus",
extra_body={"enable_thinking": False},
messages=messages,
tools=tools,
) # 总结工具结果阶段,不继续强制工具调用
print(final.choices[0].message.content)
片段里的关键不是函数名,而是三条规则:
- 没有真实工具结果,就没有当前价格结论。
- 工具失败或结果为空,应该明确告诉用户“当前无法验证”,而不是让模型补写一个价格。
- 工具结果必须绑定正确的
tool_call_id回填,之后的回答才有证据来源。
至少三个常见失败分支
| 失败分支 | 你会看到什么 | 为什么不可信 | 正确处理 |
|---|---|---|---|
| 未触发工具 | 模型直接返回一段价格回答,tool_calls 为空 | 没有任何实时查询证据 | 对行情查询分支要求调用工具;仍无调用则停止输出价格 |
| 工具请求失败或返回空结果 | 超时、权限失败、适配器报错,或返回无可用行情 | 有调用意图,但没有可消费的数据 | 返回“行情暂不可验证”,记录错误,不进入价格总结 |
| 结果未被正确回填 | 应用拿到了数据,却没有添加 role="tool" 消息,或绑定错 tool_call_id | 最终模型上下文里没有对应证据 | 保留原始 assistant 工具调用消息,按调用 ID 回填结果后再请求总结 |
| 结果存在但回答越界 | 工具只返回快照,模型却继续输出买卖判断或价格预测 | 数据查询不等于决策依据 | 限制回答范围,只描述查询结果与可验证边界 |
一个更实用的验收清单
上线前,不妨对行情工具调用链逐项检查:
- 当前行情问题是否被路由到必须查询工具的分支。
tool_calls是否只被视为请求,而不是查询成功标志。- 应用端查询是否为只读操作,并处理失败、超时和空结果。
- 返回值是否作为工具消息正确回填到对应调用。
- 最终回答是否只描述得到的数据,不补写不存在的结果。
- 日志是否足以区分“模型没调工具”“工具没数据”“回填出错”三类问题。
边界说明
本文讨论的是工具调用链路的可信性设计,不提供交易建议,也不把一次行情快照扩展成趋势、收益或买卖判断。TickDB 在这里仅作为应用端可以接入的只读行情数据路径示例;实际接入时,仍应以当前接口文档和审核后的请求实现为准。
总结
看到 tool_calls,只能证明 Qwen 知道“该去查”。
只有应用成功执行查询、确认结果可用、正确回填工具输出,并让模型基于该结果回答,当前行情结论才拥有最基本的证据闭环。
对于行情类问答,最重要的工程规则不是“尽量回答”,而是:没有可验证工具结果时,明确停止报价格。
标签:
Qwen Function Calling tool_calls Python API 行情查询 排错
通过 TickDB API 获取实时行情数据
一个 API 接入外汇、加密货币、美股、港股、A股、贵金属和全球指数的实时行情。支持 WebSocket 低延迟推送,免费开始使用。
免费领取 API Key查看 API 文档