Qwen 已返回 `tool_calls`,为什么你的行情回答仍可能不可信?

TickDB Research ·

摘要:
Qwen 返回 tool_calls,不代表行情查询已经成功。本文从掘金开发者常见排错场景出发,拆解“触发工具、应用执行、结果校验、工具回填、最终回答”的最小可信链路,并给出未触发工具、查询失败或空结果、回填错误三个必须阻断价格回答的失败分支。TickDB 仅作为应用端只读行情数据路径示例出现。

你在日志里看到了这样的返回:

{
  "tool_calls": [
    {
      "function": {
        "name": "get_market_snapshot",
        "arguments": "{\"symbol\":\"AAPL.US\"}"
      }
    }
  ]
}

这只能说明一件事:模型提出了一个工具调用请求。

它不等于应用程序已经拿到行情,不等于返回内容有效,也不等于最终回答确实基于这次查询结果生成。对于“当前价格是多少”这类问题,只要工具链路没有完整闭环,模型就不应该继续输出确定价格。

tool_calls 不是行情结果

百炼官方文档给出的 Function Calling 流程,本质上由应用程序完成闭环:

步骤发生了什么能否证明行情可信
1应用把用户问题和工具定义发给 Qwen不能
2Qwen 返回工具名称与参数,即 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)

片段里的关键不是函数名,而是三条规则:

  1. 没有真实工具结果,就没有当前价格结论。
  2. 工具失败或结果为空,应该明确告诉用户“当前无法验证”,而不是让模型补写一个价格。
  3. 工具结果必须绑定正确的 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 行情查询 排错

阿里云百炼 Function Calling 官方文档。

相关文章

Kimi 金融数据接入避坑:用 `tool_calls` 查询实时行情,而不是让模型猜价格

> Kimi 可以判断用户何时需要查询行情,但当前价格不能依靠模型猜测。本文基于 Moonshot 官方 流程,以 TickDB REST 为示例,拆解模型协议、本地工具函数与行情 API 的三层边界,并提供字段速查、最小 Python 实现和发布前验证清单。如果你让一个大模型回答“ 现在多少钱”,最危险的情况不是它报错,而是它给出一个语气非常确定、却没有实时数据来源的答案。 对于使用 Kim

TickDB Research ·

Claude Code / Cursor 接行情数据:为什么 MCP 工具的 description 不是注释

摘要 给 Claude Code 或 Cursor 接了 MCP 行情工具,AI 却用历史 K 线接口去查当前价格,或者用实时快照接口去做历史回测——问题大概率不在模型能力,而在 MCP 工具的 description 写得太含糊。本文基于 MCP 官方规范、Claude Code 和 Cursor 的官方文档,以及一篇 2026 年 arXiv 论文,解释 MCP 工具描述为什么是 AI 选择工

TickDB Research ·

智谱GLM-4 接金融数据:工具描述多写三个字,模型少犯一类错

> 你把智谱开放平台的 Function Calling 示例从“查天气”改成“查行情”。在多次测试中,GLM-4-Flash 不仅调对了工具,还能把用户说的“茅台”映射成 ——这是中文模型在 A 股数据上的天然优势。但当三个行情工具同时注册时,同一个 query 在第三轮对话里突然调错了函数。优势区和暗坑区,只隔着一套工具描述的写法。 一、GLM-4 的中文红利,在金融场景下是双刃剑 GLM-4

TickDB Research ·

AI Agent 框架接金融行情数据前,先检查这 7 个工程风险

> 本文不是框架排名,而是一份金融行情数据接入前的工程风险清单。每个风险点都附了检查方法和修正示例。 --- 一、不同框架,同一个坑 假设你用三个不同的 Agent 框架跑同一个任务——“每 30 分钟查一次价格,超过阈值时汇总分析”。 其中一个 Agent 把 ticker 快照的 (24 小时成交量)当成了单根 K 线的成交量,量级差了几千倍。另一个在 API 限流后陷入重试死循环,两分钟烧掉

TickDB Research ·

DeepSeek Tool Calls 接实时行情:如何避免模型把“猜测”写成当前价格

> 仅靠 Prompt,DeepSeek 无法证明当前价格来自实时接口。本文以 TickDB REST 行情快照为例,展示如何通过 Tool Calls 请求查询、由程序执行 REST 并回填成功结果;若工具失败,则由程序直接结束价格回答流程,以降低模型补写未经验证行情的风险。 当用户问“现在 AAPL.US 或 BTCUSDT 的价格是多少”,仅靠对话上下文,模型无法证明答案来自当前行情接口。

TickDB Research ·