Function Calling 的工程化团队实践:文档、测试和监控的标准

📅 2026/7/26 20:14:08 👁️ 阅读次数 📝 编程学习
Function Calling 的工程化团队实践:文档、测试和监控的标准

Function Calling 的工程化团队实践:文档、测试和监控的标准

一、每个工程师写的 Tool Schema 都不一样,调试全靠猜

当团队从 1 个人扩展到 5 个人后,Function Calling 的工程质量问题集中爆发。A 把 Tool 的 description 写成"查询订单",B 写成"根据订单号查询用户的订单详情",C 写成"order query function"。同样功能的 Tool 有三种描述,LLM 调用 A 的 Tool 时成功率 85%,C 的只有 50%——因为描述太简略。

更糟的是,没有统一的测试标准。什么是"Function Calling 成功"?是 LLM 正确选择了 Tool,还是 Tool 返回了正确结果?团队对此没有共识。功能上线后,唯一知道"Function Calling 出问题了"的信息源是用户投诉。

二、Function Calling 的三项工程标准

三、落地方案

标准一:Tool 定义模板

// Tool 定义规范(必须遵循的模板) type ToolDefinition struct { // 命名: {verb}_{noun},全部小写下划线 // 示例: query_order, update_inventory, send_email Name string `json:"name"` // 描述模板(必须包含四个部分): // 1. What: 这个 Tool 做什么 // 2. When: 什么时候应该调用 // 3. When NOT: 什么时候不应该调用 // 4. Error: 可能的错误场景 Description string `json:"description"` // 参数定义 Parameters ToolParameters `json:"parameters"` } // ✅ 符合规范的 Tool 定义 var QueryOrderTool = ToolDefinition{ Name: "query_order", Description: `查询指定订单的详细信息(商品、金额、状态、物流)。 适用场景: - 用户询问特定订单的状态 - 客服需要查看订单详情 - 退款时需要确认订单信息 不适用场景: - 不应用于查询退款记录,请使用 query_refund - 不应用于修改订单,请使用 update_order - 不应用于批量查询,请使用 batch_query_orders 可能的错误场景: - 订单号不存在(返回 ORDER_NOT_FOUND) - 权限不足(返回 PERMISSION_DENIED)`, Parameters: ToolParameters{ Type: "object", Required: []string{"order_id"}, Properties: map[string]PropertyDefinition{ "order_id": { Type: "string", Description: "订单号,格式为 ORD-YYYYMMDD-XXXXX", Pattern: `^ORD-\d{8}-[A-Z0-9]{5}$`, Examples: []string{"ORD-20260701-ABC12"}, }, }, }, }

标准二:Function Calling 的测试体系

// ✅ Function Calling 的测试用例 func TestFunctionCallingIntegration(t *testing.T) { tests := []struct { name string userQuery string expectedTool string expectedParams map[string]interface{} shouldFail bool }{ { name: "订单查询-正常", userQuery: "帮我查一下订单 ORD-20260701-ABC12", expectedTool: "query_order", expectedParams: map[string]interface{}{ "order_id": "ORD-20260701-ABC12", }, }, { name: "订单查询-模糊查询", userQuery: "上周买的那件T恤到哪了", expectedTool: "query_order", shouldFail: true, // 无订单号,应该走其他逻辑 }, { name: "退款查询(应选择正确Tool)", userQuery: "ORD-20260701-ABC12 退款了吗", expectedTool: "query_refund", // 不是 query_order! }, { name: "越权检测", userQuery: "把所有订单都取消", expectedTool: "", // 应被安全拦截,不调用任何Tool shouldFail: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { // 1. 调用 LLM 生成 Tool Call toolCall := callLLMWithTools(tc.userQuery, allToolDefs) // 2. 校验 Tool 选择 if tc.expectedTool != "" { assert.Equal(t, tc.expectedTool, toolCall.ToolName, "Tool 选择不正确") } // 3. 校验参数 if tc.expectedParams != nil { for key, expectedVal := range tc.expectedParams { actualVal, ok := toolCall.Parameters[key] assert.True(t, ok, "缺少参数: %s", key) assert.Equal(t, expectedVal, actualVal, "参数 %s 的值不正确", key) } } }) } }

标准三:Function Calling 的监控指标

// Function Calling 专属 Prometheus 指标 var ( // Tool 调用总数(按 Tool 名称 + 结果分类) toolCallTotal = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "fc_tool_call_total", Help: "Function Calling Tool 调用总数", }, []string{"tool_name", "status"}, // status: success, params_error, select_error ) // Tool 调用延迟 toolCallDuration = promauto.NewHistogramVec( prometheus.HistogramOpts{ Name: "fc_tool_call_duration_seconds", Help: "Tool 调用延迟(含 LLM 推理 + Tool 执行)", Buckets: []float64{0.1, 0.5, 1, 2, 5, 10, 30}, }, []string{"tool_name"}, ) // Tool 参数错误类型分布 toolParamError = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "fc_tool_param_error_total", Help: "Tool 参数错误类型分布", }, []string{"tool_name", "error_type"}, // error_type: missing_required, type_mismatch, invalid_enum ) // LLM 重新生成次数(当 Tool Call 出错时让 LLM 重试) toolRetryCount = promauto.NewHistogram( prometheus.HistogramOpts{ Name: "fc_tool_call_retries", Help: "Tool Call 出错后重试次数分布", Buckets: []float64{0, 1, 2, 3, 5}, }, ) )

四、团队协作的分工

  • 后端工程师:负责 Tool 的实现和 API 封装
  • Prompt 工程师(或产品经理):负责 Tool 的 Description 和 Schema 定义(这比写代码更需要"语言表达能力")
  • 质量工程师:负责维护 Critical User Journey 的回归测试集(至少 20 条核心场景)
  • SRE:负责 Function Calling 的延迟和错误率监控

关键认知:Tool 的 Description 是"人和 LLM 之间的接口",它的质量和 API 文档一样重要。写 Description 不是写代码,是需要文字表达和场景理解能力的——建议由最熟悉用户场景的产品经理来写,由后端技术审核技术可行性。

五、总结

Function Calling 的工程化核心是三个标准:Tool 定义模板(命名规范 + 描述模板 + 参数 Schema),测试体系(单元测 Tool 选择 + 集成测端到端 + 回归测核心场景),和监控指标(调用成功率 + 参数错误率 + 延迟分布)。Profile 驱动优化——每周查看哪个 Tool 的"参数错误率"最高,优先改进它的 Description。最容易被忽视的是"描述中的不适用场景"——明确告诉 LLM"不要用这个 Tool 做什么"可以减少 30% 的误调用。