资讯动态

Function Calling实战:构建能自主调用工具的AI智能体

发布时间:2026/8/8 6:03:36 来源:尧图企业网站定制
1. 项目概述为什么我们需要一个“能动起来”的AI风控助手在支付风控这个领域干了十几年我最大的感受就是规则引擎和模型再强大也总有“慢半拍”的时候。新的欺诈手法层出不穷等我们分析完日志、写好规则、部署上线黑产可能已经换了好几套打法。传统的风控系统就像一个反应迟缓的巨人虽然力量强大但转身太慢。这也是为什么当大语言模型LLM出现时我立刻意识到它可能就是我们一直在寻找的那个能“动起来”的“副驾驶”。这个项目的核心就是解决传统风控的“静态”痛点。我们之前搭建的系统无论是基于规则的拦截还是机器学习模型的评分本质上都是“被动响应”。系统只能处理我们预先定义好的输入输出预设好的结果。而现实中的风控决策往往需要结合实时交易数据、用户历史行为、外部情报如IP风险库、设备指纹库以及业务人员的经验判断进行多轮、动态的推理。这恰恰是LLM的强项——理解复杂语境、进行逻辑推理。但是光有“大脑”LLM还不够。LLM本身只是一个语言模型它“知道”很多但“做”不了任何事情。它无法主动查询数据库无法调用风险评分接口更无法执行拦截或放行的操作。这就是Function Calling登场的原因。你可以把它理解为给这个聪明的“大脑”安装上了“手”和“脚”。通过Function Calling我们教会AI如何根据对话的上下文自主决定去调用哪个外部工具函数获取必要的信息然后基于这些信息做出更精准的判断或执行具体的动作。所以“从0到1搭建AI智能支付风控助手”的第三阶段主题定为“Function Calling — 让AI能动起来”其目标非常明确构建一个能够自主调用外部能力、完成复杂风控工作流的智能体Agent雏形。这不再是简单的问答机器人而是一个能真正参与风控决策流程的“智能助手”。2. 核心设计构建一个能“思考”且能“执行”的智能体框架要让AI在支付风控场景下“动起来”我们不能只考虑单个函数的调用而需要设计一个完整的智能体框架。这个框架需要解决几个核心问题AI如何理解当前该做什么如何从众多工具中选择最合适的一个调用工具得到结果后又如何进行下一步决策2.1 智能体的核心循环规划、执行、观察一个典型的基于Function Calling的智能体其工作流是一个循环。我们以处理一笔“高风险交易预警”为例规划PlanAI接收到用户请求或系统事件例如“有一笔来自新设备的高额转账交易请协助分析风险。” LLM首先需要理解这个任务的最终目标评估风险并给出处置建议然后将其分解为一系列可执行的子步骤。这一步完全由LLM的推理能力完成。执行ActLLM根据分解出的当前步骤判断是否需要调用外部函数。例如第一步可能是“查询该用户的 historical transaction behavior”。这时LLM会生成一个结构化的函数调用请求指明要调用的函数名如query_user_transaction_history和所需的参数如user_id: “12345”, time_range: “last_30_days”。观察Observe我们的系统接收到这个调用请求后实际去执行对应的函数——查询数据库。然后将查询结果例如“该用户近30天有50笔交易平均金额100元本次交易金额为50000元”返回给LLM。循环与总结LLM接收到观察结果将其作为新的上下文继续下一步的规划。下一步可能是“检查交易IP的地理位置是否异常”再次触发函数调用。如此循环直到LLM认为已经收集到足够的信息可以做出最终判断然后生成面向用户的自然语言结论和处置建议例如“该用户行为突变交易金额远超历史水平且IP位于高风险地区建议执行人工审核。”。这个“规划-执行-观察”的循环是智能体能够处理复杂任务的基础。Function Calling是实现“执行”环节的关键桥梁。2.2 工具函数的设计哲学原子化与高内聚为智能体设计工具即可以被调用的函数是项目成败的关键。这里有几个核心原则原子化每个函数应该只完成一件非常具体、明确的事情。例如get_user_profile获取用户画像、calculate_transaction_risk_score计算交易风险分、query_blacklist查询黑名单。避免设计“巨无霸”函数比如一个analyze_risk函数里把查询、计算、判断全做了。原子化的好处是让LLM更容易理解和调用也便于我们单独测试和维护每个功能。高内聚函数内部逻辑要紧凑与外部系统的交互要清晰。例如calculate_transaction_risk_score函数内部可以封装对传统机器学习模型服务如XGBoost模型的调用或者集成几家第三方风控产品的API。对LLM来说它只需要提供交易数据作为参数就能得到一个风险分数无需关心内部复杂的模型逻辑。清晰的描述与参数定义这是Function Calling能工作的前提。我们在给LLM定义函数时必须提供极其清晰的自然语言描述和严格的参数JSON Schema。例如对于query_transaction函数描述要写“根据交易ID或用户ID和时间范围查询交易记录”参数要明确定义transaction_id可选字符串、user_id可选字符串、start_time必选时间戳等。LLM正是根据这些描述来决定在什么情况下调用哪个函数。实操心得描述即咒语早期我们犯过一个错误把函数描述写得太技术化比如“执行一个SQL查询以获取交易数据”。结果LLM经常在不需要查数据库的时候也调用这个函数。后来我们把描述改为更业务导向的“当需要了解一个用户过去的交易情况以评估当前交易是否异常时使用此工具。” 效果立竿见影。给LLM的函数描述更像是定义它的“思维触发器”要用它完成任务时的“场景语言”来写而不是用开发者的“实现语言”。3. 技术实现基于主流LLM API的Function Calling实战目前主流的LLM API服务如OpenAI GPT-4o/GPT-4 Turbo、Google Gemini、Anthropic Claude、国内DeepSeek、智谱GLM等都提供了对Function Calling的原生支持。虽然底层实现各有差异但核心流程大同小异。这里我以OpenAI的API格式为例拆解整个实现过程其他平台可以举一反三。3.1 定义你的工具包首先我们需要将风控领域常用的能力封装成一个个函数并按照API要求格式化成工具列表。以下是一个简化的示例# 风控工具函数定义 def query_user_behavior(user_id: str, days: int 30) - dict: 查询指定用户在过去一段时间内的行为摘要包括登录次数、交易频率、常用设备等。 # 实际实现调用用户行为分析微服务或查询数仓 # 模拟返回 return { “user_id”: user_id, “login_count_last_30d”: 45, “transaction_count_last_30d”: 25, “avg_transaction_amount”: 150.0, “common_device”: [“iPhone13”, “PC-Windows”] } def get_ip_reputation(ip_address: str) - dict: 查询IP地址的信誉评分包括是否代理、数据中心、历史欺诈关联等。 # 实际实现调用第三方IP情报API如MaxMind, IP2Location或内部风险库 return { “ip”: ip_address, “is_proxy”: False, “is_datacenter”: True, “risk_score”: 85, # 0-100越高越危险 “location”: “新加坡” } def evaluate_transaction_risk(transaction_data: dict) - dict: 使用风控模型对交易进行风险评估返回风险等级和分数。 # 实际实现将交易特征送入已部署的XGBoost/LightGBM模型服务 features extract_features(transaction_data) risk_score risk_model.predict(features)[0] risk_level “HIGH” if risk_score 80 else “MEDIUM” if risk_score 50 else “LOW” return {“risk_score”: risk_score, “risk_level”: risk_level} # 将函数描述转化为LLM可识别的工具定义 tools [ { “type”: “function”, “function”: { “name”: “query_user_behavior”, “description”: “当需要评估用户当前行为是否偏离其历史常态时调用此函数。获取用户近期的活跃度和交易模式。”, “parameters”: { “type”: “object”, “properties”: { “user_id”: {“type”: “string”, “description”: “用户的唯一标识符”}, “days”: {“type”: “integer”, “description”: “回溯查询的天数默认30天”} }, “required”: [“user_id”] } } }, { “type”: “function”, “function”: { “name”: “get_ip_reputation”, “description”: “当交易来源的IP地址可疑需要判断其是否为代理、数据中心或来自高风险地区时调用。”, “parameters”: { “type”: “object”, “properties”: { “ip_address”: {“type”: “string”, “description”: “需要查询的IP地址”} }, “required”: [“ip_address”] } } }, { “type”: “function”, “function”: { “name”: “evaluate_transaction_risk”, “description”: “当收集到一笔交易的核心信息金额、用户、设备、IP等后调用此函数获得专业的风险评分和等级。这是最终决策的核心依据之一。”, “parameters”: { “type”: “object”, “properties”: { “transaction_data”: { “type”: “object”, “description”: “交易数据对象包含amount, user_id, device_id, ip等字段”, “properties”: {…} # 详细定义每个字段 } }, “required”: [“transaction_data”] } } } ]3.2 与LLM的交互循环有了工具定义下一步就是实现与LLM的交互。核心在于处理LLM返回的tool_calls。import openai import json # 初始化OpenAI客户端示例 client openai.OpenAI(api_key“your_api_key”) def run_risk_assistant_agent(user_query: str, max_turns: int 5): 运行风控助手智能体 messages [{“role”: “user”, “content”: user_query}] for turn in range(max_turns): # 1. 调用LLM传入对话历史和工具定义 response client.chat.completions.create( model“gpt-4o”, # 或 gpt-4-turbo messagesmessages, toolstools, tool_choice“auto”, # 让模型自行决定是否及如何调用工具 ) message response.choices[0].message messages.append(message) # 将LLM的回复加入历史 # 2. 检查LLM是否想要调用工具 if not message.tool_calls: # 如果没有工具调用说明LLM已经可以给出最终答案 return message.content # 3. 处理每一个工具调用请求 for tool_call in message.tool_calls: function_name tool_call.function.name function_args json.loads(tool_call.function.arguments) # 4. 根据函数名找到本地对应的函数并执行 if function_name “query_user_behavior”: function_to_call query_user_behavior elif function_name “get_ip_reputation”: function_to_call get_ip_reputation elif function_name “evaluate_transaction_risk”: function_to_call evaluate_transaction_risk else: # 处理未知函数调用 result {“error”: f“Unknown function {function_name}”} continue # 执行实际函数 try: function_response function_to_call(**function_args) except Exception as e: function_response {“error”: str(e)} # 5. 将工具执行结果作为新的消息追加到对话历史中 messages.append({ “role”: “tool”, “tool_call_id”: tool_call.id, “content”: json.dumps(function_response, ensure_asciiFalse) }) # 如果达到最大轮次仍未得出最终结论 return “风险评估超时建议转入人工审核流程。” # 使用示例 user_query “帮我分析一下用户U123456刚刚发起的这笔50000元转账交易的风险付款IP是192.168.1.100。” final_answer run_risk_assistant_agent(user_query) print(final_answer)在这个循环中LLM扮演了“指挥官”的角色它根据对话的进展决定下一步需要什么信息调用哪个工具。我们的系统则扮演了“侦察兵”和“执行者”负责执行具体任务并将结果回报给“指挥官”。通过多轮交互最终形成一个基于充分信息的风险决策。3.3 关键参数与配置解析在实际调用LLM API时有几个参数对Function Calling的效果影响巨大tool_choice这个参数控制LLM调用工具的主动性。设为“auto”默认时由模型自行决定是否调用。设为“none”时模型不会调用任何工具即使你提供了工具列表。你还可以强制指定一个工具如{“type”: “function”, “function”: {“name”: “query_user_behavior”}}让模型必须使用该工具。在风控场景的初期我建议使用“auto”让模型自由发挥这有助于我们发现它自然的决策逻辑。temperature温度参数控制输出的随机性。对于需要严谨、可重复决策的风控任务必须将temperature设置为0或一个非常接近0的值如0.1。高温度会导致LLM在是否调用工具、调用哪个工具、以及生成参数时出现随机性这在生产环境中是灾难性的。我们需要的是稳定、可靠的决策流。max_tokens/max_completion_tokens需要设置得足够大以容纳可能的多轮工具调用和复杂的最终结论。特别是当工具返回的结果数据量较大时如一份详细的用户行为报告LLM需要足够的上下文窗口来消化这些信息。根据模型上下文长度合理设置例如GPT-4 Turbo可以设置到4096或更多。注意事项成本与延迟Function Calling虽然强大但每一次工具调用都意味着多一轮的LLM API请求。在复杂任务中可能会产生5-10轮甚至更多的交互。这会带来两个直接影响API调用成本成倍增加和任务总耗时显著变长网络往返时间叠加。在设计工具时要权衡“原子化”和“效率”。有时将两个高度相关、总是被连续调用的查询合并成一个“组合查询”函数是更经济的选择。例如一个get_user_and_ip_context函数同时返回用户行为和IP信誉可能比分开调用两次更高效。4. 进阶架构从单次调用到工作流引擎当智能体需要处理的任务越来越复杂时简单的循环可能不够用。例如一个完整的“可疑交易调查”任务可能涉及查询用户、查询IP、查询关联账户、计算风险分、生成调查报告等多个步骤并且某些步骤可能需要根据前序结果进行条件分支例如只有高风险IP才去查询更详细的黑名单情报。这时我们需要引入更强大的编排框架。目前社区有两个主流方向4.1 使用LangChain/GPTs等高级框架像LangChain、LangGraph、Dify、微软Autogen这类框架提供了更高层级的抽象。它们内置了智能体Agent、工具Tool、记忆Memory等概念并提供了多种代理执行模式如Plan-and-Execute, ReAct。以LangChain为例构建风控智能体变得非常声明式from langchain.agents import initialize_agent, AgentType from langchain.tools import Tool from langchain_openai import ChatOpenAI # 1. 将我们的函数包装成LangChain Tool user_behavior_tool Tool( name“QueryUserBehavior”, funcquery_user_behavior, description“查询用户历史行为模式。” ) ip_reputation_tool Tool( name“GetIPReputation”, funcget_ip_reputation, description“查询IP地址风险情报。” ) # 2. 初始化LLM llm ChatOpenAI(model“gpt-4o”, temperature0) # 3. 创建并运行智能体 agent initialize_agent( tools[user_behavior_tool, ip_reputation_tool], llmllm, agentAgentType.ZERO_SHOT_REACT_DESCRIPTION, # 使用ReAct推理模式 verboseTrue, # 打印详细执行过程便于调试 handle_parsing_errorsTrue # 优雅处理解析错误 ) result agent.run(“分析用户U123456从IP 192.168.1.100发起的50000元转账风险。”)使用框架的好处是省去了手动管理对话历史、解析工具调用的繁琐工作并且能利用框架提供的强大模式如ReAct鼓励LLM输出“Thought/Action/Observation”格式的推理链使其思考过程更透明。缺点是引入了额外的复杂性和学习成本并且在极端性能要求下可能不如手写循环灵活。4.2 自研轻量级工作流引擎对于追求极致控制和性能或者有特殊业务流程的团队自研一个轻量级的工作流引擎是值得考虑的。其核心思想是将风控决策流程本身“代码化”或“配置化”。我们可以定义一个JSON或YAML格式的“工作流模板”描述一个完整调查任务所需的步骤、步骤间的依赖关系、以及每个步骤对应的工具调用或LLM提示。# workflow_definition.yaml name: “deep_dive_risk_investigation” steps: - id: “step1_get_user_context” type: “tool_call” tool: “query_user_behavior” parameters: user_id: “{{input.user_id}}” days: 90 next_step: “step2_get_ip_context” - id: “step2_get_ip_context” type: “tool_call” tool: “get_ip_reputation” parameters: ip_address: “{{input.ip_address}}” next_step: “step3_llm_synthesize” - id: “step3_llm_synthesize” type: “llm_prompt” system_prompt: “你是一个风控专家。请基于以下用户行为和IP情报给出初步风险判断。” user_prompt_template: | 用户 {{input.user_id}} 的行为摘要{{steps.step1_get_user_context.output}} 交易IP情报{{steps.step2_get_ip_context.output}} 请分析是否存在异常点。 next_step: “step4_conditional_check” - id: “step4_conditional_check” type: “condition” condition: “{{steps.step3_llm_synthesize.output.risk_indication}} ‘HIGH’” if_true: “step5_deep_dive_query” if_false: “step6_generate_report” - id: “step5_deep_dive_query” type: “tool_call” tool: “query_associated_accounts” parameters: user_id: “{{input.user_id}}” next_step: “step6_generate_report” - id: “step6_generate_report” type: “llm_prompt” # ... 生成最终报告 ...然后我们编写一个引擎来解析这个模板按顺序或并行执行各个步骤管理步骤间的数据传递通过类似{{steps.step1.output}}的模板变量并根据条件判断跳转到不同的分支。LLM的Function Calling在这里被降级为工作流中某个特定步骤type: “tool_call”的执行器。这种方式的优势是流程清晰、可追溯、易于测试和运维。整个风控决策链路变成了一个可配置、可监控的管道。缺点是前期开发工作量较大且流程的灵活性依赖于模板的设计难以处理完全开放式的探索性任务。5. 生产环境落地稳定性、安全性与可观测性将基于Function Calling的AI助手推向生产环境远不止写好代码那么简单。在真实的支付风控场景中稳定性、安全性和可观测性缺一不可。5.1 稳定性保障重试、降级与超时重试策略LLM API调用和工具调用都可能因网络波动、服务暂时不可用而失败。必须为所有外部调用包括对LLM提供商和内部工具服务实现指数退避的重试机制。例如第一次失败后等待1秒重试第二次失败后等待2秒以此类推通常设置最大重试次数为3次。降级方案当LLM服务完全不可用或Function Calling解析连续失败时系统必须有“B计划”。最简单的降级是回退到基于规则的预定义流程或者将任务路由给人工审核队列。更优雅的方式是准备一个轻量级的本地模型如经过精调的较小参数模型来接管核心的判断逻辑虽然能力有下降但能保证服务不中断。严格超时控制为整个智能体任务以及每一个工具调用设置严格的超时时间。例如单次LLM API调用超时设为10秒单个工具调用如数据库查询超时设为5秒整个风控助手的决策流程总超时设为30秒。一旦超时立即终止并返回“处理超时建议人工介入”的结论避免一个慢请求拖垮整个系统。5.2 安全性加固输入校验、权限与审计输入清洗与校验LLM生成的函数参数在传递给实际工具前必须进行严格的校验和清洗。例如对于user_id参数要检查其格式是否符合预期是否是数字或特定模式的字符串防止SQL注入或NoSQL注入攻击。对于days参数要检查其范围是否合理比如不能是负数或超过1000天。永远不要相信LLM的输出是安全的。最小权限原则每个被AI调用的工具函数其背后的服务或数据库账号应该遵循最小权限原则。例如用于查询用户行为的数据库账号应该只有特定表的只读权限绝对没有写入或删除权限。这样即使AI被恶意引导或参数被污染其破坏性也被限制在最低程度。完整的审计日志记录每一次智能体交互的全量日志至关重要。这包括原始用户查询。LLM每一轮的请求和响应特别是tool_calls的详细信息。每个工具调用的输入参数和执行结果。最终决策结论。 这些日志不仅用于事后复盘和模型优化更是安全审计和合规要求的必需品。当出现误判或争议时完整的日志链是排查问题的唯一依据。5.3 可观测性建设监控、评估与迭代核心指标监控建立仪表盘实时监控以下指标请求量、成功率、平均响应时间基础的健康度指标。工具调用分布统计各个工具被调用的频率了解AI最依赖哪些信息。任务轮次分布大部分任务在几轮内完成是否存在陷入死循环的“长尾任务”错误类型统计是LLM API错误多还是工具调用错误多错误码是什么效果评估体系风控的核心是效果。需要将AI助手的决策与最终的真实结果是否欺诈进行比对。可以定义如下指标采纳率业务人员最终采纳AI建议的比例。准确率/召回率在那些被AI判定为高风险且被采纳的交易中真正发生欺诈的比例精确率AI成功捕捉到的欺诈交易占所有欺诈交易的比例召回率。决策一致性对于相似特征的交易AI给出的风险判断是否稳定。持续迭代闭环基于监控和评估数据形成一个迭代闭环发现问题通过审计日志分析误判案例。归因分析是工具返回的信息不准是LLM的推理逻辑有误还是函数描述不够清晰优化改进补充新的工具、优化函数描述、在系统提示词System Prompt中增加针对此类案例的指导原则。AB测试将改进后的版本与旧版本进行小流量AB测试验证效果提升后再全量发布。6. 避坑指南与实战心得在项目推进过程中我们踩过不少坑也积累了一些宝贵的经验。6.1 常见问题与排查技巧问题现象可能原因排查与解决思路LLM拒绝调用任何工具直接给出猜测性答案。1. 函数描述不够清晰或与当前任务不相关。2. System Prompt中未明确要求其使用工具。3.tool_choice参数被误设为“none”。1. 用更业务化、场景化的语言重写函数描述。2. 在System Prompt中强调“你是一个风控助手必须通过调用我提供的工具来获取信息不得凭空猜测。”3. 检查API调用参数。LLM调用了错误的工具或生成的参数格式错误。1. 函数名或参数名歧义。2. 参数JSON Schema定义有误如类型错误、枚举值未列出。3. Temperature参数设置过高导致输出不稳定。1. 给函数起名要直观唯一如用query_risk_score而非get_data。2. 严格定义参数类型和约束使用“enum”列出所有可选值。3.将Temperature设为0或接近0的值。工具调用陷入无限循环。LLM在“规划-执行-观察”循环中无法得出最终结论不断要求获取新信息。1. 设置最大交互轮次如10轮作为安全阀。2. 优化System Prompt明确任务边界和结束条件例如“在收集到用户行为、IP情报和风险评分后你就拥有足够信息做出判断请直接给出结论。”3. 检查工具返回的结果是否清晰、格式是否便于LLM理解。处理速度慢用户体验差。1. 工具调用如外部API、数据库查询本身慢。2. 任务所需轮次多LLM API的往返延迟累积。1. 为所有工具调用实现缓存特别是对频繁查询且数据变化不快的如用户画像、IP地理信息。2. 考虑将多个高度相关的工具调用合并或使用并行调用如果工具间无依赖。3. 对于复杂任务可以考虑“异步执行通知”模式先快速返回“已受理正在分析”分析完成后再推送结果。6.2 至关重要的提示工程System Prompt系统提示词是智能体的“宪法”它定义了AI的角色、行为规范和任务边界。在风控场景下一个有效的System Prompt应该包含明确角色与目标“你是一个专业的支付风控分析助手。你的目标是准确、高效地识别交易风险并提供清晰的处置建议。”强调工具使用“你必须通过调用我提供的工具来获取事实信息绝对不允许基于内部知识进行猜测。在获得足够工具返回的信息前不要做出最终判断。”定义输出格式“你的最终输出应该是一个结构化的结论包括风险等级高/中/低、主要风险点列举1-3条、以及建议操作如放行、拦截、人工审核。”设定安全与合规边界“你不得生成或讨论任何与金融欺诈技术细节、系统安全漏洞相关的内容。如果用户询问超出风控分析范围的问题你应礼貌拒绝并引导回主题。”6.3 关于成本控制的思考Function Calling会显著增加Token消耗每轮交互都需要在消息历史中携带工具定义和之前的对话内容和API调用次数。控制成本的方法包括精简工具描述在保证清晰的前提下用最简洁的语言描述函数和参数。会话摘要对于超长对话可以定期将之前的对话历史总结成一段摘要然后清空历史只保留摘要和最近几轮对话大幅减少Token数。选择合适的模型对于工具调用和逻辑推理不一定非要使用最顶级的模型如GPT-4。像GPT-3.5-Turbo、Claude Haiku等模型在Function Calling上也有不错的表现且成本低、速度快。可以在非核心路径或对精度要求稍低的场景中使用它们。缓存LLM响应对于完全相同的输入用户查询上下文可以考虑缓存LLM的响应包括工具调用决策在一定时间内直接返回缓存结果。这在处理高频、重复性查询时非常有效。最后我想分享一点个人体会Function Calling确实让AI“能动起来”了但它并不是银弹。它最大的价值是将人类的业务逻辑体现在工具设计和提示词中与AI的推理判断能力深度融合。成功的AI风控助手背后一定是一支既懂风控业务、又懂AI技术的团队不断地打磨工具、优化提示、分析bad case。这个过程没有捷径但每解决一个实际问题带来的效率提升和风险降低都是实实在在的回报。从这个“Stage 3”开始你的AI项目才真正从“玩具”走向了“工具”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价