从零构建Agent:解锁Function Call的底层逻辑与实践

深入理解Agent:从0实现Function Call

一、Agent与Function Call的底层逻辑

Agent系统的核心在于自主决策与工具调用能力,而Function Call正是实现这一能力的关键技术。其本质是让Agent能够动态识别任务需求,调用外部函数或API完成复杂操作。例如,在客服场景中,Agent需根据用户问题自动选择知识库查询、工单创建或人工转接等函数。

1.1 Function Call的技术定位

  • 决策层:通过LLM(大语言模型)解析用户意图,生成可执行的函数签名
  • 执行层:将函数参数转换为具体调用,处理异步响应与错误重试
  • 反馈层:将执行结果转化为自然语言反馈,形成闭环

典型技术栈包括:

  1. # 伪代码示例:Function Call决策流程
  2. def decide_function(user_input):
  3. intent = llm_analyze(user_input) # 意图识别
  4. if intent == "query_order":
  5. return {
  6. "function": "get_order_status",
  7. "params": {"order_id": extract_order_id(user_input)}
  8. }
  9. elif intent == "cancel_order":
  10. return {"function": "cancel_order", "params": {...}}

1.2 为什么需要自定义实现

现有框架(如LangChain、LlamaIndex)虽提供基础工具调用,但存在三大局限:

  1. 黑盒调用:无法深度定制参数校验逻辑
  2. 性能瓶颈:复杂场景下工具链调度效率低
  3. 安全风险:直接暴露内部API缺乏权限控制

二、从0实现Function Call的完整路径

2.1 架构设计三要素

  1. 函数注册中心:维护可用函数元数据(名称、参数、权限)

    1. class FunctionRegistry:
    2. def __init__(self):
    3. self.functions = {}
    4. def register(self, name, func, schema):
    5. self.functions[name] = {
    6. "handler": func,
    7. "schema": schema # 参数校验规则
    8. }
  2. 参数解析器:将自然语言转换为结构化参数

    • 使用正则表达式提取关键信息
    • 结合LLM进行模糊匹配(如”昨天”→具体日期)
  3. 执行控制器:管理异步调用与超时机制

    1. async def execute_function(registry, call_spec):
    2. func_meta = registry.functions.get(call_spec["name"])
    3. if not func_meta:
    4. raise ValueError("Function not found")
    5. # 参数校验
    6. validated_params = validate_params(
    7. call_spec["params"],
    8. func_meta["schema"]
    9. )
    10. # 异步执行
    11. try:
    12. result = await asyncio.wait_for(
    13. func_meta["handler"](**validated_params),
    14. timeout=10.0
    15. )
    16. return format_response(result)
    17. except Exception as e:
    18. return handle_error(e)

2.2 核心实现步骤

步骤1:定义函数契约

  1. {
  2. "get_weather": {
  3. "description": "查询天气",
  4. "parameters": {
  5. "type": "object",
  6. "properties": {
  7. "city": {"type": "string"},
  8. "date": {"type": "string", "format": "date"}
  9. },
  10. "required": ["city"]
  11. }
  12. }
  13. }

步骤2:实现参数绑定

  1. def bind_parameters(raw_input, function_schema):
  2. # 1. 使用LLM提取候选参数
  3. llm_output = llm_extract(raw_input, function_schema)
  4. # 2. 类型转换与校验
  5. validated = {}
  6. for param, value in llm_output.items():
  7. schema = function_schema["parameters"]["properties"][param]
  8. if schema["type"] == "number":
  9. validated[param] = float(value)
  10. elif schema["type"] == "boolean":
  11. validated[param] = value.lower() in ("true", "1")
  12. else:
  13. validated[param] = str(value)
  14. return validated

步骤3:构建执行沙箱

  • 使用asyncio实现并发控制
  • 通过contextvars传递上下文(如用户身份)
  • 实现熔断机制(如连续3次失败则暂停调用)

三、高级场景与优化实践

3.1 动态函数发现

在电商场景中,商品查询函数可能随业务扩展而变化。解决方案:

  1. class DynamicRegistry(FunctionRegistry):
  2. def discover_functions(self, api_gateway):
  3. # 从API网关动态加载函数
  4. services = api_gateway.list_services()
  5. for service in services:
  6. self.register(
  7. service["name"],
  8. service["handler"],
  9. service["schema"]
  10. )

