网页通话(挂件、托管通话页、你的 App)里,客户在语音之外还看得到一个对话框,所以 Agent 能做的不只是说话。电话通话没有这些功能。
打开 Agent → 工具 → 发给客户(Agent API 里是 send_to_caller),Agent 一次可以给客户发最多 5 张卡片:标题、标题下的一行、说明、图片、链接,每张最多 2 个回话按钮;问问题时还可以附上最多 3 个快捷回复。链接不会被读出来。
Agent 只能发这通电话里拿到过的链接和图片:它的指令和开场白、知识库(从网页读入的文档带着网址)、你的函数返回的结果、预设卡片。链接还必须在 allowed_hosts 里的网站上(预设卡片自己的网站总是可以)。Agent 编的、或者客户念出来的链接都不会发出去。Agent 说的话里出现这些网站的链接,对话框里可以直接点。
"send_to_caller": {
"enabled": true,
"allowed_hosts": ["shop.example.com", "*.cdn.example.com"],
"presets": [
{ "name": "hours", "when": "客户问营业时间时", "title": "营业时间",
"description": "周一至周六 9:00–18:00", "url": "https://shop.example.com/stores", "image_url": "" }
]
}
每次发送都会触发 call.message_sent Webhook(channel、带 title 和 url 的 items、quickReplies、blocked)。一通电话最多发 10 次。
通话中客户也可以打字(邮箱、订单号)或点回话按钮,Agent 像听到客户说话一样回答,正在说话时先停下。每条最多 500 字,每秒 1 条,每通电话 100 条。通话记录里会标出这一轮是打字还是点按钮。
访客也可以一开始就打字,而不是说话:网页挂件和托管页同时提供两种方式,也可以在 Agent → 部署 里只提供其中一种。打字对话不出声音,按条计费。要从你自己的服务器和 Agent 对话,用 Chat API。
挂件在你的网页上提供一个 evasay 对象。全部可选;只有网页在挂件的允许嵌入网站里(Agent → 部署),通话窗口才会接收它的消息:
<script src="https://evasay.com/voice-widget.js" data-agent="AGENT_SLUG" async></script>
<script>
window.evasay = window.evasay || [];
evasay.push(function (agent) {
agent.setVariables({ plan: 'pro', page: location.pathname }); // 给下一通电话
agent.update('购物车:2 件,合计 $120', { key: 'cart' }); // 通话中
agent.registerTool('add_to_cart', async function (args) {
// 你的代码,在访客的浏览器里执行
return { ok: true, checkout: 'https://shop.example.com/checkout' };
});
agent.on('cards', function (cards) {}); // 还有 'live'、'ended'
});
</script>
setVariables 的值用来替换指令和开场白里的 {{name}}。它们来自访客的浏览器,所以会告诉 Agent 这些值未经核实,也不会传给你的函数和来电前查询。update 告诉 Agent 页面上发生了什么,Agent 不会因此开口;带同一个 key 的新内容替换旧的。"kind": "page",没有 url),再在网页上注册同名函数。只有注册了它的网页发起的通话里 Agent 才能调用;默认最多等 timeout_sec(1–10,默认 5)秒拿结果,wait_for_result 为 false 时不等。它以访客自己的登录状态执行,相当于访客自己点了一下:花钱或不可撤销的操作请在你的页面里先让访客确认。返回结果里的链接可以发成卡片。把 谁可以发起通话 设为 只允许带令牌,由你的服务器为登录用户申请一次性令牌:
curl https://evasay.com/api/v1/voice/web-calls \
-X POST \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{ "agent_id": "AGENT_ID", "user_id": "u_42", "variables": { "name": "Ann" }, "expires_in": 600 }'
返回 token(只能用于一通电话,expires_in 秒内有效,30–3600,默认 600)、agent(slug)和 web_call_url。把令牌交给网页:在挂件的 script 上加 data-token-url="/your/endpoint"(挂件带着访客的 cookie 向这个地址发 POST,读取 { "token": "…" }),或者 agent.setToken(async () => token)。令牌里的 variables 是可信的:指令、来电前查询和函数参数模板都可以用,user_id 也一样,通话会记下它(通话里的 userId,列表用 user_id 筛选)。API 密钥只放在你的服务器上。
在你自己的 App 里,把 { "token": "…", "pageTools": ["add_to_cart"] } POST 到 https://evasay.com + web_call_url,得到这通电话的连接信息(WebRTC offer 地址或 WebSocket 地址,带通话令牌),用 RTVI 客户端连接,例如 Pipecat 的客户端 SDK;iOS、Android 和 React Native 见移动端。卡片、页面函数和提示以 RTVI server-message 发来(evasay.cards、evasay.tool、evasay.notice);打字用 send-text 发送,页面情况和函数结果用 client-message 的 evasay.context、evasay.tool_result 发送。也可以用音频 WebSocket自己传音频,带上 user_id 和 page_tools。