PAYATHON 2026

Google Assistant 中 Intent 间传递 Context 为 null

支付小周

结论

agent.setContext() 和 agent.getContext() 的写法没有明显问题。Google Assistant 中的 Context 变成 null,通常不是名称格式的问题,而是因为 payBill 请求与创建 Context 时的请求不属于同一个 Dialogflow 会话。

Dialogflow Context 只在当前 session 中有效。请确认:

  • getBillingInfo 没有结束 Assistant 会话。
  • payBill 在同一次对话中触发。
  • payBill Intent 已将 billing-context 配置为 Input Context。
  • 设置和读取 Context 时使用的名称完全相同。
  • 异步请求完成后,Webhook 返回的 outputContexts 确实包含该 Context。

推荐修正

统一定义 Context 名称,并通过 agent.parameters 获取当前 Intent 的参数:

const BILLING_CONTEXT = 'billing-context';

function getBillingInfoHandler(agent) {
  const phoneNumber = agent.parameters['phone-number'];
  const url = 'https://testapi.io/api/shwej//getBillingInfo';

  return new Promise((resolve, reject) => {
    https.get(url, (res) => {
      let body = '';

      res.on('data', (chunk) => {
        body += chunk;
      });

      res.on('end', () => {
        try {
          const apiResponse = JSON.parse(body);
          const billAmount = apiResponse.billing_amount;

          agent.setContext({
            name: BILLING_CONTEXT,
            lifespan: 5,
            parameters: {
              phoneNumber,
              billAmount
            }
          });

          agent.add(
            `Your bill for ${phoneNumber} is ${billAmount} ₹`
          );
          agent.add(new Suggestion('Click to Pay'));

          resolve();
        } catch (error) {
          console.error('Invalid API response:', error);
          agent.add('Unable to read billing information.');
          reject(error);
        }
      });

      res.on('error', (error) => {
        console.error('Error calling the API:', error);
        agent.add('Error occurred while calling API.');
        reject(error);
      });
    }).on('error', (error) => {
      console.error('HTTPS request failed:', error);
      agent.add('Error occurred while calling API.');
      reject(error);
    });
  });
}

function payBillHandler(agent) {
  const billingContext = agent.getContext(BILLING_CONTEXT);

  if (!billingContext) {
    agent.add(
      'Billing information is no longer available. Please retrieve the bill again.'
    );
    return;
  }

  const { phoneNumber, billAmount } = billingContext.parameters || {};

  if (!phoneNumber || billAmount === undefined) {
    agent.add('Billing information is incomplete. Please retrieve the bill again.');
    return;
  }

  agent.add('Your payment is successful!');
  agent.add(`Phone Number: ${phoneNumber}`);
  agent.add(`Amount paid: ${billAmount} ₹`);
}

lifespan: 50 可以延长 Context 的生命周期,但通常没有必要。它表示 Context 可以持续匹配 Intent 的轮数,并不代表 Context 能跨会话永久保存。无论这个值多大,只要会话结束,Context 就会消失。

检查 Dialogflow Intent 配置

请在 Dialogflow ES 控制台中检查以下设置:

getBillingInfo

  • 不要启用结束对话的选项,例如 Set this intent as end of conversation。
  • 确认 Webhook 已启用。
  • Webhook 返回后,应生成名为 billing-context 的 Output Context。

payBill

  • 将 billing-context 添加为 Input Context。
  • 添加能够匹配建议按钮文本的训练短语,例如:
Click to Pay

点击建议按钮后,Assistant 会发送一条新的用户文本消息,而不是直接调用 payBillHandler()。只有这条文本匹配到 payBill Intent,处理函数才会执行。

如果按钮文本无法稳定匹配训练短语,可以换成更自然、明确的表达:

agent.add(new Suggestion('Pay bill'));

然后在 payBill Intent 中加入:

Pay bill

确认是否发生了会话切换

记录两个请求中的 session:

console.log('Current session:', request.body.session);
console.log(
  'Output contexts:',
  JSON.stringify(request.body.queryResult.outputContexts || [])
);

正常情况下,getBillingInfo 和随后触发的 payBill 应该具有相同的 request.body.session。如果两者不同,说明 Assistant 已开启新会话,之前的 Context 也就无法读取。

常见原因包括:

  • getBillingInfo 被配置为结束对话。
  • 第一个响应使 Assistant 关闭了会话。
  • 用户退出后重新调用 Action。
  • 测试时重新启动了模拟器会话。
  • payBill 通过其他入口或新的调用触发。

注意事项

Context 适合保存同一次对话中的临时状态,不适合保存真实的支付数据。实际支付流程应当:

  • 在服务端生成交易或账单标识。
  • 将账单状态保存在数据库中。
  • 根据经过验证的用户身份和交易标识重新查询金额。
  • 不要只根据 Context 中的 phoneNumber 和 billAmount 执行扣款。

如果两个请求的 session 完全相同,但 billing-context 仍然不存在,请重点检查 getBillingInfo 的 Webhook 响应日志。只有响应中的 outputContexts 实际包含 .../contexts/billing-context,才能确认 Context 已成功写入。

备注:内容仅供参考。