Langchain 附加函数及应用
“附加函数”在LangChain里,本质是通过 model.bind(functions=...)
给LLM“定规则”——要么让它按固定结构输出数据(比如JSON),要么告诉它能调用哪些外部工具(比如查天气的API、发邮件的功能)。核心作用是把LLM的“自由文本输出”变成“机器能直接用的结构化结果”,避免格式混乱,还能让LLM联动外部能力,这是普通提示词很难稳定做到的。
一、先搞懂:附加函数的2个核心用途(用例子说话)
你不用先纠结复杂概念,先看它能解决什么实际问题,就懂为什么需要它了。
用途1:强制LLM输出“结构化数据”,避免格式乱
比如你想让LLM生成笑话,但必须拆成“开头(setup)”和“笑点(punchline)”两部分,方便后续存数据库或排版展示。
如果只用提示词说“输出JSON格式”,LLM偶尔会漏括号、少字段;但用“附加函数”定义好结构后,LLM会严格按这个结构输出,几乎不会错。
示例里的“joke函数”就是干这个的:
# 1. 定义函数规则:告诉LLM“笑话要包含setup和punchline两个字段”
functions = [{"name": "joke", # 函数名(给LLM看的标识)"description": "生成一个笑话", # 告诉LLM这个函数的用途"parameters": { # 强制输出的结构(类似“表单模板”)"type": "object","properties": {"setup": {"type": "string", "description": "笑话的开头,埋梗部分"},"punchline": {"type": "string", "description": "笑话的结尾,抖梗部分"}},"required": ["setup", "punchline"] # 必须包含这两个字段,不能少}}
]# 2. 绑定函数到模型:让LLM按这个规则输出
chain = prompt | model.bind(function_call={"name": "joke"}, functions=functions)
# 3. 解析结果:直接拿到Python字典(不用自己处理字符串转JSON)
result = chain | JsonOutputFunctionsParser()
print(result) # 输出:{'setup': '为什么熊不穿鞋子?', 'punchline': '因为它们有熊脚!'}
用途2:让LLM“调用外部工具”,实现落地功能
比如你想让LLM帮用户查天气——LLM自己不会联网查,但它能通过“附加函数”知道“可以调用查天气的API”,并按API要求输出参数(比如城市、日期),后续程序再用这些参数去调用真实API,最后把结果返回给用户。
举个简化的“查天气”例子,你就能懂流程:
# 1. 定义“查天气”的函数规则(告诉LLM要传哪些参数给API)
weather_functions = [{"name": "get_weather","description": "查询指定城市的天气","parameters": {"type": "object","properties": {"city": {"type": "string", "description": "城市名称,比如北京、上海"},"date": {"type": "string", "description": "查询日期,格式YYYY-MM-DD"}},"required": ["city", "date"]}}
]# 2. 让LLM根据用户问题,输出调用这个函数的参数
prompt = ChatPromptTemplate.from_template("根据用户问题,生成调用查天气函数的参数:{question}")
chain = (prompt| model.bind(function_call={"name": "get_weather"}, functions=weather_functions)| JsonOutputFunctionsParser() # 解析出参数字典
)# 3. 调用链:用户问“查北京2024-10-20的天气”
params = chain.invoke({"question": "查北京2024-10-20的天气"})
print(params) # 输出:{'city': '北京', 'date': '2024-10-20'}# 4. 后续步骤:用这个params调用真实的天气API(比如调用高德/百度天气接口)
# (这里省略真实API调用代码,核心是LLM帮你生成了API需要的参数)
二、怎么应用?3步走,从0到1落地
你不用一开始搞复杂场景,先按这3步做个“结构化输出”的小例子,上手后再扩展到工具调用。
步骤1:定义“函数规则”(核心是写清楚“输出什么结构”)
不管是生成数据还是调用工具,第一步都要写 functions
列表——这是给LLM看的“说明书”,必须明确3个信息:
name
:函数名(随便起,但要和后续绑定的一致);description
:函数用途(让LLM知道什么时候该用这个函数);parameters
:输出结构(必填字段、字段类型、含义,越详细LLM越不容易错)。
比如你想让LLM提取“用户反馈里的问题类型和联系方式”,函数规则可以这么写:
feedback_functions = [{"name": "extract_feedback_info","description": "从用户反馈中提取问题类型和联系方式","parameters": {"type": "object","properties": {"problem_type": {"type": "string","enum": ["登录问题", "支付问题", "功能bug", "其他"], # 限定选项,避免LLM乱分类"description": "用户反馈的问题类型,只能选列表里的选项"},"contact": {"type": "string","description": "用户留下的联系方式,比如手机号、邮箱"}},"required": ["problem_type", "contact"]}}
]
步骤2:绑定函数到模型,搭链
用 model.bind(functions=函数规则, function_call={"name": 函数名})
告诉模型:“必须按这个函数的规则输出”,再串联提示和解析器。
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser# 1. 初始化模型和提示
model = ChatOpenAI()
prompt = ChatPromptTemplate.from_template("分析用户反馈,提取信息:{feedback}")# 2. 搭链:提示 → 绑定函数 → 解析结果
chain = (prompt| model.bind(functions=feedback_functions, # 传入函数规则function_call={"name": "extract_feedback_info"} # 指定用这个函数)| JsonOutputFunctionsParser() # 解析成Python字典
)
步骤3:调用链,用结果
直接传入用户反馈,就能拿到结构化数据,后续可以存数据库、自动分配工单等,不用手动处理文本。
# 调用链:用户反馈内容
result = chain.invoke({"feedback": "我登录不上账号,总是提示密码错误,我的手机号是13800138000"
})print(result)
# 输出:{'problem_type': '登录问题', 'contact': '13800138000'}# 后续应用:比如用contact给用户发密码重置链接,按problem_type分配给登录模块的客服
三、常见疑问:为什么不用“提示词让LLM输出JSON”?
很多人一开始会想:“我直接让LLM输出JSON不就行了,为啥要搞附加函数?”
核心区别在**“稳定性”**:
- 普通提示词:LLM偶尔会漏写引号、少字段(比如用户反馈里没联系方式,LLM可能直接不输出contact字段),后续程序会报错;
- 附加函数:LLM会严格按
parameters
里的规则输出——就算没联系方式,也可能返回{"contact": "未提供"}
(不会漏字段),因为函数规则里明确了“required”和结构,约束更强。
结尾:帮你整理“附加函数应用包”
为了让你快速落地,我可以整理一份包含3个实用场景的完整代码包:① 结构化提取用户反馈、② 生成商品信息卡片(电商用)、③ 调用天气API的参数生成,每个例子都带注释和后续应用建议。需要我帮你做这个吗?