一种"带插件的文本生成器"。它允许你创建一个包含变量和逻辑的"模板"文件,然后在渲染时填入具体数据,最终生成想要的文本格式(如 HTML、JSON、Markdown 等)。 它的语法简单直观,主要有三种核心元素: - **`{{ … }}`**:用于输出变量值,例如 `{{ user_name }}` 会在渲染时被具体的用户名替换。 - **`{% … %}`**:用于执行逻辑,例如循环 `{% for item in items %}` 或判断 `{% if user_logged_in %}`。 - **`{# … #}`**:用于添加注释,不会出现在最终输出中。 ### 提示词(Prompt)工程 主要用于**动态、精确地构建和管理提示词(Prompt)**。其核心价值在于将"提示词模板"与"可变数据"分离。 #### 1. 构建动态、结构化的提示词 在实际应用中,很少会发送固定的文本给 AI。你需要将用户问题、聊天历史、检索到的知识等动态拼接到提示词中。Jinja 让这件事变得优雅和可控。 **示例:一个智能客服的提示词模板** ```jinja 你是一名专业的客服助手,名叫"小智"。 请基于以下提供的资料,回答用户的问题。 如果资料中找不到答案,请直接说"不知道",不要编造。 ### 参考资料: {% for doc in search_results %} - {{ doc.title }}: {{ doc.content }} {% endfor %} ### 对话历史: {% for msg in chat_history %} {{ msg.role }}: {{ msg.content }} {% endfor %} ### 用户当前的问题是: {{ current_question }} 请给出你的回答: ``` 在这个模板中,`search_results`、`chat_history` 和 `current_question` 都是变量。渲染时,Jinja 会循环填入所有搜索结果和对话记录,生成一个结构清晰、信息完整的提示词。 #### 2. 统一聊天模型的提示词格式(Chat Template) 这是 Jinja 在 AI 领域最"杀手级"的应用。每个大模型(如 Llama、Mistral、ChatGLM)对聊天对话的输入格式要求都不同。**Chat Template** 就是用 Jinja 语法写的一段脚本,定义了如何将 `messages` 数组(包含 role 和 content 的对话)转换成模型能理解的单一字符串。 **一个简化的 Chat Template 示例:** ```jinja {% for message in messages %} {% if message['role'] == 'user' %} {{ '[INST] ' + message['content'] + ' [/INST]' }} {% elif message['role'] == 'assistant' %} {{ ' ' + message['content'] + ' ' }} {% endif %} {% endfor %} ``` - **输入(Messages)**: `[{"role": "user", "content": "你好"}, {"role": "assistant", "content": "你好!有什么可以帮你的?"}]` - **输出(Prompt String)**: `[INST] 你好 [/INST] 你好!有什么可以帮你的? ` AI 框架如 Hugging Face Transformers 已经内置了对 Jinja Chat Template 的支持。你只需调用 `tokenizer.apply_chat_template(messages)`,它就会自动使用模型对应的 Jinja 模板来生成正确的输入,极大地简化了开发流程。 #### 3. 在 AI 工作流和数据转换中作为"胶水" 在复杂的 AI 应用(如 RAG 检索增强生成、Agent)和工作流平台(如阿里云的 AI Studio、Azure 的 Prompt Flow)中,Jinja 常被用作轻量级的**数据处理和格式化工具**。 例如,可以将检索到的多个相关文档片段,通过一个 Jinja 模板快速格式化为一个结构清晰的 Markdown 列表,再输入给大模型。 ``` {% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='') %}{%- for message in messages %}{%- if message['role'] == 'system' %}{% set ns.system_prompt = message['content'] %}{%- endif %}{%- endfor %}{{bos_token}}{{ns.system_prompt}}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is none %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls']%}{%- if not ns.is_first %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- set ns.is_first = true -%}{%- else %}{{'\\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- endfor %}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is not none %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{% set content = message['content'] %}{% if '' in content %}{% set content = message['content'].replace('', '').split('')[-1] %}{% endif %}{{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'\\n<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|>'}}{% endif %} ```