我见过一条AI生成的视频:篮球从半空“摔”到地面,弹起来的瞬间,影子却先一步跳到了墙上。把同样的提示词跑八次,每一次反弹轨迹都不一样。这让我意识到一个被很多人忽略的问题——视频生成模型在“想象力”上已经很强,但它对真实世界的信息和物理规律,几乎一无所知。
我们后来尝试的方案比较直白:在视频生成模型前面加一个“会查资料、会做模拟”的智能体层,用参数量只有8B的语言模型当大脑,让它学会自主调用外部工具,再把查到的资料、模拟出来的轨迹转成生成条件喂给视频模型。这篇文章会把项目的选型逻辑、链路设计、两个核心场景的落地细节,以及部署阶段的真坑完整梳理一遍。如果正在折腾AIGC应用、Agent推理,或者只是对Function Calling这类机制感兴趣,这篇内容应该能提供一些直接可用的参考。
1. 视频生成模型的“信息断供”与“物理失能”:工具调用为什么成为刚需
1.1 生成模型是在“想象”世界,不是在“看见”世界
先花点时间对齐一个基础认知:视频生成模型在训练阶段接触到的数据是大量视频片段,它学到的是一种视觉分布,而不是世界运行的规则。模型能生成一幅“雨天路面积水倒映霓虹灯”的画面,是因为训练样本里常见这种组合,不代表它理解积水为什么会倒映灯光,也不代表它知道雨滴的飘落方向受风速影响。
这个差异平时不明显,一旦需求超出训练样本的覆盖范围,问题就暴露了。比如历史上训练数据相对偏少、又需要精确空间关系的场景,模型往往会选择一个“视觉上合理但物理上离谱”的答案。篮球弹跳就是一个典型:生成模型靠像素统计拟合出“球落地后会弹起”这个现象,但拟合出来的轨迹可以是不衰减的、乱跳的,甚至影子移动方向和球完全不一致。整个画面单独看每一帧都说得过去,连贯起来却经不起任何推敲。
类似的失能也体现在“最新信息”上。训练数据的标注有截止点,凡是截止点之后出现的新事件、新地标、新技术名词,模型只能靠上下文猜测或直接编造。视频生成属性比文本生成更依赖这类细节,因为一个画面里出现的建筑、服装、街头装饰往往具有强时效性。没有外部信息入口,生成模型只能拿记忆片段“脑补”,这就是信息断供带来的幻觉。
1.2 纯生成流程的三个核心痛点
结合我们内部的测试,纯生成模型在视频场景下表现出的问题可以归纳为三点。
知识截止导致时新内容幻觉。让模型生成“某城市跨年夜街道”的画面,训练数据截止之后新搭建的灯光装置、新的广场改造格局,模型一概不知道。结果就是生成了风格趋同的“标准跨年夜”,而不是用户真正想要的那个现场。
物理规律缺失导致动态失真。涉及重物跌落、流体扩散、机械结构运动的场景,生成结果经常违反常识。更麻烦的是,这种错误不稳定,同一提示词每次跑结果都不一样,没法靠“调参”修正。
语义对齐脆弱。用户说“夕阳下的街道”,模型可能理解成晚霞、日落、余晖三种完全不同的视觉表现。文本描述和画面元素之间的映射关系,本质上是概率性的。需求越具体,模型猜错的概率越高。
1.3 工具调用的定位:不是替换生成模型,而是提高输入质量
解决了前面这些痛点,对工具调用的定位就很清晰了:它不会替代视频生成模型,而是改变生成模型的输入质量。
打个比方。智能体是导演,负责前期勘查场景、查证资料、设计运镜轨迹;视频生成模型是摄影师加特效团队,负责把导演的意图变成画面。导演不能替摄影师按快门,但导演的前期工作质量,直接决定了最终画面的上限。我们给视频生成前面加的这一层8B智能体,承担的正是“导演”的角色:需要事实信息时调用检索工具,需要运动规律时调用物理模拟工具,然后把结果组织成视频模型更容易落地的条件。
这个思路看起来简单,但落地过程中的细节相当多。接下来先讲模型选型和整体架构,这是后面所有实现的地基。
2. 为什么是8B而不是3B或70B:模型选型与智能体整体架构
2.1 参数规模是一个成本和能力的平衡点
项目初期,我们其实同时试过三个规模档位的开源基座模型,最后才锁定了8B。
小参数档位(3B左右)部署确实轻松,但工具调用场景表现勉强。它们面对多字段参数的函数结构时,经常丢掉必填字段,或者把参数名理解错;指令稍微复杂一点,模型的输出就开始偏离结构化格式。我们统计过,3B级模型在单工具调用上的参数完整率大约在70%左右,多工具组合时更是会掉到一半以下,这个水平没法用于生产。
大参数档位(70B级)能力没问题,但部署代价高,单卡放不下,推理延迟更是让人头痛。视频生成流程中,智能体层后面还有更贵的视频模型推理环节,中间层每多一秒延迟,用户感知的成本都放大一次。用过云上大模型接口虽然能省部署功夫,但把生成指令包来回传输的耗时和费用,在小批量验证阶段还能接受,规模化以后很难看。
8B是一个很特别的平衡点:指令遵循能力明显过了一个质变门槛,尤其在做工具调用这种结构化输出任务时,配合合适的提示词,几乎可以用得很稳;部署上,FP16权重约占用16GB显存,量化到INT4以后只有5到6GB,一张消费级显卡就能带动,可以和视频生成管线放在同一台机器上,省掉跨网络传输的延迟和费用。
2.2 三层架构与数据流向
8B智能体与大模型之间的数据流向,我画不出来流程图,但可以用文字很清楚地描述。
第一层是交互层。接收用户输入的自然语言需求,先做一个快速意图判断:这个请求是要直接生成,还是需要先查资料,还是需要做物理模拟,或者几种需求叠加。
第二层是智能体层。8B语言模型在这一层负责维护可用工具列表、解析用户输入中的关键参数、决定调用哪些工具以及调用顺序。模型输出的不是普通文本,而是一个结构化的工具调用计划,包含工具名称、参数、期望的输出类型。
第三层是执行层。外部工具集群在这里真正干活,包括文档搜索、参考图检索、物理引擎、姿态估计等。工具执行完返回结果,回到智能体层做过滤、压缩、重新组织,最终形成一份“生成指令包”,交给视频生成模型。
用伪代码描述最简管线:
user_request = "生成一段篮球从三层台阶上滚落并弹跳的视频" agent = load_agent("8b-ft-int4") plan = agent.parse_tool_plan(user_request) if plan.requires == "physics_sim": params = agent.extract_physics_params(plan) # 台阶高度、初速度、恢复系数 traj = physics_sim.run(params) # 返回轨迹点序列 conditions = to_control_points(traj) # 降采样为视频控制条件 elif plan.requires == "search_docs": raw = search_docs(plan.rewritten_queries) conditions = filter_and_format(raw) # 清洗压缩后的参考信息 elif plan.requires == "both": # 先查资料确定场景特征,再做动力学约束 scene_info = search_docs(plan.rewritten_queries) traj = physics_sim.run(plan.extract_physics_params(scene_info)) conditions = merge(scene_info, to_control_points(traj)) video = video_generation.generate(prompt=plan.prompt, conditions=conditions)流程看着不复杂,但真正麻烦的是每一步之间如何衔接。我们踩过的坑,后面在场景落地章节里展开。
2.3 三种工具调用模式
在具体实现里,工具调用不光是“调一下”这么简单。根据用户需求的复杂度,需要支持三种模式。
第一种是同步单调用。用户需求只需要一个工具,比如“查一下这款新发布车型的侧面轮廓”,智能体调用检索工具获取参考信息即可。
第二种是并行调用。需求涉及多个独立维度,例如“生成科幻风格实验室的清晨场景”,需要同时检索实验器材参考图和光照角度资料,两个查询之间没有依赖关系,可以并发执行,节省时间。
第三种是组合调用。场景里既有事实信息需求,又有动态模拟需求,就必须分步串行。比如“生成一只老鼠从斜坡木板滑下后撞到纸箱的片段”,先查斜坡木板的材质和场景参考,再根据材质参数设定摩擦系数,调用物理引擎计算滑落和碰撞轨迹,最后把两块信息合并成生成条件。
我们内部总结的决策规律很简单:需要事实信息的走查资料,需要运动规律或空间布局的走模拟,两者都需要的就并行发起。8B模型学会这个判断规则,比想象中更难,这部分放到训练章节细说。
3. Function Calling链路实战:从意图解析到工具结果回填
3.1 工具注册与Schema定义
工具调用的第一步,是把工具“告诉”模型。这里不是把一个函数源码扔给模型,而是注册一份结构化的工具描述,通常是一个函数定义清单。
以我们常用的两个工具为例:
{ "tools": [ { "type": "function", "function": { "name": "search_docs", "description": "在指定知识库或资料库中检索与查询词相关的图文资料,返回结果为文本片段和图片URL。", "parameters": { "type": "object", "properties": { "queries": { "type": "array", "items": {"type": "string"}, "description": "检索查询词列表,建议拆分不同维度,例如场景、光照、材质、视觉风格。" }, "max_results": { "type": "integer", "description": "期望返回的最大结果条数,默认5。" } }, "required": ["queries"] } } }, { "type": "function", "function": { "name": "physics_sim", "description": "运行确定性物理模拟,计算物体运动轨迹。适用于跌落、抛体、滑动、碰撞等场景。", "parameters": { "type": "object", "properties": { "object_mass": {"type": "number"}, "initial_velocity": { "type": "object", "properties": { "x": {"type": "number"}, "y": {"type": "number"} } }, "restitution": {"type": "number", "description": "恢复系数0~1"}, "gravity": {"type": "number", "description": "重力加速度,默认9.8"} }, "required": ["object_mass", "initial_velocity"] } } } ] }这里有一个容易被忽略的经验:工具描述里对参数含义写多少字,直接影响模型参数抽取的准确率。测试过程中,我们把“queries”参数的描述从不带说明改成现在的详细版之后,检索query的合理率提升了将近10个百分点。模型理解参数语义,不是靠名字猜的,而是靠描述文字里的信息密度。
另外,中文模型配中文描述,工具调用还原度明显高于配英文描述。同一个8B基座,英文schema和中文schema的测试对比,整体准确率相差好几个点。
3.2 强制JSON输出的提示与解析策略
Function Calling使用中,模型最让人头疼的毛病是话痨。明明让它只输出结构化结果,它偏要在JSON前面加一句“好的,我来为您处理”,或者JSON后面跟一段“这样可以理解吗”。
我们的提示词里写了很多强调,最有效的一条是“直接输出结果,不要任何解释性文字”。但提示词不是万能的,我们最终在解析层加了兜底机制:拿到模型输出文本后,先做JSON语法校验,不合法就用正则抽取最外层大括号,再丢给解析器二次重试。
实际用的输出格式:
{ "intent": "tool_use", "tool_calls": [ { "name": "physics_sim", "arguments": { "object_mass": 0.6, "initial_velocity": {"x": 0.8, "y": 0.2}, "restitution": 0.6, "gravity": 9.8 } } ] }如果模型连续两次都输出非法JSON,我们就不再重试,而是将整个请求降级为“纯生成模式”,由更上层的兜底策略接手。这个策略很有必要,因为工具调用链条上,解析失败继续死磕只会浪费时间,用户已经等不起了。
3.3 工具返回值如何压缩回填
工具执行完之后返回的内容,通常不能直接塞给视频生成模型。
搜索工具返回一堆文档片段,可能包含大量无关信息;物理引擎返回上千个轨迹点,视频模型的控制接口用不了这么密。这里需要一个信息压缩环节。我们让智能体对工具结果做三种处理:文本结果抽取关键事实,压缩成两三句结构化描述;视觉结果选最相关的两三张参考图;物理模拟结果均匀降采样,把一千个轨迹点抽稀到需要的8到12个关键帧。
这个过程如果不做,最直接的后果是上下文窗口被撑爆。我们初版就在这方面吃过亏,工具调用几次以后模型对原生指令的遵守度明显变差,本质是上下文本末倒置,原始指令的注意力占比被稀释了。
3.4 失败与超时:模型也要学会“认怂”
工具调用一定会遇到失败,真正考验链路设计的是失败后的反应。
我们遇到过三种典型情况。第一种是搜索返回结果为空。这时候模型可能反复重试相同查询,造成死循环。我们的处理是:单工具最多重试两次,两次仍失败的,把错误信息交给模型,让它“换一种查询策略”,比如换同义词、缩小检索范围或改变查询维度。
第二种是物理模拟参数不合法。比如用户描述里缺少必需参数,模型抽不全。单纯让模型补问用户很费轮次,我们更倾向于给一个合理的默认值并标明“默认赋值”,先把模拟跑通。
第三种是多个工具并行调用时部分失败。不能因为一个工具挂了就让整条链路失败,得让链路降级成剩余工具可完成的部分,并在给视频生成模型的指令包里手动加上一条“因资料缺失,该部分由模型推测”的标记。这个标记看似简单,实际在项目管理上帮了大忙,后续人工检查时能明确区分哪些内容是实测得出的,哪些需要重点审核。
4. “查资料”场景落地:让视频生成模型告别编造细节
4.1 一个典型的多信息需求场景
查资料这条链路,针对的是所有“需要真实世界细节”的视频生成需求。我们内部最常用的测试场景之一是“生成某城市老城区冬日早晨的画面”。
这个需求看似简单,实际上涉及很多细节:老城区的建筑立面是什么风格,冬日早晨的光线从哪个方向来,街道尺度是宽是窄,地面材质有没有特种铺装。纯生成模型面对这种需求时,会非常自然地按照训练样本里的“高线城市网红街区”来画,因为这种画面样本最多、最容易触发。
加入查资料环节后,智能体会先在知识库和公开资料库中检索老城区的街景描述,再用参考图检索功能拿到有代表性的建筑外观和道路材质图片,最后综合成一份场景特征摘要。视频生成模型拿到的不再是干巴巴一句“老城区冬日早晨”,而是具体的视觉元素和参考图,生成结果的细节丰富度会明显上一个台阶。
4.2 查询词改写是成败关键
实践中最先遇到的问题是:用户描述和检索工具能有效处理的查询词之间,存在很大距离。
用户说“老城区”,检索工具返回大量泛内容,有新闻、有散文、有攻略,真正包含视觉细节的有效内容占比很低。智能体需要把用户需求改写成适合搜索的查询词,把一个宽泛概念拆成多个维度。我们总结了一份经验化的改写规则,核心思路是把“氛围词”转成“实体词”。
| 用户表述 | 改写后的查询词 |
|---|---|
| 跨年夜 | 跨年夜城市广场灯光装饰、人群聚集、烟花燃放现场照片 |
| 老城区 | 老城骑楼建筑立面、青石板路质感、冬日晨光角度 |
| 科幻实验室 | 实验室光学设备、金属试验台、大型显示面板 |
| 雨夜街头 | 雨夜路灯、积水倒影、霓虹招牌、行人撑伞动作 |
这个改写过程看起来是纯工程技巧,实际上8B模型在训练章节学到的一个重点能力就是它。改写质量好不好,直接影响后续所有环节。如果查询词太泛,检索回来都是噪音,压缩环节工作量倍增;如果太窄,可能会漏掉关键维度的资料。
4.3 检索结果的清洗压缩与格式统一
工具返回的原始结果格式五花八门,文本、URL、图片混在一起,不能直接喂给视频模型。我们的做法是让智能体把结果统一成一个固定模板:
场景背景描述(2-3句):该区域保留有大量上世纪修建的骑楼建筑,街道宽度较窄,冬季早晨光线以低角度斜射为主。 关键视觉元素列表: - 骑楼连廊立柱和拱形窗 - 青石板路面,局部有苔藓 - 老式木质招牌 - 晨雾遮掩的高层塔楼轮廓 推荐参考图:ref_img_01.jpg, ref_img_02.jpg这个模板在压缩阶段就把信息结构化,视频模型直接按固定字段读取,避免理解混乱。同时固定模板也方便调试,哪一步出现问题,看中间产物就能快速定位。
4.4 查不到资料时的兜底策略
查资料链路不会永远顺利,最典型的失败场景是资料库覆盖不足。比如用户要生成“某个小城市本地节庆的巡游队列”,查询词已经改得很具体了,但资料库里就是没有有效内容。
这种时候最忌讳模型自己编一套。我们的策略是:智能体明确输出“未检索到该主题的有效图文资料”,然后回退到通用场景库,选择最接近的地区节庆风格预设,同时手动触发人工审核标记。生成结果会标注为“推测性画面”,后续审核人员重点检查这部分内容。这种“可追踪的兜底”比让模型假装查到了重要得多,它把灰色判断留给了人,而不是让模型擅自做事实性断言。
5. “做模拟”场景落地:用物理/轨迹工具约束生成结果
5.1 确定性模拟解决什么问题
生成模型在静态画面上的错误尚可接受,动态场景一旦违反物理规律,整个视频的可信度会崩塌。我们做模拟场景的定位很明确:把运动轨迹、空间布局这类适合确定性计算的环节,从生成模型手里拿回来,交给专门的模拟工具,让生成模型只负责把轨迹渲染成好看的具体帧。
篮球滚下台阶,就用物理引擎算弹跳轨迹;人物跑步,就用姿态库给关键帧关节角度;镜头推进,就用摄像机规划算法算平滑运镜曲线。确定性的归确定性引擎,视觉丰富的归生成模型,分工明确以后,高度非线性的动态场景稳定率大幅提升。
5.2 完整实操:篮球从台阶滚落并弹跳
我们把最常用的测试案例完整走一遍,便于复现。
用户输入:生成一段篮球从三层台阶上滚落并弹跳的视频。
第一步,8B智能体对需求做参数抽取。它需要从自然语言里提炼出台阶高度、初速度、恢复系数这些物理引擎输入。用户没有直接给参数,智能体按常识设定默认值——台阶每级高约0.15米,初速度设为0.8米每秒,恢复系数取0.6,重力加速度9.8。
第二步,调用物理引擎计算轨迹。引擎返回一组轨迹点,包含每一帧球心位置和速度向量。
第三步,轨迹降采样。物理引擎给出的点非常密,每帧一个点,直接用作控制条件会让后续生成端爆炸。我们按时间均匀抽8到12个关键帧,保留速度方向信息,其余细节让生成模型自行补全。
第四步,转换成视频生成模型能读取的控制条件。这一步涉及坐标系对齐、画布归一化,下文细说。
第五步,把控制条件和原始的文本提示合在一起交给视频模型,生成最终片段。
5.3 轨迹转控制条件:坐标系对齐的坑
这是做模拟场景落地时踩过最深的一个坑。
物理引擎输出的轨迹坐标是世界坐标系,单位是米,原点在台阶起点;视频生成模型的控制条件则是画布坐标系,单位是像素或归一化坐标。直接拿世界坐标去驱动画布上的球,结果就是球出现在画面的左上角之外,或者弹跳高度明显失真。
解决办法是在转换层做一次仿射变换:先根据视图范围把世界坐标归一化到0到1区间,再映射到画布分辨率,最后按时间轴对齐关键帧。踩了两次坑之后,我们把它固化成标准处理流程:
1. 确定画布视野的物理范围(宽、高对应多少米) 2. 世界坐标统一减去起点,按视野范围缩放 3. 坐标轴翻转以匹配画布Y轴朝下的方向 4. 若模拟结果超出画面,做裁剪而不是整体缩放第4步也很关键。如果球弹得比画面高,整体缩小会让整段视频观感失真,正确做法是只截取画面范围内的轨迹段,让球飞出画面也是合理的视觉表达。
5.4 模拟工具远不止物理引擎
模拟场景的工具有很多种,物理引擎只是覆盖面最窄的一类。我们后来陆续补齐了三类常用工具。
人体动作轨迹库负责生成角色动作。用户说“人物翻滚躲开飞来的障碍物”,智能体调用动作库获得翻滚动作的关节旋转序列,作为视频生成的动作控制信号,这样生成的角色动作不再是模型“自由发挥”,而是符合人体运动约束的合理动作。
摄像机路径规划负责运镜。要生成一段“镜头从地面缓缓升起,又快速推向远处广告牌”的片段,摄像机轨迹会让生成模型明确知道各时刻的视角变化,避免画面连续性和景别逻辑出问题。
光照与天气模拟负责光效。根据时间地点估算太阳高度角、光线方向和色温,这些参数作为附加条件控制视频画面的光影风格。
这三类工具组合起来,覆盖了动态视频中绝大部分“需要确定性约束”的部分。
6. 训练数据构造与微调:教会8B模型在视频域里用工具
6.1 通用工具调用能力从哪里来
一个开箱即用的8B基座模型,在普通对话场景表现不错,但直接扔到工具调用场景,表现非常不稳定。它会选择性忽略工具描述,把工具参数当成闲聊内容,或者输出格式不对。
提升路径分两步。第一步用公开的指令集把基座模型的浅层工具调用能力激活出来,让模型理解结构化输出任务;第二步用视频生成域的自定义数据做深度微调,让它学会在视频场景里做合理的工具选择。
只有第一步的话,模型能完成简单的查天气、算加减法等通用工具调用,但遇到视频生成这种多步骤、多条件融合的任务,它就不知道该怎么组合工具了。域内微调是关键。
6.2 视频域工具调用数据长什么样
训练数据构造是整个项目里最吃人力,也是回报最高的部分。我们构造的数据格式严格统一,一条指令数据由三个方面组成:用户生成需求、期望的工具调用序列、以及最终给生成模型的指令包。
一条典型样本:
{ "instruction": "生成一段雨夜路灯下的人物剪影视频", "tool_sequence": [ {"tool": "search_docs", "arguments": {"queries": ["雨夜", "路灯", "人物剪影", "视觉参考"]}}, {"tool": "physics_sim", "arguments": {"scenario": "rain_droplet_motion", "wind_speed": 3.2}} ], "final_instruction_pack": { "scene": "雨夜路灯下,人物站在逆光位置,形成剪影,雨滴呈斜线飘落", "reference_images": ["rain_night_01.jpg"], "dynamic_control": "rain_droplet_trajectory_level_2" } }数据来源主要有三个渠道。第一个是内部产品同学提交的真实用户需求,这些样本最贴近线上情况;第二个是用更强的大模型把已有样本做扩写,增加表达方式多样性,比如同一需求换成不同句式、不同语气;第三个是人工构造一些边界样本,专门覆盖工具调用失败、参数缺失、异常请求等情况,否则模型遇到没见过的边界场景会乱来。
数据量不需要特别大,质量比数量重要得多。我们初期用约一万条高质量样本就把工具调用成功率拉到了可以接受的范围,后面再增加数据,收益边际递减。
6.3 SFT加偏好优化的完整策略
训练策略本身并不复杂,复杂的是每步的目的要清晰。
第一步做有监督微调SFT,采用LoRA方式只训练部分适配器层,保留模型通用能力的同时让它学会视频域工具调用格式。这一步完成后,模型已经能输出结构正确的工具调用序列,但仍存在一个明显问题:它会在输出前加很多“思考过程”或者解释性文字,增加解析难度、拖慢延迟。
第二步做偏好优化,我们采用DPO的方法,构造配对样本:一组是“正确调用工具且没有多余废话”的输出,一组是“调用正确但啰嗦解释”的输出,让模型学会压缩输出内容。这一步对延迟的影响非常明显,推理时每次调用节省的token数可观,累积起来整体服务时延降低了约两成。
6.4 端到端评测指标怎么设
工具调用环节的评测维度虽然可以拆得很细,但最终都要落到“端到端视频质量是否提升”上来。我们日常监控的核心指标有四组:
| 评测维度 | 说明 | 测量方式 |
|---|---|---|
| 工具选择准确率 | 在应当调用工具的场景中,模型正确选择对应工具并完成调用的比例 | 离线抽取测试集,人工校验 |
| 参数完整率 | 工具调用json里必填字段的完整占比 | 自动校验+人工复核 |
| 信息可用率 | 工具返回结果经压缩后,能被视频模型有效采纳的比例 | 比对生成结果与资料内容的相关性 |
| 端到端主题命中率 | 最终生成视频是否准确匹配用户原始需求 | 人工评估、AB对比 |
这四个维度里,前两个偏模型能力,后两个偏链路结果。调试时我会建议先看前两个,因为后两个指标受视频生成模型自身的稳定性干扰太大,链路问题容易被掩盖。
7. 部署阶段的硬仗:延迟、稳定性与量化踩坑
7.1 INT4量化带来的工具调用能力退化
部署最省资源的方式是量化,8B模型量化到INT4后显存占用只有5到6GB,一张消费级显卡就能带动。但量化不是免费午餐,对工具调用这种结构化输出任务,压缩精度会导致两个明显问题:参数名偶尔拼错,以及嵌套JSON结构层级一深就容易失稳。
我们的处理方案是混合精度部署——大部分层用INT4,保留最后两层的FP16精度。只留最后两层,显存增加非常有限,但参数还原准确率提升明显。另外在数据层面也做了补偿:训练阶段加入一定比例的量化感知模拟,让模型在低精度环境下的输出更稳。
7.2 上下文窗口与延迟优化
工具调用链路有一个天然矛盾:工具越多、返回结果越多。上下文一旦被撑满,模型对初始指令的注意力就会下降,表现为“忘记”用户要什么。我们的做法是给工具结果分优先级,事实结论排在最高优先级。
优先级排序是这样的:结论性摘要优于原始文档,参考图优于文字描述,关键帧轨迹优于完整轨迹数组。压缩模块按照这个顺序裁剪,保证没用的信息最先被丢弃。在上下文窗口从4K调整到8K的过程中,我们还专门验证过一个规律:不是窗口越大越好,更大的窗口意味着模型需要处理更多噪音,工具结果反而可能被稀释。
延迟优化上,最有效的一招是预加载热点工具。把用户最常用的几个工具进程常驻内存,避免每次调用时重新拉起进程。查资料类的工具启动开销大,常驻后整个工具调用阶段平均耗时能减少约三分之一。
7.3 工具白名单与内容安全
智能体能自由调用工具听上去很酷,但生产环境绝对不能这么放开。我们给工具层加了一层强制白名单,智能体只能调用预先登记、已经过安全审查的工具,不能自行“发明”新工具或者调用未授权接口。
所有工具调用请求和返回结果都会留档。尤其是查资料类工具的搜索词,全部进入审计日志,方便后期追溯。这层设计和模型本身的能力无关,纯粹是工程底线,但它决定了系统能不能真正上线跑业务。工具调用的上限应该定在工程可靠性之内,而不是模型能力上限。
7.4 一组实测数据和调优建议
在这个配置下,我们积累了一组内部对比测试数据。数据来自固定测试集,不宣称代表所有场景,但趋势可以参考:
| 配置 | 工具选择准确率 | 端到端主题命中率 | 单请求平均时延(秒) |
|---|---|---|---|
| 未加智能体直接生成 | - | 64% | 18 |
| 加智能体+查资料 | 87% | 81% | 22 |
| 加智能体+查资料+模拟 | 92% | 89% | 27 |
从数据里能看出两个点。第一,加入工具调用后,端到端主题命中率提升明显,说明资料和模拟信息对生成结果的定向作用很实在。第二,延迟增加了约50%,但用户观看时对视频内容准确率的感知价值远高于这不到十秒的等待。
调优上还有一条几乎没被人提过但非常实际的建议:不要一次性给智能体挂太多工具。每增加一个工具,模型在多点选择时的准确率都会下降几个百分点,闲置工具会产生选择噪音。按需加载,场景确定时只开放需要的两到三个工具,效果比全部堆上去要好得多。
这个项目走到现在,我最深的一层体会是:工具调用不是把一堆能力塞给模型就完事,而是要把它设计成可验证、可回退、可观察的工作流。8B模型能发挥出多高的上限,很大程度上取决于你喂给它的工具上下文干不干净、结构化程度够不够。后期我们还在试更细的方向,比如让智能体根据用户历史偏好自行挑选工具,以及让模拟结果直接参与首帧生成。每一步都有新问题,但这条路的方向是对的。对于同样在折腾这类应用的朋友,建议先抓住“查资料”和“做模拟”这两个基本场景跑通,再考虑扩展更多花式工具,链路稳定永远是第一位的。