Mustache 参数报错
Mustache 聊天模板能渲染 user 字典,as_tool 却报 string_type。显式声明嵌套参数,在工具校验后转回普通字典,验收真实姓名没有变空。
ChatPromptTemplate 使用 {{user.name}}、{{user.company}},直接传入 user 字典能正常渲染。转换成 as_tool() 后,同一份输入却报 Input should be a valid string,工具参数也把 user 描述成 string。
这条情况有 langchain-core 1.4.8 与后续源码反馈,已核对的正式 1.6.10 仍有两层需要分别处理:聊天提示词的嵌套输入 schema,以及工具校验后传给 Mustache 的对象表示。
改对 schema 还不够
ChatPromptTemplate 使用通用输入 schema,未明确指定类型的顶层变量默认是 str。Mustache 模板需要的 user 本来是带 name、company 的对象,渲染能力与声明的工具参数因而不同。
普通 PromptTemplate 已有 Mustache 专用 schema,但只把聊天模板换成它,或只给工具补一个嵌套 Pydantic schema,并不能保证渲染正确。工具校验后,嵌套 user 可能成为 Pydantic 模型实例;Mustache 的点路径只遍历映射、列表与元组,不遍历任意 Python 对象。没有再转换时,姓名和公司可能静默变成空字符串。
因此需要同时核对“模型看见的输入 schema”和“渲染器真正收到的输入”,不能只以 ValidationError 消失为准。
显式声明,再转成字典
保留嵌套 user 契约时,可以用明确的工具 schema,并在提示词前将已校验的 User 转成普通字典。下面定义一个工具工厂,不创建模型实例,也不调用模型或工具:
from pydantic import BaseModel
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda
class User(BaseModel):
name: str
company: str
class GreetingArgs(BaseModel):
user: User
def template_input(values):
user = values["user"]
if isinstance(user, User):
user = user.model_dump()
return {"user": user}
def make_greeting_tool(model):
prompt = ChatPromptTemplate.from_messages(
[("human", "Write a greeting for {{user.name}} from {{user.company}}.")],
template_format="mustache",
)
chain = RunnableLambda(template_input) | prompt | model
return chain.as_tool(
args_schema=GreetingArgs,
name="greet",
description="Greet a user using their name and company.",
)
这是仅有 user 两个字符串字段的示例。真实工具应沿用原来的必填、可选、别名、额外字段和类型验证,不用示例 schema 覆盖业务契约。输入适配器也应保留模板实际需要的其他字段、消息历史和 partial 设置。
model_dump() 只在这里处理已知 User,不把任意传入对象的属性暴露给模板。不要修改 Mustache 的安全边界,让它随意遍历 Python 对象;也不要把 user 序列化成 JSON 字符串,那仍不能供 user.name 查找。
另一种办法是经过调用方协商,把接口改成两个明确的平面字符串字段,并同步模板、工具签名和已有调用方。这会改变公开输入契约,应按自己的接口流程处理,不能只改模板一端。
验收实际渲染出的内容
检查工具 schema 中 user 是对象,name、company 的类型和必填要求准确,再用获准的无副作用模型替身核对经过工具入口后,提示词仍含真实姓名和公司。缺字段、字段类型错误也应按原契约拒绝,不能悄悄变成空白问候。
直接 prompt.invoke 成功,只证明字典渲染这一段正常,不能替代工具入口验收。反过来,工具接受对象也只说明校验通过;最终渲染为空时,应先检查嵌套模型转换,而不是让模型猜测缺失字段。


