10、Function Calling日志检测

10、Function Calling日志检测

Function Calling 交互日志(使用自定义HttpClint记录请求日志)

之前讲解了什么是Function Calling:Function Calling 特性并不是指模型主动调用函数,而是指会根据用户意图和提供的函数列表选择合适的函数并返回调用函数及所需的参数。

那具体Function Calling 在 SK中的请求流程是怎样的,具体发送的数据包如何呢?我们通过自定义HttpClientIFunctionInvocationFilter 来实现请求日志的拦截和记录,以便于我们一窥究竟。

自定义HttpClientHandler

HttpClientHandler 简介

在.NET 中,HttpClientHandler 类用于配置 HttpClient 的行为,它提供了许多属性和方法来控制 HTTP 请求和响应的处理方式。以下是关于 HttpClientHandler 用法的总结:

  1. 创建 HttpClientHandler 实例
    可以直接实例化 HttpClientHandler 类,然后将其传递给 HttpClient 的构造函数,以便为 HttpClient 配置特定的行为。

    1
    2
    var handler = new HttpClientHandler();
    var client = new HttpClient(handler);
  2. 设置代理
    使用 Proxy 属性可以设置 HTTP 请求的代理服务器。

    1
    2
    3
    4
    5
    6
    var proxy = new WebProxy("http://proxy.example.com:8080");
    var handler = new HttpClientHandler
    {
    Proxy = proxy
    };
    var client = new HttpClient(handler);
  3. 处理证书验证
    当与使用 HTTPS 的服务器通信时,可以通过 ServerCertificateCustomValidationCallback 属性自定义证书验证逻辑。例如,在开发环境中可能需要忽略证书验证错误(不建议在生产环境中使用):

    1
    2
    3
    4
    5
    var handler = new HttpClientHandler
    {
    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
    };
    var client = new HttpClient(handler);

    或者编写更严格的自定义验证逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var handler = new HttpClientHandler
    {
    ServerCertificateCustomValidationCallback = (sender, cert, chain, errors) =>
    {
    // 自定义验证逻辑,返回 true 表示接受证书,false 表示拒绝
    return true;
    }
    };
    var client = new HttpClient(handler);
  4. 管理 Cookie
    HttpClientHandler 包含一个 CookieContainer 属性,用于管理与服务器之间的 Cookie。

    1
    2
    3
    4
    5
    6
    var cookieContainer = new CookieContainer();
    var handler = new HttpClientHandler
    {
    CookieContainer = cookieContainer
    };
    var client = new HttpClient(handler);

    这样,HttpClient 发送的请求会自动包含 CookieContainer 中的 Cookie,并且接收到的响应中的 Cookie 也会被存储到 CookieContainer 中。

  5. 处理重定向
    可以通过 AllowAutoRedirect 属性控制是否自动处理重定向。

    1
    2
    3
    4
    5
    var handler = new HttpClientHandler
    {
    AllowAutoRedirect = false
    };
    var client = new HttpClient(handler);

    如果设置为 false,当服务器返回重定向响应时,HttpClient 不会自动进行重定向,而是将重定向响应返回给调用者,由调用者决定如何处理。

  6. 设置连接超时
    使用 Timeout 属性可以设置请求的超时时间。

    1
    2
    3
    4
    5
    var handler = new HttpClientHandler
    {
    Timeout = TimeSpan.FromSeconds(30)
    };
    var client = new HttpClient(handler);

    上述代码将请求的超时时间设置为 30 秒,如果请求在 30 秒内没有完成,将抛出 TaskCanceledException

HttpClientHandler 提供了丰富的功能来定制 HttpClient 的行为,以满足不同的网络请求需求,在实际开发中,根据具体的应用场景合理配置 HttpClientHandler 可以提高应用程序的稳定性和性能。

定义 OpenAiHttpClientHandler

定义OpenAiHttpClientHandler 如下,并将以下代码定义至Config/OpenAiHttpClientHandler.cs,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Threading;
public class OpenAiHttpClientHandler(ILogger<OpenAiHttpClientHandler> logger): HttpClientHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var escapedString = await request.Content?.ReadAsStringAsync(cancellationToken)!;
var content = System.Text.RegularExpressions.Regex.Unescape(escapedString);

logger.LogInformation("Sending '{Request.Method}' to '{Request.Host}{Request.Path}' with content {Request.Content}",
request.Method,
request.RequestUri?.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped),
request.RequestUri!.PathAndQuery,
content);

var response = await base.SendAsync(request, cancellationToken);

var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);

logger.LogInformation(
"Received '{Response.StatusCodeInt} {Response.StatusCodeString}' with content {Response.Content}",
(int)response.StatusCode,
response.StatusCode,
responseContent
);

return response;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.Extensions.DependencyInjection;
using OpenAI;
using System.ClientModel;

// 引入交互式的内核命名空间,以便用户输入

using PolyglotKernel= Microsoft.DotNet.Interactive.Kernel;

