客服知道用户买过什么,不代表每一次对话都应该读取全部消费历史。用户问“如何修改邮箱”,把历年订单和内部价值标签一起塞给模型,既没有帮助,还可能让回答泄露不应展示的分层信息。
我会让前端先确定这次请求的目的,再由服务端根据身份和租户挑选字段。模型只拿到回答所需的视图;促销推荐另设开关,不和售后帮助捆在一起。
浏览器工具出现之后,身份边界依然存在
2026年10月21日,Cloudflare与Commerce.com介绍了通过WebMCP向购物Agent暴露结构化工具的实践,同时明确WebMCP仍是实验性浏览器提案。官方文章 页面能提供工具,不意味着调用者自动拥有某个客户的权限。
对自己的产品,关键边界仍是服务端:登录用户是谁,属于哪个租户,正在处理哪个工单。不要从模型参数里的customerId直接相信身份,也不要因为数据库查到了结果就返回全部字段。
图里的字段过滤发生在模型之前。隐藏字段不只是CSS不显示;未授权字段不应进入响应体,更不应进入模型上下文或浏览器日志。
一个可以本地验证的投影视图
下面用内存数据演示。租户和登录身份由调用方明确传入,真实服务里应来自已经验证的会话。这不是完整的认证实现。
const assert = require('node:assert/strict'); function supportView(actor, customer) { if (!actor || actor.tenantId !== customer.tenantId || actor.customerId !== customer.id) throw new Error('forbidden'); return { plan: customer.plan, renewalDay: customer.renewalDay }; } const customer = { id: 'c1', tenantId: 't1', plan: 'basic', renewalDay: 12, internalValue: 'high', lifetimeSpend: 9999 }; const view = supportView({ tenantId: 't1', customerId: 'c1' }, customer); assert.deepEqual(Object.keys(view), ['plan', 'renewalDay']); assert.equal('lifetimeSpend' in view, false); assert.throws(() => supportView({ tenantId: 't2', customerId: 'c1' }, customer)); console.log('crm-view: 3 assertions passed');保存为crm-view.cjs,运行node crm-view.cjs。它验证允许字段与跨租户拒绝,不测试数据库权限、登录凭据或模型表现。员工代办、家庭账户等关系需要独立的授权规则,不能把这段“本人查询”直接扩展成通用客服权限。
推荐必须能解释为什么出现
如果用户主动请求套餐建议,可以使用当前套餐、明确表达的需求与可公开的套餐差异。内部“高价值”“容易流失”等标签不适合直接变成对用户的说法。建议卡应展示推荐原因、价格和替代方案,而不是把预测写成事实。
我也会把“获取帮助”和“接收营销”分成不同设置。关闭推荐后,客服仍应正常工作;退出营销不应导致用户失去支持。这些是产品设计选择,需要结合实际业务与适用规则审查,本文不替任何地区提供合规结论。
比起让Agent显得什么都知道,我更在意它知道的内容是否恰好够用。