第二周:多模态聊天机器人的函数调用与图像
模型从头到尾没碰过任何东西,它只是生成了一段约定格式的文本。搞清楚这一点,工具设计的三条规矩就是自明的。
第一篇选好了模型。这一篇让它能干活 —— 而干活的唯一通道是函数调用。
先破一个误解
「函数调用」听起来像模型能执行代码。不是。
真实流程是:模型输出一段结构化文本说「我想调 search_flights,参数是这些」→ 你的代码去执行 → 把结果作为一条消息塞回对话里。
模型自始至终没碰过任何东西。
这个认识不是抠字眼,它直接推出三条设计规矩。
规矩一:安全边界全在你这边
模型说要删除,删不删是你的代码决定的。所以不可逆的动作必须有一道显式的门 —— 删除、支付、发布这类,白名单之外一律拦下并留痕。
拦下来之后有个细节容易错:不能静默跳过。「做了」「做失败了」「被拦下没做」是三个状态,混成两个就会出现假绿灯。
规矩二:工具描述是提示词的一部分,而且很值钱
把工具描述从一行摘要扩成写清前置条件的完整说明,同一个模型同一个任务能涨几十个百分点,一个字的训练都没有。
同一篇里还有更狠的一组:3B 模型配好的工具描述,比 7B 配差的高 14.1 个点。
所以工具描述不是文档,是杠杆。 该写的至少有四样:这个工具干什么、什么时候该用、参数各是什么含义、以及什么时候不该用它。最后一条最常被漏掉。
规矩三:解析层要迁就模型
模型不会严格按你的字段名回话。真实收到过的偏差包括:字段名换个说法、该给数组的给了对象映射、整个回复是个裸数组。
每一种都可以再写一版提示词去纠正,但那是把成本压在每一次调用上。写在解析层只付一次。
schema 迁就模型,比让模型迁就 schema 划算。
但这条有边界:解析层的宽容是给「模型换了个说法」用的,不是给「我没定义清楚」用的。 如果一个字段你认下了五种叫法,该反过来问是不是提示词里根本没说清它要什么。
多模态:把图喂进去
多模态把输入从文本扩到图像。这一格有两个非常实际的坑。
坑一:坐标系。 让模型报界面元素的位置时,它报的坐标可能不在你以为的那个空间里。我实测过一次:模型报的 x 一律偏左约 16%,比值恒定在 0.84 —— 反推是模型内部把图缩到了 1280 宽再报坐标。把 x 除以那个系数之后,四个控件全部落在 2–3 像素内。
模型的定位是准的,错的是坐标空间的换算。 而这类错误不报错,只是点错地方 —— 数值错了仍然是合法的值。
坑二:图很贵。 一张截图进上下文,实测 prompt 能到 9000 token 级别,而且即使前缀缓存命中了 6144 token,单次调用仍然要 85 到 90 秒。说明贵的不是文本,是图像编码本身。
所以多模态的成本模型和纯文本完全不同:文本按长度算,图像按张数算,而且每一张都贵。
这句话有多贵,我自己量过。本地跑一个 27B 的模型处理界面截图,单次视觉调用 85 到 90 秒,一条四步的用例整整十六分钟。 更说明问题的是日志里那一行:每次请求的 prompt 都在 9000 token 上下,其中 6144 已经命中前缀缓存 —— 也就是说贵的根本不是文本,缓存早就把文本那部分吃掉了,贵的是每一步都要重新编码一张图。所以多模态的优化方向和纯文本完全不同:压提示词几乎没用,减少调用图像的次数才有用。
边界与代价
工具越多不是越好。 工具描述是每一轮都要带的固定开销,而且相似的工具会互相干扰。我在本地还撞过一堵更硬的墙:想同时留一个文本模型和一个视觉模型待命,第三个一加载,推理服务直接拒绝,报的是 prefill 显存守卫拦下了一次约 22.49 GB 的请求。**多模态的「多」不只是多一种输入,是多一份常驻显存。**按需加载比一次全塞更实际。
函数调用的可靠性在不同模型上差异很大。 有的模型工具调用合法率很高,有的会编造不存在的工具名。这一格要单独测,不能假设「支持 function calling」就等于「用得对」。
多模态那两个坑我是自己撞的,但只在一台机器、一个模型上验证过。坐标系那个系数换个模型就会变 —— 真正的做法是每换一个模型都做一次已知答案的标定,而不是背下某个数字。