Function Calling理解

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。

相关推荐
玉宇夕落1 小时前
LangChain RAG Retriever 的学习
后端
用户298698530141 小时前
Python 一键导出 Excel 中的图表和形状为图片
后端·python·excel
用户8130077816371 小时前
PageHelper分页查询步骤
后端
是小李呀1 小时前
MyBatis、MyBatis-Plus 与全自动 ORM 的本质区别
后端
小堂子这厢有礼了1 小时前
Chet.Admin 权限模型:菜单级 + 按钮级 + 行级三层防护
前端·后端·信息可视化·前端框架·.net
SimonKing1 小时前
Spring Boot 4.0 + gRPC 极简集成:4步搞定,比REST还丝滑
java·后端·程序员
IT_陈寒2 小时前
Vue的这个响应式陷阱,让我加班到凌晨三点
前端·人工智能·后端
Wang's Blog2 小时前
Go-Zero基础入门5: 探究go-zero如何基于gRPC扩展客户端
开发语言·后端·golang·go-zero
小堂子这厢有礼了3 小时前
Chet.QuartzNet.UI v2.3.0 大更新!表格重构 + 系统配置 + 分析页全面升级!
前端·后端·ui·重构·c#·vue