这个坑值得记一笔。提示词模板用 Jinja2 3.1.4 渲染,模板里想在 for 循环中标记某个标签出现过,循环外再用这个标记决定是否追加一段说明,渲染出来永远是空字符串。改成直接赋值也一样,试过加 do 扩展和 namespace 两种写法,只有后者生效但初始化写错了位置,模型收到的正文一直少那一段。

刚开始盯着「没有开启 StrictUndefined,缺失变量被渲染成空串且不报错」,折腾了半个多小时,排掉了。

原因不止一个,得分开看。Jinja2 的 set 写在循环体内是局部作用域,出了循环就丢;namespace 对象必须在循环外初始化,循环里重新赋值才算数;模板被渲染两次,第一次的变量状态没有传下去。

先用最小模板确认作用域行为

python -c "from jinja2 import Template;print(Template('{% set n=1 %}{% for i in [1,2] %}{% set n=i %}{% endfor %}{{ n }}').render())"

随后改成 namespace 写法后再渲染

python -c "from jinja2 import Template;print(Template('{% set ns=namespace(f=false) %}{% for i in [1] %}{% set ns.f=true %}{% endfor %}{{ ns.f }}').render())"

接着打开 StrictUndefined 让缺失变量直接报错

python -c "from jinja2 import Template,StrictUndefined;print(Template('{{ missing }}',undefined=StrictUndefined).render())"

末了把渲染结果落盘后与提示词原文对照

python -c "from jinja2 import Template;open('p.txt','w').write(Template(open('t.j2').read()).render(tags=['a']))"

怎么算真的好了,看的是同一批现象。渲染后的提示词里那段说明稳定出现,StrictUndefined 下不再报 undefined,同一份模板跑 100 次结果一致。

容易忽略的是,Jinja2 在循环里 set 的只是临时变量,跨循环传值只能用 namespace,或者把判断挪到 Python 侧算好再传进模板。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。

作者 9823189