一个注解搞定 AGI,两个注解搞定 AMI —— SmartCall 让 Java 对接 Asterisk 简单到离谱

简介: SmartCall 是基于 Java + Asterisk 的开源智能客服呼叫中心,其核心模块 `jpower-core-asterisk` 将繁琐的 AGI/AMI 对接封装为 `@Agi` 和 `@AmiEvent` 等注解,几行代码即可实现通话控制、TTS/ASR、事件监听与外呼,大幅降低开发门槛。Apache 2.0 协议,支持商用。

做过呼叫中心 / 智能外呼 / IVR 语音机器人的同学都知道,Java 对接 Asterisk 是个体力活:AGI 要自己注册脚本、管理线程和生命周期、自己轮询检测挂机;AMI 要自己建连接、写一个巨大的 onManagerEvent,然后在里面堆一屏 if (event instanceof XxxEvent)。光是把这些样板代码搭起来,一两天就没了,还处处是坑。

今天介绍的开源项目 SmartCall(AI 客服呼叫中心),把这一切收敛成了几个注解。它的通信基座 jpower-core-asterisk 让「实现一个 AGI 脚本」「监听一个 AMI 事件」都变成了几行代码的事。这篇文章就用最直白的对比,带你看清它到底有多简单。

一、SmartCall 是什么?

SmartCall 是一套基于 AI 大模型 + Asterisk 通信引擎 的新一代智能客服呼叫中心系统,深度融合了 AI 语音机器人、可视化 IVR 流程编排、实时 ASR/TTS、大模型意图识别、端到端实时对话模型等能力,覆盖从「呼入智能应答」到「智能外呼」的全链路。

  • 技术栈:Java 17 + Spring Boot 3.5 + Spring Cloud 2025 + MyBatis-Flex + Asterisk(PJSIP) + Spring AI,前端 Vue 3。
  • 架构:微服务 / 单体双模,可独立部署也能一键启动;原生多租户、RBAC、OAuth2。
  • 协议:Apache 2.0,允许商用。

而它「打电话」这件事的所有底座能力,都来自 smart-aster 模块 + jpower-core-asterisk 这个 jar。下面我们就聚焦这个 jar,看看 AGI 和 AMI 是怎么被它「注解化」的。


二、5 分钟接入:一个依赖 + 一段配置

引入依赖:

<dependency>
    <groupId>top.jpower</groupId>
    <artifactId>jpower-core-asterisk</artifactId>
</dependency>

写一段配置(Spring Boot 自动装配,无需任何 @Bean 手写):

asterisk:
  ami:                       # AMI:监听全局事件、主动外呼
    host: 127.0.0.1
    port: 5038
    username: admin
    password: ******
    event: true
  agi:                       # AGI:IVR 交互
    port: 4573
  context:
    in: inbound-dialer
    out: outbound-dialer
    exten: robot
  voice-root-dir: /data/     # 录音根目录

启动后,模块自动帮你做完了这些「脏活」:

  • 登录 AMI,创建好 ManagerConnection 和 AsteriskServer 两个 Bean(外呼、查询直接注入即用);
  • 拉起 FastAGI Server 监听 4573 端口,随 Spring 生命周期自动启停;
  • 内置挂机事件管理器、实时音频(TTS/ASR)媒体流通道。

你要做的,只剩写业务。


三、AGI:一个注解 + 一个类,搞定一路通话

3.1 对比一下就知道有多爽

❌ 原生 asteriskjava 的写法(简化):

// 1. 实现 AgiScript
public class MyScript implements AgiScript {
   
    public void service(AgiRequest request, AgiChannel channel) {
   
        try {
   
            channel.answer();
            channel.exec("Playback", "hello");
            // 挂机检测?自己轮询 channel.getStatus()...
        } catch (AgiHangupException e) {
    /* 到处 try-catch */ }
    }
}
// 2. 自己 new DefaultAgiServer,注册 MappingStrategy,起线程,管理生命周期
// 3. TTS 放音、ASR 识别、录音、收号?全部自己造轮子

✅ 用 jpower-core-asterisk 的写法:

@Slf4j
@Agi("call.agi")            // ← 一个注解,绑定 dialplan 脚本名
@Component                  // ← 交给 Spring,自动注册进 AGI 路由表
public class AgiHandler extends AgiAbstractScript {
   

