我打造了一个AI代理,能同时拨打电话给5家按摩店,对比价格和可用时段。以下是它的工作原理。

Reddit r/AI_Agents 工具

摘要

一位开发者构建了一个名为Sooner的AI代理,该代理能自动化同时拨打多家按摩店的电话,以比较价格和可用性。它使用Twilio进行通话,Anthropic的Claude模型进行语言处理,并利用Playwright进行在线预订检查。

上周末我想要一个60分钟的深层组织按摩。我希望在周六下午,价格低于约120美元,由女性治疗师提供,并且在我附近。水疗网站上的在线价格通常过时或为“起价”,而且有一半不显示真实空位。通常这需要花一个小时到处打电话询问。所以我输入到Sooner中,这是我的一个副项目:“60分钟深层组织按摩,周六下午2-5点,低于120美元,有经验的治疗师,靠近Back Bay” 底层原理如下:发现和评分。它拉取附近的商家,并根据每个要求对它们进行评分:价格、时间窗口、治疗师性别。对于能确认的要求,它会显示证据。无法验证的地方排名较低但仍保留在列表中。你选择最多5家进行拨打。首先进行在线可用性检查。许多水疗中心通过Mindbody、Vagaro、Boulevard、Fresha、Square或Wix进行预订。后端为每个平台适配了在headless Playwright中运行的适配器,读取真实空位。结果显示绿色表示在你的时间附近有空位,红色表示没有。如果某个地方在线已显示3:30有空位,你可能根本不需要打电话。并行呼叫。其余的通过Twilio ConversationRelay同时拨打,它处理语音到文本和文本到语音的WebSocket通信。实时面板显示每个呼叫的状态:拨号中、已接通、匹配或未接通。大多数呼叫在2分钟内完成。如果水疗中心已关闭,呼叫会排队等待其开门,结果通过短信返回。每次交互都是强制工具调用。LLM(用于交互的Claude Haiku,用于复杂情况的Claude Sonnet)从不返回自由文本。每次交互都是tool_choice: {type: "tool", name: "speak"},模式为:{ language?, say, done, outcome: match|partial|none|noanswer|human_preferred, detail, needsInfo? } 我们过去从自由文本中解析JSON。这导致了真正的错误,例如在实时呼叫中被读出截断的JSON。使用强制工具调用,API保证参数有效,因此无需解析。detail包含结果,例如“110美元60分钟深层组织按摩,周六3:30与Maria;首次访问减15美元”。你列出的每个要求都会在呼叫中询问。“女性治疗师”不是模型可以跳过的内容。结果最终进入呼叫摘要。电话树和AI接待员处理。在IVR菜单中,say只是要按的数字。许多水疗中心现在使用AI接待员接听电话。当代理高度确信它在与一个AI交谈时,它自己的台词以不可中断的方式发送。否则两个机器人会互相打断。面对人类时,它使用正常的打断:如果人开始说话,代理会停止。它总是披露身份。如果有人问“这是机器人吗?”,它会说是的,它是代表某人拨打电话的AI。这个规则是硬编码的,而不是交给模型。结果(示例):一个排名列表,前三名标有最早时段、评分和距离。对于可在线预订的水疗中心,一键即可预订。对于仅电话预订的水疗中心,它会进行后续呼叫以锁定时段,然后添加到你的日历。技术栈:Node/Express、SQLite(better-sqlite3)、Twilio ConversationRelay(Deepgram STT、Google TTS)、Anthropic API与提示缓存、Playwright用于预订平台,以及前端的React。我需要反馈的难点:延迟。非常短的回复完全跳过了流式路径,这导致了约1.2秒的静默,直到我发现它。调整打断功能,使代理在录制“您的呼叫对我们很重要”的问候语上停止说话。呼叫中切换语言。我缩小了每次呼叫的语言枚举,因为模型一直选择有效的但对呼叫错误的区域代码。目前处于测试阶段。在11月1日之前注册的用户,第一年每月获得更多的呼叫次数。我真的很想听听它在哪里会出问题。你会首先交给它什么呼叫?
查看原文

相似文章