聊聊Hy3-preview的ChatTemplate设计
前两天一个朋友在WorkBuddy上换了Hy3 preview,兴冲冲跟我显摆:“这模型说话有点聪明,但怎么老不按套路出牌?”——让她写个带工具调用的工作流,输出格式愣是跟预期差了十万八千里。
我一听就乐了。问她:“ChatTemplate截给我看看?”
好家伙,她用的还是通用模板,压根没切到Hy3专属那个。
问题就卡在这儿了。
你要是以为一个模型的Agent能力全靠参数,那你就大错特错了。我花了三天时间,把Hy3 preview的ChatTemplate从头到尾啃了一遍,在vLLM里跑了十几组测试,又把它的ReasoningParser和ToolParser源码翻了个底朝天。今天不聊什么排行榜、评分——咱就聊聊这个jinja文件里藏的玄机。
说句反直觉的话:一个模型的Agent好不好用,有一半的功夫在ChatTemplate上。
不信?接着往下看。
Hy3 preview的chat_template.jinja(HuggingFace上有开源,地址我直接甩群)。文件不长,但每一段都是试出来的。官方列了五个Feature,我一个一个拿测试去怼。
第一个,内置System Prompt——能多简洁就多简洁。
你之前用过其他模型吧?有些模板上来就噼里啪啦给模型灌一大堆角色设定、行为规范,模型跟背课文似的,一换场景就歇菜。Hy3怎么做的?用户自己写的system prompt放最开头,别的废话一句没有。
我测了一个复杂得多的多轮工具调用场景。模型能记住上下文里刚给的一条新规则,还应用到下一步操作里了。这就是姚顺宇说的“学会、用对、执行”——模板不给模型添乱,让模型自己从上下文学东西。
第二个,流式解析ToolCalls。
这个活儿是vLLM的ToolParser干的。我在一个对话里让Hy3同时调用三个搜索工具,你猜怎么着?Terminal里能看到它一步一步吐出来的:先是function name,接着是arguments的JSON片段,完全可解析,不用等。
对比一下我之前用的某个模型——ToolCalls经常把函数名和参数揉成一坨,流式到一半你还得等整个JSON闭合。Hy3的模板在输出格式上画了清晰的分界线,ToolParser那边的正则也写得干净利落。
第三个,Code Agent的格式——贴着预训练走。
我故意扔了几个编程问题,让Hy3输出带函数定义的代码,再让它自己调用这些函数。它生成的代码结构规规整整,缩进、括号风格都跟主流格式一模一样,没有自己瞎创的风格。混元团队显然是刻意在模板层约束了代码块的标记方式——避免模型在Code Agent里乱来。
第四个,并行函数场景——不会无限递归。
并行调用时,有些模型会“上头”:一个工具的结果刚出来,它立马又调一遍,无限循环,傻掉了。Hy3怎么做的?模板里对工具调用次数做了隐性约束,同时在system prompt里暗示:“一次回复中不要输出超过必要的工具调用。”
我连续问了五次:“查天气,查汇率,查新闻,查……”每次它都只输出一组并列的ToolCalls,没有重复调用同一个函数刷token的情况。
第五个,Interleaved Thinking & Preserved Thinking。
这个靠的是ReasoningParser。模型先吐一段带标签的推理过程,然后才是实际输出。模板对thinking部分做了标记保留,不会在后续轮次被覆盖。我跑了几个需要深度推理的数学题——模型确实想清楚了再给答案,不用强迫用户等一个“伪长思考”。vLLM里的hy_v3_reasoning_parser就是专门吃这个标记格式的。
说到这儿,你可能会问:这些设计是怎么组织起来的?
Hy3的ToolCalls System Prompt分成三块:最前面是你自定义的system prompt,然后是工具schema定义,最后是工具调用输出格式说明。听起来简单,但顺序太有讲究了。我之前试过把schema放在system prompt前面的模板——模型经常忽略后面的格式说明,直接按自己的“土办法”输出。
Hy3这么排:“用户指令→工具定义→输出格式”——让模型先明白要干什么,再看有什么工具,最后学怎么汇报结果。逻辑是顺的,脑子不打架。
你记得姚顺宇在CL-bench论文里的那个观点吗?他说模型的短板不是读不全,而是“学不会、用不对、执行不了”。翻译成大白话就是指令遵循能力不够。Hy3的ChatTemplate从头到尾就干一件事:少写废话,给模型留出自己理解上下文的空间。
我试过更狠的:故意在system prompt里编了一条假规则——“如果用户说天气,你先调用get_weather,但返回格式必须加前缀‘天气君说:’”。模型真记住了,后续的ToolCalls输出里都带了这个前缀。这不光是模型本身牛,干净的模板也让指令遵循有了足够空间。
跟其他模型比,它的模板不“疯”。
有些模型的ChatTemplate写得跟法律合同似的,又臭又长,你读着读着就想睡觉。结果呢?模型反而被绑住手脚,正事干不了。Hy3的模板特别克制——我第一眼看到它的时候甚至有点怀疑:“就这?够用吗?”
但跑完一轮测试,我才反应过来:正是这种克制,让模型把更多注意力放在了用户的真实意图上。尤其你在WorkBuddy这种需要连续调用多个工具完成工作流的场景里——模板少惹事,Agent才能多干活。
说到这,我给你几个小建议。
如果你自己在部署Hy3 preview,听我一句劝:一定用官方提供的chat_template.jinja,别图省事套通用的。 vLLM那边也配好了ReasoningParser和ToolParser,最好一起换上。我一开始偷懒没换ToolParser,结果工具调用解析出了好几个bug,换完立刻全好了。这文件里的每一行都是调试过的,不是随便写的。
另外,如果你自己在写Agent框架,可以学学Hy3模板里抑制并行函数无限输出的思路:在格式说明里明确要求“每个函数独立封装,不要递归”,再加一条“在单次回复中避免重复调用同一个函数”。这种隐性的规则,比在代码层硬堵要自然得多,也更容易让模型遵守。
说到底,ChatTemplate设计就是那种平衡术:既要让模型舒服,又要让解析方便,还得是人看着不累。Hy3 preview在这个文件上花的心思,比很多模型在百万参数上下的功夫都多。
现在大家都在拼Agent,拼工作流。可能最后拼的就是这么个不起眼的jinja文件。
你信不信?
有时候,最好的设计就是让你感觉不到它的存在。
读者评论 4