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。

相关推荐
有梦不弃6 小时前
模块化单体架构设计方案:DDD + 六边形架构落地实践
后端·架构
江南十四行10 小时前
Spring框架核心(上)——IoC控制反转与DI依赖注入详解
java·后端·spring
IT_陈寒10 小时前
Redis缓存击穿把我坑惨了,原来这样设过期时间才靠谱
前端·人工智能·后端
妙码生花11 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十二):管理员权限检查中间件,AI 随意放行预闯大祸
前端·后端·go
创安电气研究所11 小时前
用 Node.js 将变频器运行数据接入 MQTT
后端
董员外11 小时前
RAG 系统进化论(八):Agentic RAG(智能体式 RAG),从回答问题到完成任务
人工智能·后端·设计模式
大勇前进12 小时前
告别 div 满天飞,HTML5 语义化标签真的有必要用吗?
后端
小满zs12 小时前
Go语言第六章(结构体)
后端·go
属于自己的天空12 小时前
每天省去重复 Prompt:我用 Skills 把 CRUD 生成标准化了
前端·后端