    @Override
    public void service(AgiSupport support) {
   
        support.answer();                    // 接听
        support.playback("您好,欢迎致电");    // TTS 实时合成放音
        String said = support.radio("请说出您的需求"); // ASR 识别(可被打断)
        log.info("用户说:{}", said);
        support.hangup();                    // 挂断
    }

    @Override
    public void hangup(AgiSupport support) {
   
        // 挂机回调:框架已用「AMI 事件驱动 + 轮询兜底」保证幂等,你只管清理
        support.thread().interrupt();
    }
}

没有线程管理、没有生命周期、没有 instanceof、没有挂机轮询、没有 try-catch 地狱——@Agi + @Component + 继承 AgiAbstractScript,一个能接听、放音、识别、挂断的 IVR 脚本就成了。

3.2 dialplan 只需一行

Asterisk 的 extensions.conf 里把电话交给它:

[inbound-dialer]
exten = _X.,1,AGI(agi://127.0.0.1/call.agi?type=1)   ; 名字对上 @Agi("call.agi") 即可
same  = n,Hangup()

@Agi 的 value 就是这里的脚本名,框架启动时自动建好「脚本名 → Bean」的路由表。

3.3 AgiSupport:一行代码一个能力

service(AgiSupport support) 里的 support 是通话级对象(每来一通电话框架自动实例化),把 IVR 里最常用的动作全封装成了方法,真正做到「一行代码一个功能」:

一行代码 干的事
support.answer() / support.hangup() 接听 / 挂断
support.playback("文本") TTS 实时合成并播放
support.radio("提示语") 边播边 ASR 识别,用户一说话立即打断(barge-in)
support.received("请输入工号", "#") 放音后 DTMF 收号
support.exec("queue", "kf", ...) 执行任意 dialplan 应用(转人工队列等)
support.mixMonitor(url) / stopMixMonitor(id) 双向录音并自动上传
support.getVariable(k) / setVariable(k,v) 读写通道变量
support.streamSession() 拿全双工裸音频流,对接端到端语音大模型

尤其是 radio()——TTS 播报和 ASR 识别是全双工并行的,用户一开口就打断机器人,纯 Java 侧控制、无磁盘中转。做「能被打断的语音机器人」最难的一块,这里一个方法搞定。

3.4 需要定制?继承一下就行

@Agi(value = "call.agi", support = AgiSupportImpl.class) 可以指定你自己的支撑类,用来注入 ASR/TTS 引擎、加业务回调。SmartCall 就是这么干的:

public class AgiSupportImpl extends AgiSupport {
   
    public AgiSupportImpl(AgiChannel channel, AgiRequest request, Thread thread) {
   
        super(channel, request, thread);
        // support 是框架反射 new 的,用 SpringUtil 取依赖即可
        this.processService = SpringUtil.getBean(IProcessService.class);
    }
    @Override protected AsrClient initAsr() {
    /* 按流程配置构建 ASR */ }
    @Override protected TtsClient initTts() {
    /* 构建 TTS */ }
    @Override public void realtimeData(Boolean isBot, byte[] pcm, String content) {
   
        // 每一句话(含音频)都回调这里:实时推前端、落库做通话转写
    }
}

四、AMI:两个注解,彻底告别 instanceof 地狱

4.1 对比一下

❌ 原生写法:

connection.addEventListener(event -> {
   
    if (event instanceof NewChannelEvent e) {
    /* ... */ }
    else if (event instanceof HangupEvent e) {
    /* ... */ }
    else if (event instanceof AgentConnectEvent e) {
    /* ... */ }
    // ...几十行 else if,还得自己管理连接、重连、线程
});

✅ jpower-core-asterisk 写法:

@Slf4j
@Component
@AmiListener({
   NewChannelEvent.class, HangupEvent.class})   // ← 类上声明「我关心哪些事件」
public class MyListener extends EventAbstractListener {
   

    @AmiEvent(NewChannelEvent.class)                       // ← 方法上声明「我处理哪个事件」
    public void onNewChannel(NewChannelEvent event) {
   
        log.info("新通道:{}", event.getChannel());
    }

    @AmiEvent(HangupEvent.class)
    public void onHangup(HangupEvent event) {
   
        log.info("挂断:{},原因:{}", event.getChannel(), event.getCauseTxt());
    }
}

就这么简单。框架会自动:

  1. 收集所有带 @AmiListener 的 Spring Bean;
  2. 按注解里的事件类型过滤(只有你订阅的事件才投递给你);
  3. 反射派发到对应的 @AmiEvent 方法。

你只写「处理逻辑」,订阅、过滤、分发、连接管理全是框架的事。

4.2 两个注解的分工

  • @AmiListener({...})(类级):订阅白名单,声明这个监听器关心哪些事件,留空则收全部。
  • @AmiEvent(Xxx.class)(方法级):分发路由,一个事件进来自动调到标注了它的方法。

一个类可以同时处理十几种事件,各自独立成方法,清爽得不得了。SmartCall 的话单监听器 RecordCallListener 就是这样,一个方法管一个事件:

@Component
@Order(20)
@AmiListener({
   NewChannelEvent.class, NewCallerIdEvent.class, QueueCallerJoinEvent.class,
              AgentConnectEvent.class, VarSetEvent.class, HangupRequestEvent.class, HangupEvent.class})
public class RecordCallListener extends LinkedEventListener {
   

    @AmiEvent(NewChannelEvent.class)   public void newChannel(NewChannelEvent e)   {
    /* 建话单 */ }
    @AmiEvent(QueueCallerJoinEvent.class) public void join(QueueCallerJoinEvent e) {
    /* 转人工入队 */ }
    @AmiEvent(AgentConnectEvent.class) public void connect(AgentConnectEvent e)    {
    /* 坐席接通 */ }
    @AmiEvent(HangupEvent.class)       public void hangup(HangupEvent e)           {
    /* 结算话单 */ }
}

4.3 需要「跨事件推理」?换个基类即可

如果业务需要回看「同一通电话之前发生过什么」(比如判断是用户先挂还是坐席先挂),把基类从 EventAbstractListener 换成 LinkedEventListener,就能拿到按 linkedId 聚合的整条事件链:

// CACHE 里是这通电话按时序排列的所有事件,随便回溯
boolean userHungFirst = CACHE.get(event.getLinkedId()).stream()
        .filter(e -> e instanceof HangupRequestEvent)
        .anyMatch(e -> isUserChannel(((HangupRequestEvent) e).getChannel()));

框架内部用「监听器计数」保证所有监听器都处理完,事件才写入链路缓存,多监听器读到的视图完全一致。你不用管这些细节,继承就完事了。

4.4 主动外呼?注入 Bean 直接用

模块已经装配好 AsteriskServer,配合 OriginateAction 几行就能发起外呼。SmartCall 的 CallServiceImpl 甚至本身就是一个监听器(@Service + @AmiListener),一边外呼一边监听挂机回填结果:

@Service
@AmiListener({
   HangupRequestEvent.class, HangupEvent.class})
public class CallServiceImpl extends EventAbstractListener implements CallService {
   

    private final AsteriskServer asteriskServer;   // 直接注入

    public CallVO call(String phone, String lineId) {
   
        OriginateAction action = new OriginateAction();
        action.setChannel("PJSIP/" + phone + "@" + lineId);
        action.setContext(asteriskProperties.getContext().getOut());
        action.setExten(asteriskProperties.getContext().getExten());
        action.setPriority(1);
        action.setTimeout(60000L);
        asteriskServer.originateAsync(action, callback);   // 异步发起
        // ...
    }

    @AmiEvent(HangupEvent.class)
    public void onHangup(HangupEvent e) {
    /* 回填呼叫结果 */ }
}

一个小提醒:@AmiListener 不只能打在专门的 Listener 上,任何 Spring Bean(含 @Service)都可以,非常灵活。


五、「简单」到底体现在哪?一张表说清

能力 原生 asteriskjava jpower-core-asterisk
AGI 脚本注册 手写 Server + MappingStrategy + 起线程 @Agi("x.agi") + @Component
AGI 生命周期 自己管理 随 Spring 自动启停
挂机检测 自己轮询通道状态 框架 AMI 事件驱动 + 轮询兜底,幂等
TTS 放音 / ASR 识别 自己对接、自己造轮子 playback() / radio() 一行搞定
AMI 连接 自己建、自己登录、自己重连 自动装配成 Bean
AMI 事件分发 一屏 if (event instanceof ...) @AmiListener + @AmiEvent
事件链路回溯 自己缓存、自己管理 继承 LinkedEventListener
主动外呼 自己拼 Action、自己管回调 注入 AsteriskServer 即用

结论很直白:原本要写几百行样板 + 踩无数坑的活儿,现在几个注解、几行代码就搞定。 这正是 SmartCall 能把精力集中在「AI 能力」和「IVR 编排」上的原因——通信底座足够简单可靠。


六、这些能力,在 SmartCall 里长成了什么样

上面这些「简单的注解」,最终支撑起了 SmartCall 一整套生产级功能:

  • 🧠 AI 智能应答:集成通义千问、DeepSeek 等大模型做意图识别;radio() 的全双工打断能力让 AI 机器人对话接近真人;对接 MaxKB 知识库做多轮智能问答。
  • 🔊 端到端实时对话模型:基于 streamSession() 的全双工裸流,音频直通大模型服务端,由模型端到端完成「听-想-说」,首响延迟更低,支持服务端 VAD 打断。
  • 📞 可视化 IVR 流程编排:基于 LogicFlow 拖拽式设计,Say / Answer / Intention / Agent / Transfer / Extract / Script 等丰富节点,每个节点背后就是 AgiSupport 的一个方法。
  • 📋 通话记录(CDR):LinkedEventListener 的事件链路缓存,支撑起复杂的话单归因、转接记录、呼损统计。
  • 📤 智能外呼:OriginateAction + AMI 挂机监听,实现批量外呼、智能重拨、多机器人并发。
  • 👥 坐席管理 / 转人工:AMI 事件驱动的坐席状态监控、队列分配、通话保持与转接。

一句话:你看到的每一个「智能」功能,底层都是这几个注解在稳稳地跑。


七、为什么值得试试 SmartCall?

  • ✅ 通信底座极简:AGI/AMI 注解化,二次开发门槛低,本文的例子拿来即用。
  • ✅ AI 能力齐全:大模型意图识别、ASR/TTS 多厂商可插拔、端到端实时对话、知识库智能体,全都开箱即用。
  • ✅ 架构够企业级:微服务 + 单体双模、多租户、RBAC、OAuth2、网关限流、SkyWalking 链路追踪。
  • ✅ 部署够省心:提供完整 Docker Compose 编排,Asterisk 镜像一键拉起,完整支持内网私有化部署。
  • ✅ 开源可商用:Apache 2.0 协议。

八、立即体验

⭐ Gitee 开源仓库:https://gitee.com/gdzWork/SmartCall


写在最后

Java 对接 Asterisk 曾经是件「脏活累活」,但有了 jpower-core-asterisk 这样的注解化封装,AGI 一个注解、AMI 两个注解,就能把复杂的电话通信能力稳稳地跑起来。而 SmartCall 正是把这套「简单」发挥到极致的开源实践——它让你不必再纠结底层通信,而是把精力真正放在「让 AI 讲好每一通电话」上。

如果你正在做智能客服、AI 外呼、IVR 语音机器人,或者受够了原生 asteriskjava 的繁琐,强烈建议去演示站点点看,或者给项目点个 Star ⭐,你的支持是它持续迭代的最大动力。

相关文章
|
23天前
|
人工智能 算法 Unix
傲慢≠谦虚=圣爱·七美德体系结构性缺陷·否决一切的AI算法
BSD Step237提出革命性洞见:七罪非程度之分,而是本体论层级之别——六罪是人性六种本能的极端化(仍在人性框架内),唯傲慢是否定人性本身、跳出框架的“反人性罪”。传统以“谦虚”对治傲慢实为结构性缺陷,因其仍是封闭的中庸姿态;真正解药是超越人性的“接受性”:无条件承认子集≠母集、拥抱B=C变化、破除封闭。
417 10
|
3月前
|
运维 Java 调度
XXL-JOB 分布式定时任务框架:任务分片、失败重试、调度中心一次讲透
单机 @Scheduled 扛不住分布式定时任务?拆解 XXL-JOB 调度中心与执行器架构、任务分片、失败重试机制,附 30 分钟接入示例。
511 0
XXL-JOB 分布式定时任务框架:任务分片、失败重试、调度中心一次讲透
|
28天前
|
Web App开发 人工智能 JavaScript
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA
REA(Reverse Engineer Anything)是近期爆火的开源AI逆向工具,4月发布,1个月内斩获15万+ Star。它让AI Agent无需源码即可分析二进制、APK、JS/Electron、HAR包等17类目标,自动反编译、生成可运行代码,并强制返回带证据与局限的结论。全本地执行,MIT协议,支持Hopper/Ghidra/IDA等引擎,一行命令即可上手。
1514 4
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA
|
1月前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
1117 0
|
15天前
|
人工智能 弹性计算 双11
2026年阿里云双11热门产品优惠活动:大模型4.5折起、云服务器38元起,Token Plan79元起、AI建站9.9元起‌
2026年双11活动,阿里云围绕"AI生产力普惠"与"云上成本优化"两条主线,释放大模型、Token Plan、AI建站、云服务器四大板块优惠:全模型通享低至4.5折、首购10元起;Token Plan个人版纳入12项Harness权益,79元/月起、团队版150元/月起;万小智3.0建站9.90元/月起;云服务器覆盖38元轻量至13.8万元GPU旗舰,99元、199元档续费同价。多数支持先领券再下单。
|
20天前
|
运维 安全 Linux
告别云端账单!Ollama本地部署LLM完全指南:从环境变量到命令行,再到Python/curl实战代码一网打尽
当每一次Token消耗都在蚕食你的预算,当每一条数据上传云端都在挑战隐私红线——是时候把大模型"请回家"了。本文将带你从零构建完全离线的本地大模型运行环境,手把手敲定环境变量、核心命令与实战代码,让你彻底告别云端按量扣费的焦虑。而如果你同时需要云端弹性算力作补充,也可随时前往‌阿里云百炼大模型服务平台页面‌了解一站式MaaS方案。
239 1
|
20天前
|
人工智能 运维 监控
找不到百炼入口?阿里云百炼官网、控制台地址整理,额度核验与 API 成本管控教程
百炼平台分为前台产品官网、后台控制台两套核心入口,分工清晰。产品官网用于浏览产品介绍、报价信息、领取新人超7000万Tokens免费权益;控制台是开发者核心后台,完成API‑Key创建、地域切换、免费额度查询、订阅套餐管理、调用监控。新人免费额度仅限华北2(北京)地域,各个模型额度互相独立,主账号与RAM子账号共享额度池,开启用完即停可以规避实名账号意外扣费风险。
290 1
|
14天前
|
人工智能 自然语言处理 API
阿里云Token Plan全解析:个人版与团队版区别、价格对比及优惠活动
Token Plan 是千问 AI 平台推出的 AI 大模型订阅服务,以 Credits 统一计量,一份订阅即可在 Claude Code、Cursor、Qwen Code、Qoder 等主流编程与智能体工具中调用文本、图像、视频、语音等多模态模型及联网搜索、代码解释器等内置工具。产品分为个人版与团队版:个人版设 Lite、Essential、Standard、Pro 四档,Standard 与 Pro 附赠 Harness 权益;团队版按标准、高级、尊享三档席位订阅,支持多席位管理与用量分析,承诺不使用数据训练模型。两者均可购买加油包补充额度,并叠加夜间折扣及多项优惠活动。
|
16天前
|
人工智能 双11 开发者
阿里云2026年双11活动Token Plan优惠:个人版79元起,团队版150元起
2026年阿里云双11期间,Token Plan个人版完成升级,将Harness权益纳入AI生产力体系,覆盖18+旗舰模型,新增12项权益,超额调用享88折。新套餐支持Qwen3.8-Max、Deepseek-v4-pro、glm-5.2等模型,分「部署AI Agent」「AI编程全包」「AI自选套餐」三大方向,组合低至39元/月,AI Agent可68元起零代码部署。价格上个人版79元/月起,团队版150元/月起,季付年付更省,整体呈现入门门槛低、长期档位省、组合玩法多的特点。
|
18天前
|
人工智能
学习手册|学习备考 × Token Plan:让 Agent 批量整理讲义、生成练习
期中考试快到了,三门课的讲义和笔记堆成一团,想刷题却找不到成套的练习?这篇手册用一份虚构的章节要点做练习,带你在支持 Token Plan 的兼容工具中让 Agent 分工:逐份提炼要点、生成分章练习,再逐题核对答案与出处。练完之后,把方括号里的内容换成自己的课程资料和题型要求,就能直接用在真实备考中。