Function Calling 链路追踪:参数从 Prompt 到执行的全路径可视化

📅 2026/7/8 14:58:03 👁️ 阅读次数 📝 编程学习
Function Calling 链路追踪:参数从 Prompt 到执行的全路径可视化

Function Calling 链路追踪:参数从 Prompt 到执行的全路径可视化

一、当 Agent 告诉你"我已经调用了搜索",但实际没有

一个常见的场景:用户问 Agent 某个问题。
Agent 回复"我已查询了最新数据,结果是……"
但实际上,工具调用根本没有发生。
大模型凭空生成了结果,即幻觉。

排查这类问题极其困难。
你无法知道模型是否真的产生了 tool_call。
即便产生了,参数是否正确传递给了工具。
即便正确传递了,工具返回的结果是否被模型正确消费。
每一步都是黑盒,只能靠猜。

核心诉求很明确。
需要看到 Function Calling 的完整调用链路。
从用户输入 Prompt,到模型输出 tool_call 参数。
再到参数注入工具执行,最后结果回传给模型。

二、Function Calling 的请求-执行链路

Function Calling 的调用包含四个关键节点。
第一,Prompt 编译:将用户输入和工具 Schema 组装为模型输入。
第二,模型推理:模型决定调用哪个工具及参数。
第三,工具执行:解析参数,调用实际函数。
第四,结果注入:工具返回结果写入对话上下文。

sequenceDiagram participant U as 用户 participant P as Prompt 组装层 participant M as LLM participant T as 工具调度器 participant F as 实际函数 participant L as 追踪系统 U->>P: 发送问题 P->>P: 注入工具 Schema P->>L: 📝 Node1: Prompt 快照记录 P->>M: 发起推理请求 M->>L: 📝 Node2: 模型输出(含 tool_call) M-->>P: 返回 tool_call 决策 alt 需要调用工具 P->>T: 派发 tool_call T->>T: 参数校验 T->>L: 📝 Node3: 工具名 + 参数 T->>F: 执行函数 F-->>T: 返回结果 T->>L: 📝 Node4: 工具返回值 T-->>P: 注入结果到上下文 else 直接回复 M-->>U: 返回文本回复 end P->>L: 📝 Node5: 最终响应 P-->>U: 返回给用户

每个节点记录三类信息。
时间戳、输入输出快照、延迟耗时。
这些数据构成完整的调用链。

三、链路追踪的 Go 实现

package tracing import ( "context" "encoding/json" "fmt" "time" ) // TraceNode 追踪节点 type TraceNode struct { ID string `json:"id"` Type string `json:"type"` // prompt_compile, llm_call, tool_exec, result_inject Timestamp time.Time `json:"timestamp"` Input json.RawMessage `json:"input"` Output json.RawMessage `json:"output"` Duration time.Duration `json:"duration_ms"` Error string `json:"error,omitempty"` } // TraceChain 完整调用链 type TraceChain struct { TraceID string `json:"trace_id"` Nodes []TraceNode `json:"nodes"` StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` } // Tracer 链路追踪器 type Tracer struct { chain *TraceChain mu sync.Mutex } // NewTracer 创建追踪器 func NewTracer(traceID string) *Tracer { return &Tracer{ chain: &TraceChain{ TraceID: traceID, Nodes: make([]TraceNode, 0), StartTime: time.Now(), }, } } // RecordNode 记录一个调用节点,返回结束函数 func (t *Tracer) RecordNode(nodeType string, input interface{}) func(output interface{}, err error) { inputBytes, _ := json.Marshal(input) start := time.Now() node := TraceNode{ ID: fmt.Sprintf("%s-%d", nodeType, len(t.chain.Nodes)), Type: nodeType, Timestamp: start, Input: inputBytes, } return func(output interface{}, err error) { node.Duration = time.Since(start) if err != nil { node.Error = err.Error() } else if output != nil { node.Output, _ = json.Marshal(output) } t.mu.Lock() t.chain.Nodes = append(t.chain.Nodes, node) t.mu.Unlock() } } // Export 导出完整链路 func (t *Tracer) Export() *TraceChain { t.mu.Lock() defer t.mu.Unlock() t.chain.EndTime = time.Now() return t.chain }

在 Function Calling 流程中的使用。

func handleFunctionCall(ctx context.Context, prompt string, tools []Tool) (string, *TraceChain) { tracer := NewTracer(generateTraceID()) // Node 1: Prompt 编译 endPrompt := tracer.RecordNode("prompt_compile", map[string]interface{}{ "user_input": prompt, "tool_count": len(tools), }) compiledPrompt := buildPrompt(prompt, tools) endPrompt(compiledPrompt, nil) // Node 2: LLM 推理 endLLM := tracer.RecordNode("llm_call", map[string]interface{}{ "model": "gpt-4", "prompt_len": len(compiledPrompt), }) response, err := callLLM(ctx, compiledPrompt) if err != nil { endLLM(nil, fmt.Errorf("LLM 调用失败: %w", err)) return "", tracer.Export() } endLLM(response, nil) // Node 3: 工具执行 if response.ToolCall != nil { endTool := tracer.RecordNode("tool_exec", map[string]interface{}{ "tool_name": response.ToolCall.Name, "parameters": response.ToolCall.Args, }) result, execErr := executeTool(ctx, response.ToolCall) endTool(result, execErr) if execErr != nil { return fmt.Sprintf("工具执行失败: %v", execErr), tracer.Export() } } return response.Content, tracer.Export() }

四、追踪系统的边界与成本

全链路追踪带来存储和性能开销。
每个调用链约 2-5KB JSON 数据。
日均 10 万次调用产生约 500MB 追踪数据。
建议设置采样率,生产环境采样 10% 即可。

追踪系统不适合的场景:
延迟极度敏感的实时推理(增加 1-2ms 序列化开销);
本地开发的快速调试(直接用 print 更高效)。

隐私问题需注意。
用户 Prompt 可能包含敏感信息。
追踪数据中应脱敏处理或设置存储过期策略。

五、总结

Function Calling 的链路追踪让每个调用可观测。
通过在关键节点插入 Recorder,捕获输入、输出、耗时。
Tracer 模式比侵入式日志更结构化。
生产环境建议 10% 采样率,配合数据脱敏。
有了完整链路,排查幻觉和参数错误不再靠猜。