作者:MeshCloud脉时云公有云架构师陈博文简介: Dialogflow是Google提供的一款人机交互平台,通过该平台可以轻松地设计出属于自己的交互机器人,比如常见的网页聊天机器人,电话智能客服等。借助Dialogflow甚至可以用于扫地机器人交互系统或者更高级的使用。 Dialogflow通过客户输入的语音或者文字甚至情感分析,来识别客户的意图(Intens),结合实体(Entities),来进行相应的回复。 Dialogflow的几个优点:识别准确率高,响应速度快支持30多种语言和语言变体上手简单:图形界面配置;官方文档丰富、详细;网络上有案例可供参考有问题易解决:开发者社区超过150万名开发者 Dialogflow经典案例: 一、马航的订票查票机器人: 使用GoogleCloud上的Dialogflow,马来西亚航空公司和Amadeus创建了一个聊天机器人,使客户能够搜索、预订和支付航班,从而使航空公司能够满足未来的需求并增加数字渠道的收入。 二、达美乐披萨的订餐机器人: 三、KLM预定、打包机器人: KLM于2016年开始探索为客户提供体验的方法。他们在测试多个平台后选择了Dialogflow。 常用工具 一、内置SmallTalk SmallTalk用于为闲聊对话提供响应。此功能可以解答代理范围之外的常见问题,极大地提升最终用户体验。 SmallTalk有两种版本:内置SmallTalk:为代理启用SmallTalk后,它会自动处理闲聊对话,无需向代理添加意图。预建SmallTalk:导入预建SmallTalk代理时,它会提供处理闲聊对话的意图。 二、prebuiltagent 由Dialogflow提供的一组代理,适用于常见的使用场景。您可以这些代理为基础,构建涵盖特定场景(如外出就餐、酒店预订和导航)的对话。 由Dialogflow提供的一组代理,适用于常见的使用场景。您可以这些代理为基础,构建涵盖特定场景(如外出就餐、酒店预订和导航)的对话。 如何制作一个自己的天气新闻语音问答机器人 使用了文字输入Dialogflow的方式 通过speechtotext将音频麦克风流到Dialogflow的文本意图检测API 案例使用了以下GCP产品:DialogflowESKnowledgeBasesSpeechtoText 其它组件:WebhookWeathersNewsAPI 在这个demo中你可以使用麦克风输入,然后返回新闻或者天气 一、DialogflowES(页面配置) 1、意图配置 配置输入 配置回复 2、Webhook配置 意图开启Fulfillment 添加webhook webhook代码importrequests新闻接口fromnewsapiimportNewsApiClientimporttimeimportjson使用了flask框架fromflaskimportFlask,requestimportpycountryfromgevent。pywsgiimportWSGIServerappFlask(name)app。route(webhook,methods〔POST〕)defwebhook():Dialogflowdatajson。loads(request。data)intentDialogflowdata〔queryResult〕〔intent〕〔displayName〕print()ifintentnews:responseTextcallnewsapi()newsresponseText〔articles〕〔0〕〔title〕print(news)headlineheadlinenewsiss(news)需要按要求返回dialogflow才能回复给客户端fulfillmentText是客户端接收消息res{fulfillmentText:headline,fulfillmentMessages:〔{text:{text:〔headline〕}}〕}return(res)elifintentweather:CITYDialogflowdata〔queryResult〕〔parameters〕〔geocity〕key479284d0d8574437b8170935221508responseTextjson。loads(callweatherapi(key,CITY))mintempresponseText〔data〕〔ClimateAverages〕〔0〕〔month〕〔7〕〔avgMinTemp〕maxtempresponseText〔data〕〔ClimateAverages〕〔0〕〔month〕〔7〕〔absMaxTemp〕tempresLondonMaxtempissMintempeiss(maxtemp,mintemp)需要按要求返回dialogflow才能回复给客户端fulfillmentText是客户端接收消息res{fulfillmentText:tempres,fulfillmentMessages:〔{text:{text:〔tempres〕}}〕}return(res)defcallweatherapi(key,CITY):time。sleep(0。01)responserequests。post(http:api。worldweatheronline。compremiumv1weather。ashx?keysqsfxnoccnomcayesformatjson(key,CITY))ifresponse。statuscode200:return(response。text)defcallnewsapi():newsapiNewsApiClient(apikey0eaad3923a654da2a2a32d84870e0405)responsenewsapi。gettopheadlines(languagees)return(response)ifnamemain:WSGIServer((0。0。0。0,5000),app)。serveforever()app。run(host0。0。0。0,port5000,sslcontext(rootscs1660552637313cbw404。cnscs1660552637313cbw404。cnNginxscs1660552637313cbw404。cnserver。crt,rootscs1660552637313cbw404。cnscs1660552637313cbw404。cnNginxscs1660552637313cbw404。cnserver。key)) 新闻接口:http:api。worldweatheronline。compremiumv1weather。ashx?keyapikeyqcityfxnoccnomcayesformatjson 天气接口:installpipinstallnewsapipythonusagefromnewsapiimportNewsApiClientinitnewsapiNewsApiClient(apikeyAPIKEY)v2topheadlinestopheadlinesnewsapi。gettopheadlines(qbitcoin,sourcesbbcnews,theverge,categorybusiness,languageen,countryus)v2everythingallarticlesnewsapi。geteverything(qbitcoin,sourcesbbcnews,theverge,domainsbbc。co。uk,techcrunch。com,fromparam20171201,to20171212,languageen,sortbyrelevancy,page2)v2topheadlinessourcessourcesnewsapi。getsources() 二、Speechtotext(后面简称stt)toDialogflow 1、准备工作 权限配置下载serviceaccountjson格式Linux:配置环境变量exportGOOGLEAPPLICATIONCREDENTIALSjsonpathjsonpath为1中下载的sa的json文件Windows:setGOOGLEAPPLICATIONCREDENTIALSC:UsersAdministratorDownloadssa。json python包python包googlecloudspeechpyaudiogoogleclouddialogflowpythondotenvuuid 。env文件用于读取配置PROJECTIDprojectid这里做的西班牙语测试LANGUAGECODEes语音的一些参数设置,保持默认ENCODINGAUDIOENCODINGLINEAR16SAMPLERATEHERZ16000SINGLEUTTERANCEfalseSPEECHENCODINGLINEAR16SSMLGENDERFEMALEdialogflow的区域(有us,es,zh)LOCATIONIDglobal 2、Speechtotext 使用实时流式音频执行识别(transcribestreamingmic),也就是麦克风持续输入,代码如下:!usrbinenvpythonGoogleCloudSpeechAPIsampleapplicationusingthestreamingAPI。NOTE:Thismodulerequirestheadditionaldependencypyaudio。Toinstallusingpip:pipinstallpyaudioExampleusage:pythontranscribestreamingmic。pyfromfutureimportpisionimportreimportsysfromgoogle。cloudimportspeechimportpyaudiofromsix。movesimportqueueRATE16000CHUNKint(RATE10)100msclassMicrophoneStream(object):Opensarecordingstreamasageneratoryieldingtheaudiochunks。definit(self,rate,chunk):self。raterateself。chunkchunkself。buffqueue。Queue()self。closedTruedefenter(self):self。audiointerfacepyaudio。PyAudio()self。audiostreamself。audiointerface。open(formatpyaudio。paInt16,channels1,rateself。rate,inputTrue,framesperbufferself。chunk,streamcallbackself。fillbuffer,)self。closedFalsereturnselfdefexit(self,type,value,traceback):self。audiostream。stopstream()self。audiostream。close()self。closedTrueself。buff。put(None)self。audiointerface。terminate()deffillbuffer(self,indata,framecount,timeinfo,statusflags):Continuouslycollectdatafromtheaudiostream,intothebuffer。self。buff。put(indata)returnNone,pyaudio。paContinuedefgenerator(self):whilenotself。closed:chunkself。buff。get()ifchunkisNone:returndata〔chunk〕whileTrue:try:chunkself。buff。get(blockFalse)ifchunkisNone:returndata。append(chunk)exceptqueue。Empty:breakyieldb。join(data)deflistenprintloop(responses):numcharsprinted0forresponseinresponses:ifnotresponse。results:continueresultresponse。results〔0〕ifnotresult。alternatives:continuetranscriptresult。alternatives〔0〕。transcriptoverwritechars(numcharsprintedlen(transcript))ifnotresult。isfinal:sys。stdout。write(transcriptoverwritecharsr)sys。stdout。flush()numcharsprintedlen(transcript)else:print(transcriptoverwritechars)ifre。search(rb(exitquit)b,transcript,re。I):print(Exiting。。)breaknumcharsprinted0defmain():languagecodeenUSBCP47clientspeech。SpeechClient()configspeech。RecognitionConfig(encodingspeech。RecognitionConfig。AudioEncoding。LINEAR16,sampleratehertzRATE,languagecodelanguagecode,)streamingconfigspeech。StreamingRecognitionConfig(configconfig,interimresultsTrue)withMicrophoneStream(RATE,CHUNK)asstream:audiogeneratorstream。generator()requests(speech。StreamingRecognizeRequest(audiocontentcontent)forcontentinaudiogenerator)responsesclient。streamingrecognize(streamingconfig,requests)Now,putthetranscriptionresponsestouse。listenprintloop(responses)ifnamemain:main() 3、Dialogflow 调用检测意图,代码如下:!usrbinenvpythonDialogFlowAPIDetectIntentPythonsampletouseregionalendpoint。Examples:pythondetectintenttextswithlocation。pyhpythondetectintenttextswithlocation。pyprojectidPROJECTIDlocationidLOCATIONIDsessionidSESSIONIDhellobookameetingroomMountainViewimportargparseimportuuiddefdetectintenttextswithlocation(projectid,locationid,sessionid,texts,languagecode):fromgoogle。cloudimportdialogflowsessionclientdialogflow。SessionsClient(clientoptions{apiendpoint:f{locationid}dialogflow。googleapis。com})session(fprojects{projectid}locations{locationid}agentsessions{sessionid})print(fSessionpath:{session})textinputdialogflow。TextInput(texttexts,languagecodelanguagecode)queryinputdialogflow。QueryInput(texttextinput)responsesessionclient。detectintent(request{session:session,queryinput:queryinput})print(20)print(fQuerytext:{response。queryresult。querytext})print(fDetectedintent:{response。queryresult。intent。displayname}(confidence:{response。queryresult。intentdetectionconfidence,}))print(fFulfillmenttext:{response。queryresult。fulfillmenttext})ifnamemain:parserargparse。ArgumentParser(descriptiondoc,formatterclassargparse。RawDescriptionHelpFormatter)parser。addargument(projectid,helpProjectagentid。Required。,requiredTrue)parser。addargument(locationid,helpLocationid。Required。,requiredTrue)parser。addargument(sessionid,helpIdentifieroftheDetectIntentsession。DefaultstoarandomUUID。,defaultstr(uuid。uuid4()),)parser。addargument(languagecode,helpLanguagecodeofthequery。DefaultstoenUS。,defaultenUS,)parser。addargument(texts,nargs,typestr,helpTextinputs。)argsparser。parseargs()detectintenttextswithlocation(args。projectid,args。locationid,args。sessionid,args。texts,args。languagecode,) 4、(主要代码)将stt的结果(文字)输出到Dialogflow意图检测,Dialogflow作出回复 流程: 代码如下:!usrbinenvpythonGoogleCloudSpeechAPIsampleapplicationusingthestreamingAPI。NOTE:Thismodulerequirestheadditionaldependencypyaudio。Toinstallusingpip:pipinstallpyaudioExampleusage:pythontranscribestreamingmic。pyfromfutureimportpisionimportreimportsysfromgoogle。cloudimportspeechimportpyaudiofromsix。movesimportqueueimportosimportuuid调用Dialogflow意图检测包(代码见2。dialogflow)fromdetectintenttextswithlocationimportdetectintenttextswithlocationfromdotenvimportloaddotenvRATE16000CHUNKint(RATE10)100msclassMicrophoneStream(object):Opensarecordingstreamasageneratoryieldingtheaudiochunks。definit(self,rate,chunk):self。raterateself。chunkchunkself。buffqueue。Queue()self。closedTruedefenter(self):self。audiointerfacepyaudio。PyAudio()self。audiostreamself。audiointerface。open(formatpyaudio。paInt16,channels1,rateself。rate,inputTrue,framesperbufferself。chunk,streamcallbackself。fillbuffer,)self。closedFalsereturnselfdefexit(self,type,value,traceback):self。audiostream。stopstream()self。audiostream。close()self。closedTrueself。buff。put(None)self。audiointerface。terminate()deffillbuffer(self,indata,framecount,timeinfo,statusflags):Continuouslycollectdatafromtheaudiostream,intothebuffer。self。buff。put(indata)returnNone,pyaudio。paContinuedefgenerator(self):whilenotself。closed:chunkself。buff。get()ifchunkisNone:returndata〔chunk〕whileTrue:try:chunkself。buff。get(blockFalse)ifchunkisNone:returndata。append(chunk)exceptqueue。Empty:breakyieldb。join(data)deflistenprintloop(responses):loaddotenv(verboseTrue)numcharsprinted0forresponseinresponses:ifnotresponse。results:continueresultresponse。results〔0〕ifnotresult。alternatives:continuetranscriptresult。alternatives〔0〕。transcriptoverwritechars(numcharsprintedlen(transcript))ifnotresult。isfinal:sys。stdout。write(transcriptoverwritecharsr)sys。stdout。flush()numcharsprintedlen(transcript)else:从。env中导出Projectid等配置,可以通过修改。env修改TEXTtranscriptoverwritecharsprint(transcriptoverwritechars)PROJECTIDos。getenv(PROJECTID)SESSIONIDuuid。uuid1()LANGUAGECODEos。getenv(LANGUAGECODE)LocationIDLOCATIONIDos。getenv(LOCATIONID)意图检测TEXT为mic接收到的语音转成的文字(代码见2。dialogflow)detectintenttextswithlocation(PROJECTID,LOCATIONID,SESSIONID,TEXT,LANGUAGECODE)Exitrecognitionifanyofthetranscribedphrasescouldbeoneofourkeywords。对麦克风说exit即可退出ifre。search(rb(exitquit)b,transcript,re。I):print(Exiting。。)breaknumcharsprinted0defmain():languagecodeenUSBCP47clientspeech。SpeechClient()configspeech。RecognitionConfig(encodingspeech。RecognitionConfig。AudioEncoding。LINEAR16,sampleratehertzRATE,languagecodelanguagecode,)streamingconfigspeech。StreamingRecognitionConfig(configconfig,interimresultsTrue)withMicrophoneStream(RATE,CHUNK)asstream:audiogeneratorstream。generator()requests(speech。StreamingRecognizeRequest(audiocontentcontent)forcontentinaudiogenerator)responsesclient。streamingrecognize(streamingconfig,requests)listenprintloop(responses)ifnamemain:main() Locationid:(上面意图检测API的locationid参数) 国家地区分组 地理位置 地区ID 美洲 爱荷华 uscentral1 美洲 蒙特利尔 northamericanortheast1 美洲 南卡罗来纳 useast1 美洲 俄勒冈 uswest1 欧洲 比利时 europewest1 欧洲 伦敦 europewest2 欧洲 法兰克福 europewest3 亚太地区 悉尼 australiasoutheast1 亚太地区 东京 asianortheast1 亚太地区 孟买 asiasouth1 亚太地区 新加坡 asiasoutheast1 全球 全球服务,静态数据在美国 global(首选)、us或无区域(默认) 5、测试 Dialogflowweb测试:Fulfillment通过webhookresponse格式化数据后返回给客户端,测试成功 程测试:micsttdialogflowclient(defaultwelcomeintentdefaultresponse) 测试成功: 可以看到语音输入的内容转成了文字,并发送给了dialogflow的意图检测,并匹配意图给出相应的回复 全流程测试:micsttdialogflowfulfillmentwebhookapiclient 对麦克风说:noticias(西语新闻) 返回:头条新闻的标题,测试成功 三、总结 至此,一个天气新闻语音问答机器人就做好了 官方还提供了另外的集成或者使用方式,可以供大家参考学习。希望这篇文章对大家有抛砖引玉的作用,以便大家能做出更高级更智能、更符合自己项目的交互机器人