3.2 多模态参数处理

处理包含图片/语音的调用请求:

  1. def handle_multimodal(input_data):
  2. if "image" in input_data:
  3. # 调用OCR服务提取文本
  4. text = ocr_service.extract(input_data["image"])
  5. return bind_parameters(text, current_schema)
  6. elif "audio" in input_data:
  7. # 语音转文本后处理
  8. ...

3.3 性能优化方案

  1. 缓存层:对频繁调用的函数(如汇率查询)实现结果缓存
  2. 批处理:合并同类函数调用(如批量查询订单状态)
  3. 服务网格:使用gRPC替代REST提升调用效率

四、安全与可靠性设计

4.1 权限控制体系

  1. class FunctionGatekeeper:
  2. def __init__(self, auth_service):
  3. self.auth = auth_service
  4. def check_permission(self, user, function_name):
  5. # 检查用户是否有调用权限
  6. required_role = FUNCTION_ROLE_MAP.get(function_name)
  7. return self.auth.has_role(user, required_role)

4.2 异常处理机制

  • 实现分级重试策略(网络错误重试3次,业务错误不重试)
  • 记录完整的调用链用于问题排查
  • 提供优雅的降级方案(如返回静态提示而非报错)

五、完整代码示例

  1. import asyncio
  2. from typing import Dict, Any
  3. class AdvancedFunctionCaller:
  4. def __init__(self, registry, gatekeeper):
  5. self.registry = registry
  6. self.gatekeeper = gatekeeper
  7. async def call(self, user: Dict, function_name: str, raw_params: Dict) -> Dict:
  8. # 1. 权限校验
  9. if not self.gatekeeper.check_permission(user, function_name):
  10. return {"error": "Permission denied"}
  11. # 2. 获取函数元数据
  12. func_meta = self.registry.get(function_name)
  13. if not func_meta:
  14. return {"error": "Function not found"}
  15. # 3. 参数绑定与校验
  16. try:
  17. params = self._bind_params(raw_params, func_meta["schema"])
  18. except ValueError as e:
  19. return {"error": f"Invalid parameters: {str(e)}"}
  20. # 4. 执行调用
  21. try:
  22. result = await asyncio.wait_for(
  23. func_meta["handler"](**params),
  24. timeout=func_meta.get("timeout", 5.0)
  25. )
  26. return {"result": result}
  27. except asyncio.TimeoutError:
  28. return {"error": "Function call timed out"}
  29. except Exception as e:
  30. return {"error": f"Function failed: {str(e)}"}
  31. def _bind_params(self, raw_params: Dict, schema: Dict) -> Dict:
  32. # 实现参数类型转换、必填校验等逻辑
  33. validated = {}
  34. for param, value in raw_params.items():
  35. if param not in schema["properties"]:
  36. continue # 或抛出异常
  37. prop_schema = schema["properties"][param]
  38. if "type" in prop_schema:
  39. if prop_schema["type"] == "number":
  40. validated[param] = float(value)
  41. elif prop_schema["type"] == "integer":
  42. validated[param] = int(value)
  43. else:
  44. validated[param] = str(value)
  45. # 检查必填参数
  46. missing = [p for p in schema["required"] if p not in validated]
  47. if missing:
  48. raise ValueError(f"Missing required parameters: {', '.join(missing)}")
  49. return validated

六、部署与监控建议

  1. 指标收集

    • 函数调用成功率
    • 平均响应时间
    • 参数绑定错误率
  2. 日志规范

    1. {
    2. "timestamp": "2023-07-20T14:30:00Z",
    3. "function": "get_product_info",
    4. "user_id": "user_123",
    5. "params": {"product_id": "P1001"},
    6. "status": "success",
    7. "duration_ms": 452
    8. }
  3. 告警策略

    • 连续5次调用失败触发告警
    • 平均响应时间超过阈值时缩容

通过本文的完整实现方案,开发者可以构建出既灵活又可靠的Function Call系统,为Agent赋予真正的工具使用能力。实际开发中建议先实现核心调用流程,再逐步完善安全、监控等周边功能。