You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dialogflow会话实体更新失败排查求助(PHP实现)

问题诊断与修复方案

从你的描述和日志来看,核心问题出在身份验证方式错误和HTTP请求方法不正确,这直接导致了400 Bad Request错误,会话实体根本没被正确更新。下面是具体的问题分析和修复步骤:

1. 最关键的错误:用私钥直接作为Bearer令牌

你代码里把GCP服务账号的私钥直接放到了Authorization: Bearer头里,这完全不符合Dialogflow v2 API的身份验证要求。Dialogflow需要的是基于服务账号私钥生成的OAuth 2.0访问令牌,而不是原始的私钥内容。

修复方法:生成合法的访问令牌

你需要通过JWT(JSON Web Token)流程生成临时访问令牌,有效期通常是3600秒。这里给你一个PHP实现的示例函数,用来生成符合要求的令牌:

function getDialogflowAccessToken($privateKeyPath, $clientEmail) {
    $now = time();
    $jwtPayload = [
        'iss' => $clientEmail,
        'sub' => $clientEmail,
        'aud' => 'https://dialogflow.googleapis.com/',
        'iat' => $now,
        'exp' => $now + 3600 // 1小时有效期
    ];

    // 加载私钥
    $privateKey = file_get_contents($privateKeyPath);
    $privateKeyResource = openssl_pkey_get_private($privateKey);

    // 生成JWT签名
    openssl_sign(json_encode($jwtPayload), $signature, $privateKeyResource, 'SHA256');
    $base64UrlPayload = base64UrlEncode(json_encode($jwtPayload));
    $base64UrlSignature = base64UrlEncode($signature);

    $jwt = $base64UrlPayload . '.' . $base64UrlSignature;

    // 请求获取访问令牌
    $ch = curl_init('https://oauth2.googleapis.com/token');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
        'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion' => $jwt
    ]));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = json_decode(curl_exec($ch), true);
    curl_close($ch);

    return $response['access_token'] ?? null;
}

// 辅助函数:Base64URL编码
function base64UrlEncode($data) {
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

使用这个函数时,你需要传入服务账号的私钥文件路径和客户端邮箱(都可以从GCP下载的服务账号JSON文件中找到):

// 替换为你的服务账号信息
$privateKeyPath = '/path/to/your/service-account-key.json';
$serviceAccountData = json_decode(file_get_contents($privateKeyPath), true);
$accessToken = getDialogflowAccessToken($privateKeyPath, $serviceAccountData['client_email']);

2. HTTP请求方法错误:应该用PATCH而不是POST

Dialogflow的projects.agent.sessions.entityTypes.patch接口要求使用PATCH HTTP方法,而你代码里设置了CURLOPT_POST = true(这是POST方法),这会导致API无法识别你的请求意图,返回400错误。

修复方法:修改curl的请求方法

把curl的请求方法改成PATCH:

// 替换原来的CURLOPT_POST设置
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');

3. 其他需要验证的细节

  • 会话ID格式:确保$request['session']的格式是正确的,应该是projects/{PROJECT_ID}/agent/sessions/{SESSION_ID},你的日志里显示的格式看起来是对的,但可以再确认。
  • 实体结构:你生成的实体数组看起来没问题,但可以检查$_SESSION['STORY']['VISIBLE_OBJECTS']是否正确加载了数据,比如通过日志确认$user_ent_objects的内容是否符合预期。
  • 错误日志:可以添加错误捕获,看看curl返回的具体错误信息,比如:
if(curl_errno($ch)) {
    file_put_contents($SITE_DIR . '/webhook/curl-error.txt', curl_error($ch));
} else {
    file_put_contents($SITE_DIR . '/webhook/curl-response.txt', $curl_response);
}

修正后的完整代码片段

把上面的修复点整合到你的代码中,大概是这样:

<?php 
$json = file_get_contents('php://input'); 
$request = json_decode($json, true); 
$df_action = $request['queryResult']['action']; 
$df_contexts = $request['queryResult']['outputContexts']; 
$df_parameters = $request['queryResult']['parameters']; 

// 加载服务账号信息
$privateKeyPath = '/path/to/your/service-account-key.json'; // 替换为你的路径
$serviceAccountData = json_decode(file_get_contents($privateKeyPath), true);

// 获取访问令牌
function getDialogflowAccessToken($privateKeyPath, $clientEmail) {
    $now = time();
    $jwtPayload = [
        'iss' => $clientEmail,
        'sub' => $clientEmail,
        'aud' => 'https://dialogflow.googleapis.com/',
        'iat' => $now,
        'exp' => $now + 3600
    ];

    $privateKey = file_get_contents($privateKeyPath);
    $privateKeyResource = openssl_pkey_get_private($privateKey);

    openssl_sign(json_encode($jwtPayload), $signature, $privateKeyResource, 'SHA256');
    $base64UrlPayload = base64UrlEncode(json_encode($jwtPayload));
    $base64UrlSignature = base64UrlEncode($signature);

    $jwt = $base64UrlPayload . '.' . $base64UrlSignature;

    $ch = curl_init('https://oauth2.googleapis.com/token');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
        'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion' => $jwt
    ]));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = json_decode(curl_exec($ch), true);
    curl_close($ch);

    return $response['access_token'] ?? null;
}

function base64UrlEncode($data) {
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

$accessToken = getDialogflowAccessToken($privateKeyPath, $serviceAccountData['client_email']);

// 准备实体数据
$user_ent_objects = [];
foreach($_SESSION['STORY']['VISIBLE_OBJECTS'] as $visible_object) { 
    $synonyms = array(); 
    $synonyms[] = $visible_object['name']; 
    if (strpos($visible_object['name'], ' ', 1)) { 
        $sub_objects = explode(' ', $visible_object['name']); 
        foreach($sub_objects as $sub_object) $synonyms[] = $sub_object; 
    } 
    $user_ent_objects[] = array('value'=>$visible_object['name'], 'synonyms'=>$synonyms); 
} 

$user_entity = array(
    'name'=>$request['session'] . '/entityTypes/visible_objects', 
    'entityOverrideMode'=>'ENTITY_OVERRIDE_MODE_OVERRIDE', 
    'entities'=>$user_ent_objects
); 

file_put_contents($SITE_DIR . '/webhook/user-entity-visible-objects.txt', json_encode($user_entity,JSON_PRETTY_PRINT)); 

$ch = curl_init('https://dialogflow.googleapis.com/v2/' . $request['session'] . '/entityTypes/visible_objects'); 
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); // 使用PATCH方法
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json', 
    'Authorization: Bearer ' . $accessToken // 使用生成的访问令牌
)); 
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($user_entity)); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 

$curl_response = curl_exec($ch); 

// 记录错误和响应
if(curl_errno($ch)) {
    file_put_contents($SITE_DIR . '/webhook/curl-error.txt', curl_error($ch));
} else {
    file_put_contents($SITE_DIR . '/webhook/curl-response.txt', $curl_response);
}

curl_close($ch);
?>

测试验证

修复后,重新测试:

  1. 检查curl-response.txt里的API响应,如果返回200状态码和更新后的实体数据,说明成功。
  2. 再测试用户查询,比如look on the test,应该能正确匹配到test tubes实体。

内容的提问来源于stack exchange,提问作者Derek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:17:25