Function Calling 详解
一、核心概念
Function Calling 是 LLM(大语言模型)的一种能力:模型不仅能生成文本回答,还能判断何时需要调用外部函数,并生成结构化的函数调用参数,让外部代码执行实际操作。
sql
用户提问
│
▼
┌─────────────────────────────────────┐
│ LLM 推理 │
│ │
│ 1. 理解用户意图 │
│ 2. 判断是否需要调用工具/函数 │
│ 3. 如果需要 → 生成函数名 + 参数 │
│ 如果不需要 → 直接生成文本回答 │
└─────────────────────────────────────┘
│ │
▼ ▼
函数调用请求 文本回答
{"name": "get_weather", "今天北京晴天,
"args": {"city": "北京"}} 气温25度..."
│
▼
┌─────────────────────────────────────┐
│ 外部代码执行函数 │
│ result = get_weather(city="北京") │
│ → {"temp": 25, "condition": "晴"} │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 把函数结果喂回 LLM,生成最终回答 │
│ → "北京今天25度,晴天,适合出行" │
└─────────────────────────────────────┘
二、为什么需要 Function Calling
| 问题 | 没有 Function Calling | 有 Function Calling |
|---|---|---|
| 获取实时数据 | LLM 只能靠训练数据,无法获取实时信息 | 调用外部 API 获取最新数据 |
| 精确计算 | LLM 做数学计算容易出错 | 调用计算器函数,100% 准确 |
| 操作外部系统 | LLM 无法直接操作数据库、发邮件等 | 调用对应函数执行操作 |
| 结构化输出 | 从文本中提取结构化数据,容易格式错误 | 直接输出 JSON 格式参数 |
三、工作流程(四步)
Step 1:定义可用函数
python
# 定义一个函数的 schema(告诉 LLM 有什么工具可用)
tools = [
{
"type": "function",
"function": {
"name": "search_contract",
"description": "在合同库中搜索合同内容",
"parameters": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "搜索关键词"
},
"contract_type": {
"type": "string",
"enum": ["采购", "销售", "租赁", "服务"],
"description": "合同类型"
},
"date_range": {
"type": "object",
"properties": {
"start": {"type": "string", "description": "起始日期 YYYY-MM-DD"},
"end": {"type": "string", "description": "结束日期 YYYY-MM-DD"}
}
}
},
"required": ["keyword"]
}
}
},
{
"type": "function",
"function": {
"name": "check_risk",
"description": "对合同进行风险检查",
"parameters": {
"type": "object",
"properties": {
"contract_id": {"type": "string", "description": "合同ID"},
"risk_type": {
"type": "string",
"enum": ["付款风险", "违约风险", "合规风险"],
"description": "风险类型"
}
},
"required": ["contract_id", "risk_type"]
}
}
}
]
Step 2:用户提问,LLM 决定调用哪个函数
python
# 用户提问
user_message = "帮我查一下今年签的采购合同里,有没有付款条款风险的"
# 发给 LLM
response = llm.chat.completions.create(
model="deepseek-chat",
messages=[{"role": "user", "content": user_message}],
tools=tools, # 把可用函数列表传给 LLM
)
# LLM 返回的不是文本,而是函数调用请求
tool_call = response.choices[0].message.tool_calls[0]
LLM 返回的内容:
json
{
"name": "search_contract",
"arguments": {
"keyword": "付款条款",
"contract_type": "采购",
"date_range": {
"start": "2025-01-01",
"end": "2025-12-31"
}
}
}
Step 3:外部代码执行函数
python
# 实际执行函数
def search_contract(keyword, contract_type=None, date_range=None):
"""真实的业务逻辑:查数据库"""
query = "SELECT * FROM contracts WHERE content LIKE ?"
params = [f"%{keyword}%"]
if contract_type:
query += " AND type = ?"
params.append(contract_type)
if date_range:
query += " AND sign_date BETWEEN ? AND ?"
params.extend([date_range["start"], date_range["end"]])
results = db.execute(query, params)
return results
# 执行 LLM 要求的函数调用
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
# 动态调用
if function_name == "search_contract":
result = search_contract(**function_args)
# result = [{"id": "C001", "title": "采购合同-原材料", ...}, ...]
Step 4:把结果喂回 LLM,生成最终回答
python
# 把函数执行结果发给 LLM
follow_up_response = llm.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "user", "content": user_message},
{"role": "assistant", "tool_calls": [tool_call]},
{"role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result)}
],
tools=tools,
)
# LLM 可能继续调用 check_risk,或者直接生成回答
final_answer = follow_up_response.choices[0].message.content
# → "找到3份今年签订的采购合同。其中合同C001的付款条款存在风险,
# 付款周期为180天,建议缩短至90天..."
四、完整流程图
css
用户: "帮我查今年采购合同有没有付款风险"
│
▼
┌──────────────────────────────────────────────────────────┐
│ Round 1: LLM 分析意图 │
│ │
│ LLM: "需要先搜索合同,再检查风险" │
│ → 调用 search_contract(keyword="付款条款", type="采购") │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 执行 search_contract() → 返回 3 份合同 │
│ [{"id":"C001",...}, {"id":"C002",...}, {"id":"C003",...}]│
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Round 2: LLM 收到搜索结果,决定下一步 │
│ │
│ LLM: "找到3份合同,逐个检查风险" │
│ → 调用 check_risk(contract_id="C001", risk_type="付款风险")│
│ → 调用 check_risk(contract_id="C002", risk_type="付款风险")│
│ → 调用 check_risk(contract_id="C003", risk_type="付款风险")│
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 执行 3 次 check_risk() → 返回风险结果 │
│ C001: {"risk_level": "高", "reason": "付款周期180天"} │
│ C002: {"risk_level": "低", "reason": "付款正常"} │
│ C003: {"risk_level": "中", "reason": "缺少违约条款"} │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ Round 3: LLM 汇总所有结果,生成最终回答 │
│ │
│ "找到3份今年签订的采购合同: │
│ 1. 合同C001:付款风险高,付款周期180天,建议缩短至90天 │
│ 2. 合同C002:风险低,付款条款正常 │
│ 3. 合同C003:风险中,缺少违约条款,建议补充" │
└──────────────────────────────────────────────────────────┘
五、Function Calling vs RAG vs Agent
| 对比项 | Function Calling | RAG | Agent |
|---|---|---|---|
| 本质 | LLM 调用外部函数的能力 | 检索知识增强生成 | 自主决策+行动的 AI 系统 |
| 触发方式 | LLM 自主判断是否调用 | 每次都会检索 | 根据目标自主规划 |
| 作用 | 扩展 LLM 的能力边界 | 补充 LLM 的知识 | 让 LLM 能完成复杂任务 |
| 关系 | 是 Agent 的基础能力 | 是 Agent 的知识来源 | Function Calling + RAG 的上位概念 |
类比理解:
javascript
人(LLM)
├─ 脑子里的知识 → 训练数据
├─ 查资料(RAG)→ 检索外部知识库
├─ 用工具(Function Calling)→ 调用计算器、查数据库、发邮件
└─ 自主完成任务(Agent)→ 自己规划步骤,用工具+查资料完成目标
六、在合同质检系统中的应用
python
# 合同质检系统中的 Function 定义
tools = [
# 1. 检索相关法规
{
"name": "search_regulations",
"description": "检索与合同条款相关的法律法规",
"parameters": {
"clause_content": "合同条款内容",
"contract_type": "合同类型"
}
},
# 2. 查询历史案例
{
"name": "search_similar_cases",
"description": "查询类似合同的历史风险案例",
"parameters": {
"contract_summary": "合同摘要",
"risk_category": "风险类别"
}
},
# 3. 计算风险评分
{
"name": "calculate_risk_score",
"description": "根据多个维度计算合同风险评分",
"parameters": {
"payment_terms": "付款条款",
"breach_clauses": "违约条款",
"compliance_status": "合规状态"
}
},
# 4. 生成质检报告
{
"name": "generate_report",
"description": "生成合同质检报告",
"parameters": {
"contract_id": "合同ID",
"risk_items": "风险项列表"
}
}
]
实际执行流程:
css
法务人员: "检查这份采购合同的付款条款风险"
│
▼
LLM 决策:
1. 调用 search_regulations(条款="付款周期180天") → 找到《合同法》相关规定
2. 调用 search_similar_cases(类型="采购", 风险="付款") → 找到5个类似案例
3. 调用 calculate_risk_score(付款="180天", 违约="缺失", 合规="不达标") → 风险评分85
4. 调用 generate_report(合同ID="C001", 风险项=[...]) → 生成报告
最终输出: "这份合同风险评分85(高风险)。付款周期180天过长,
违反《合同法》建议。缺少违约条款,建议补充..."
七、LangChain 中的实现
python
from langchain.tools import Tool
from langchain.agents import create_tool_calling_agent
# 定义工具
@Tool
def search_contract(keyword: str, contract_type: str = None) -> list:
"""在合同库中搜索合同内容"""
return db.search(keyword, contract_type)
@Tool
def check_risk(contract_id: str, risk_type: str) -> dict:
"""对合同进行风险检查"""
return risk_engine.check(contract_id, risk_type)
# 创建 Agent(自动使用 Function Calling)
agent = create_tool_calling_agent(
llm=get_llm(),
tools=[search_contract, check_risk],
prompt=system_prompt,
)
# 执行
result = agent.invoke({
"input": "检查这份采购合同的付款条款风险"
})
八、面试回答总结
问:Function Calling 怎么理解?
答:
Function Calling 是 LLM 调用外部函数的能力。核心流程是:用户提问后,LLM 先理解意图,判断需要调用哪些函数,生成结构化的函数名和参数(JSON 格式),外部代码执行函数后把结果返回给 LLM,LLM 再基于结果生成最终回答。
举个例子,在合同质检系统中,用户说"检查这份合同的付款风险",LLM 会判断需要调用三个函数:先调用
search_regulations检索相关法规,再调用search_similar_cases查历史案例,最后调用calculate_risk_score计算风险评分。LLM 汇总三个函数的结果,生成完整的风险评估报告。Function Calling、RAG 和 Agent 的关系是:RAG 解决的是"LLM 知识不够"的问题,Function Calling 解决的是"LLM 不能行动"的问题,Agent 是把两者结合起来,让 LLM 能自主规划步骤、调用工具、完成复杂任务。Agent 的底层能力就是 Function Calling。