var zhipuApiKey = await PolyglotKernel.GetInputAsync("请输入您的智谱API Key:");

// Create kernel builder
var builder = Kernel.CreateBuilder();

var zhipuEndpoint = new Uri("https://open.bigmodel.cn/api/paas/v4/");
OpenAIClientOptions clientOptions = new OpenAIClientOptions();
clientOptions.Endpoint = zhipuEndpoint;

// 创建自定义的OpenAI客户端

var httpClient = new HttpClient(new OpenAiHttpClientHandler(logger));

// Add OpenAI Chat completion
builder.AddOpenAIChatCompletion(
modelId: "glm-4-flash",
openAIClient: httpClient);

// Build kernel
var kernel = builder.Build();

var response = await kernel.InvokePromptAsync("智谱AI有哪些模型,请直接返回模型列表。");
response.Display();

测试Function Calling

准备工作

自定义IFunctionInvocationFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private sealed class FunctionCallLogFilter(ILogger<FunctionCallLogFilter> logger) : IFunctionInvocationFilter
{
public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next)
{
try
{
logger.LogInformation($"Invoking function {context.Function.PluginName}-{context.Function.Name} with parameters: {context.Arguments}");
// Try to invoke function
await next(context);
logger.LogInformation($"Function {context.Function.PluginName}-{context.Function.Name} invoked successfully with result: {context.Result}");

}
catch (Exception ex)
{
ex.Display();
context.Result = new FunctionResult(context.Result, "Function invocation failed. Please check...");
}
}
}

自定义Plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kernel.Plugins.Clear();
kernel.ImportPluginFromFunctions("HelperFunctions",
[
kernel.CreateFunctionFromMethod((string cityName) =>
cityName switch
{
"Boston" => "61 and rainy",
"London" => "55 and cloudy",
"Miami" => "80 and sunny",
"Paris" => "60 and rainy",
"Tokyo" => "50 and sunny",
"Sydney" => "75 and sunny",
"Tel Aviv" => "80 and sunny",
"Beijing" => throw new Exception("Weather data not available for Beijing."),
_ => "31 and snowing",
}, "GetWeatherForCity", "Gets the current weather for the specified city."),
]);

测试

会返回一堆logger信息,下面是经过格式化的信息

1. first request & response

Request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"tools": [
{
"function": {
"description": "Gets the current weather for the specified city.",
"name": "HelperFunctions-GetWeatherForCity",
"strict": false,
"parameters": {
"type": "object",
"required": [
"cityName"
],
"properties": {
"cityName": {
"type": "string"
}
}
}
},
"type": "function"
}
],
"messages": [
{
"role": "user",
"content": "What is the likely color of the sky in London today?"
}
],
"model": "gpt-4o",
"tool_choice": "auto"
}

Response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
"choices": [
{
"content_filter_results": {},
"finish_reason": "tool_calls",
"index": 0,
"logprobs": null,
"message": {
"content": null,
"refusal": null,
"role": "assistant",
"tool_calls": [
{
"function": {
"arguments": "{\"cityName\":\"London\"}",
"name": "HelperFunctions-GetWeatherForCity"
},
"id": "call_PkTX1ikZmc7qniXWA2pRN1iH",
"type": "function"
}
]
}
}
],
"created": 1740104819,
"id": "chatcmpl-B3D2hiZOFw9D219t2UpmrxQLGY54n",
"model": "gpt-4o-2024-08-06",
"object": "chat.completion",
"system_fingerprint": "fp_f3927aa00d"
}

2. second request & response

Request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
"tools": [
{
"function": {
"description": "Gets the current weather for the specified city.",
"name": "HelperFunctions-GetWeatherForCity",
"strict": false,
"parameters": {
"type": "object",
"required": [
"cityName"
],
"properties": {
"cityName": {
"type": "string"
}
}
}
},
"type": "function"
}
],
"messages": [
{
"role": "user",
"content": "What is the likely color of the sky in London today?"
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_PkTX1ikZmc7qniXWA2pRN1iH",
"function": {
"name": "HelperFunctions-GetWeatherForCity",
"arguments": "{"cityName":"London"}"
},
"type": "function"
}
]
},
{
"role": "tool",
"tool_call_id": "call_PkTX1ikZmc7qniXWA2pRN1iH",
"content": "55 and cloudy"
}
],
"model": "gpt-4o",
"tool_choice": "auto"
}

Response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"message": {
"content": "The likely color of the sky in London today is gray due to the cloudy weather.",
"refusal": null,
"role": "assistant"
}
}
],
"created": 1740104820,
"id": "chatcmpl-B3D2iSLmW1kqjYhTLu6qBgBxsLdE5",
"model": "gpt-4o-2024-08-06",
"object": "chat.completion",
"system_fingerprint": "fp_f3927aa00d"
}
作者

步步为营

发布于

2025-04-11

更新于

2025-04-11

许可协议