当前位置: 首页 > news >正文

vllm0.8.5:自定义聊天模板qwen_nonthinking.jinja,从根本上避免模型输出<think>标签

一、环境

vllm:0.8.5

大语言模型:DeepSeek-R1-Distill-Qwen-1.5B

内存:128G

GPU:无

二、qwen_nonthinking.jinja内容如下:

2.1、内容解读

第一部分:如果存在可用工具(tools

{%- if tools %}

判断是否存在外部提供的工具(函数),如果有,则进入工具模式。

显示系统头 + 工具说明

    {{- '<|System|>\n' }}    {%- if messages[0].role == 'system' %}

        {{- messages[0].content + '\n\n' }}

    {%- endif %}

  • 输出 <|System|> 标记。
  • 如果第一条消息是 system 角色,输出其内容,并加两个换行。
输出工具列表说明

    {{- "# Tools\n\nYou may call one or more functions..." }}    <tools>

    {%- for tool in tools %}

        {{- "\n" }}

        {{- tool | tojson }}

    {%- endfor %}

    </tools>

  • 向模型说明可以使用的工具。
  • 使用 <tools>...</tools> 包裹每个工具的 JSON 定义(如函数名、参数等)。
  • tool | tojson 将 Python/JSON 对象转为标准 JSON 字符串。
指导模型如何调用函数

    {{- "\n</tools>\n\nFor each function call... within <tool_call><tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": ...}\n<tool_call>\n" }}

告诉模型:当你想调用函数时,请使用如下格式:

<tool_call>{"name": "get_weather", "arguments": {"city": "Beijing"}}</tool_call>

注意:这里的 <tool_call> 是一个自定义 XML-like 标签,用来清晰地标记函数调用的开始和结束,避免与普通文本混淆。

第二部分:如果没有工具

{%- else %}    {%- if messages[0].role == 'system' %}

        {{- '<|System|>\n' + messages[0].content + '\n<|End|>\n' }}

    {%- endif %}

{%- endif %}

  • 不显示工具相关说明。
  • 仅输出第一条 system 消息(如果有),并用 <|End|> 结束。


第三部分:设置命名空间变量(用于判断上下文)

{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}

创建一个命名空间 ns,用于在循环中跨迭代共享变量:

  • multi_step_tool: 判断是否处于多步工具调用流程。
  • last_query_index: 记录最后一次“用户主动提问”的位置。
向前遍历消息,判断是否为多步工具调用

{%- for message in messages[::-1] %}    {%- set index = (messages|length - 1) - loop.index0 %}

    {%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_call>') and message.content.endswith('<tool_call>')) %}

        {%- set ns.multi_step_tool = false %}

        {%- set ns.last_query_index = index %}

    {%- endif %}

{%- endfor %}

  • 倒序遍历 messages(从最新到最旧)。
  • 找到第一个 role == "user" 且内容不是以 <tool_call> 开头/结尾的消息(即不是工具调用指令)。
  • 把这个消息的索引记为 last_query_index,表示这是用户最后一次“真正提问”的位置。
  • 用于后续判断哪些回复需要加上 <|End|>

作用:防止在工具调用链中错误地终止对话流。


第四部分:主循环 —— 遍历每条消息并格式化输出

{%- for message in messages %}

对每条消息进行处理。

设置内容变量

    {%- if message.content is string %}        {%- set content = message.content %}

    {%- else %}

        {%- set content = '' %}

    {%- endif %}

安全获取 content,避免非字符串类型出错。


情况一:用户消息(User)

    {%- if message.role == "user" %}        {{- '<|User|>\n' + content + '\n<|End|>\n' }}

格式:

<|User|>[用户内容]

<|End|>


情况二:助手消息(Assistant)

    {%- elif message.role == "assistant" %}

处理 <think>...</think> 推理块(可选)

        {%- set reasoning_content = '' %}        {%- if '</think>' in content %}

            {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}

            {%- set content = content.split('</think>')[-1].lstrip('\n') %}

        {%- endif %}

  • 如果内容包含 <think>推理内容</think>,则提取推理部分(虽然这里没使用),并把剩余部分作为正式回复。
  • 这是为了支持“先思考,再回答”的格式。

根据是否是“最后一问之后的回复”,决定是否加 <|End|>

        {%- if loop.index0 > ns.last_query_index %}            {{- '<|Assistant|>\n' + content.lstrip('\n') + '\n<|End|>\n' }}

        {%- else %}

            {{- '<|Assistant|>\n' + content + '\n<|End|>\n' }}

        {%- endif %}

  • 如果该回复是在用户最后一次提问之前,不加 <|End|>(可能是中间步骤)。
  • 如果是在之后,加上 <|End|> 表示回应完成。

处理工具调用(Tool Calls)

        {%- if message.tool_calls %}            {%- for tool_call in message.tool_calls %}

                {%- if (loop.first and content) or (not loop.first) %}

                    {{- '\n' }}

                {%- endif %}

                {%- if tool_call.function %}

                    {%- set tool_call = tool_call.function %}

                {%- endif %}

                {{- '<tool_call>\n{"name": "' }}

                {{- tool_call.name }}

                {{- '", "arguments": ' }}

                {%- if tool_call.arguments is string %}

                    {{- tool_call.arguments }}

                {%- else %}

                    {{- tool_call.arguments | tojson }}

                {%- endif %}

                {{- '}\n</tool_call>' }}

            {%- endfor %}

        {%- endif %}

  • 如果助手要调用函数,生成如下格式:

<tool_call>{"name": "search_web", "arguments": {"query": "天气预报"}}</tool_call><tool_call>{"name": "get_time", "arguments": {}}</tool_call>

  • 支持多个函数调用。
  • arguments 可以是字符串或对象,自动处理。


情况三:工具执行结果(Tool)

    {%- elif message.role == "tool" %}        {%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}

            {{- '<|User|>' }}

        {%- endif %}

        {{- '\n<tool_call>\n' }}

        {{- content }}

        {{- '\n</tool_call>' }}

        {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}

            {{- '\n<|End|>\n' }}

        {%- endif %}

  • 工具返回的结果被视为“用户侧输入”,所以用 <|User|> 标记。
  • 多个连续的 tool 消息会被合并到同一个 <|User|> 块中。
  • 每个工具结果用 <tool_call>\ntool_result\n</tool_call> 包裹,与函数调用请求对称。
  • 最后一个工具结果后加 <|End|>

示例输出:

<|User|><tool_call>

{"result": "北京今天晴,气温25°C"}

</tool_call>

<tool_call>

{"result": "当前时间:2025-08-03 18:30"}

</tool_call>

<|End|>


 第五部分:生成提示(用于触发模型回复)

{%- if add_generation_prompt %}    {{- '<|Assistant|>\n<think>\n\n</think>\n\n' }}

{%- endif %}

  • 如果设置了 add_generation_prompt=True,在最后加上:

<|Assistant|><think>

</think>

  • 表示“现在轮到模型输出了”,并且鼓励模型先进行内部推理(<think>)。
  • 这个标记不会被模型重复输出,而是作为生成起点。

总结:这个模板的作用

功能

实现方式

✅ 支持函数调用

用 <tool_call>...<tool_call> 包裹 JSON 格式的调用请求

✅ 接收工具结果

工具返回也用 <tool_call>...</tool_call> 标记,归为用户输入

✅ 支持多步工具调用

通过 ns.last_query_index 判断上下文

✅ 支持内部推理

使用 <think>...</think> 分隔思考与回答

 

http://www.lryc.cn/news/608598.html

相关文章:

  • 【python实用小脚本-169】『Python』所见即所得 Markdown 编辑器:写完即出网页预览——告别“写完→保存→刷新”三连
  • k8s+isulad 国产化技术栈云原生技术栈搭建1-VPC
  • OSPF HCIP
  • Starrocks ShortCircuit短路径的调度
  • 华为云云服务高级顾问叶正晖:华为对多模态大模型的思考与实践
  • 基于云模型的模糊综合风险评估Matlab代码
  • Matlab 高斯牛顿法拟合曲线
  • K8S部署ELK(四):部署logstash
  • MATLAB小波分析工具包进行时间序列的小波功率谱分析
  • 后端研发转型爬虫实战:Scrapy 二开爬虫框架的避坑指南
  • 量子物理学的前沿意义虚无、形式混乱
  • 0803 思维导图+小项目
  • Python爬虫实战:研究awesome-python工具,构建技术资源采集系统
  • uniapp 跨端开发
  • 机器学习——下采样(UnderSampling),解决类别不平衡问题,案例:逻辑回归 信用卡欺诈检测
  • 什么是shebang
  • Java基础:代码块/内部类/Lambda函数/常用API/GUI编程
  • JavaEE初阶第十三期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(十一)
  • 自动驾驶中的传感器技术20——Camera(11)
  • 【MATLAB】(六)多项式的创建与四则运算
  • TCP-单线程版本
  • pytorch 安装
  • 2025年渗透测试面试题总结-2025年HW(护网面试) 76-1(题目+回答)
  • cmd怎么取消关机命令
  • 麦肯锡咨询公司PEI经典面试题目汇总
  • 【一天一个知识点】RAG遇见推理
  • Piriority_queue
  • sifu mod制作 相关经验
  • Linux性能监控与调优全攻略
  • 轻量级鼠标右键增强工具 MousePlus