yihong0618 / xiaogpt

Play ChatGPT and other LLM with Xiaomi AI Speaker
MIT License
6.13k stars 857 forks source link

在使用 chatgpt api 回答英语时,会自动在消息中每个单词间加 , 号。 造成不连续。 #46

Closed caitouwh closed 1 year ago

caitouwh commented 1 year ago

比如问“帮我 用英语回答天空为什么是蓝色的” 回答的message 如下。 以下是GPT的回答: ,,The,sky,appears,blue,because,of,the,scattering,of,sunlight,by,the,Earth's,atmosphere. ,As,sunlight,enters,the,Earth's,atmosphere,,the,short-wavelength,blue,light,is,scattered,more,widely,tha n,the,other,colors,in,the,spectrum,,such,as,red,and,orange.,This,scattering,process,causes,the,blue,l ight,to,travel,in,multiple,directions,and,reach,our,eyes,from,all,angles,,making,the,sky,appear,blue, during,the,day.,However,,during,sunrise,and,sunset,,the,longer-wavelength,colors,such,as,red,and,orange ,appear,more,visible,since,they,are,not,scattered,as,much,by,the,atmosphere.,This,is,why,the,sky,cha nges,color,to,hues,of,reds,and,oranges,during,these,times,of,the,day.

yihong0618 commented 1 year ago

@caitouwh 没想到太好的方法,因为小爱的 tts 不支持空格,我们把空格替换了,你有好的方式 PR welcome @pjq 你有什么好的方式么?

yihong0618 commented 1 year ago

倒是可以把空格替换为空,小爱会分词

pjq commented 1 year ago

目前看来英文 确实读的不好,中英文混的很难,试过微软TTS, 中英文混读也都不行

pjq commented 1 year ago

如果纯英文的,能接入比如微软TTS audio会好很多。 当然没关注国内比如讯飞语音怎么样了

pjq commented 1 year ago

这个fix 能够改善一些这个问题 https://github.com/yihong0618/xiaogpt/pull/59

gantrol commented 1 year ago

@caitouwh 没想到太好的方法,因为小爱的 tts 不支持空格,我们把空格替换了,你有好的方式 PR welcome @pjq 你有什么好的方式么?

个人认为,换行符可以换成顿号或者干脆去掉。

然后源项目说看spec,个人是不明白哪里写着空格应该怎么传的,后面试试。https://github.com/Yonsm/MiService/issues/5

gantrol commented 1 year ago

@caitouwh 没想到太好的方法,因为小爱的 tts 不支持空格,我们把空格替换了,你有好的方式 PR welcome @pjq 你有什么好的方式么?

个人认为,换行符可以换成顿号或者干脆去掉。

然后源项目说看spec,个人是不明白哪里写着空格应该怎么传的,后面试试。Yonsm/MiService#5

试了一些符号,感觉在我这台L15A里,

micli.py 7-3 "Thisisagoodidea"
micli.py 7-3 "This/is/a/good/idea"
micli.py 7-3 "This-is-a-good-idea"
micli.py 7-3 "This_is_a_good_idea"
yihong0618 commented 1 year ago

@gantrol 不是的,你试试 issue 里那个句子,去掉空格是不行的。短句子才行。

gantrol commented 1 year ago

@gantrol 不是的,你试试 issue 里那个句子,去掉空格是不行的。短句子才行。

确实。

我目前用/。更进一步地,先将.,?等英文断句符号后面的空格换成 ''。不过实际用下来,长句子的断句还有点问题,可能是小爱同学的问题……

代码:https://github.com/gantrol/voiceGPT/blob/1b46731157eba45546fb33f39a2c8cf3fc90980d/xiaogpt.py#L38

yihong0618 commented 1 year ago

@gantrol 这个看起来可以

caitouwh commented 1 year ago

我提供一个新思路, 先让小爱回答 帮我 “ 我喜欢跑步,用英文怎么说” , 这时,小爱自己可以流畅的说出英文。 这时理论上从输给TTS 的官方英文字符可以流找到官方的TTS 的正确英文输入格式, 就可以模拟 之。

但目前程序抓到的 TTS官方英文信息流,这个值是空的。 所以这里需要重新设计抓包。

yihong0618 commented 1 year ago

我提供一个新思路, 先让小爱回答 帮我 “ 我喜欢跑步,用英文怎么说” , 这时,小爱自己可以流畅的说出英文。 这时理论上从输给TTS 的官方英文字符可以流找到官方的TTS 的正确英文输入格式, 就可以模拟 之。

但目前程序抓到的 TTS官方英文信息流,这个值是空的。 所以这里需要重新设计抓包。

昨天实验了,不行,你试一下就知道为啥了。小爱会隐藏所有非中文的回答

caitouwh commented 1 year ago

关于小米英文TTS控制格式,我试过,确实没有程序里抓到英文TTS控制指令的包。 我的理解xiaogpt程序是在音箱的seessioni 信息里读取相关指令。而小米的session 中可能删除了或则不保存英文TTS信息,所以无法获取。

而理论上可以对音箱进行局域网抓包分析,获取小米云服务的音箱的实际控制报文,从而获取相应格式。

研究了一下小米控制协议,基于github 的 [python-miio] 库, (https://github.com/rytilahti/python-miio) 根据代码分析了 小米的,miIO 的协议,音箱发包控制机理如下 :

-------------------音箱控制命令发送原理-(控制指令通过UDP协议加密发送)----------------------------

发给音箱的指令 是用 socket 发送以下 json 字符串 到音箱 54321端口实现的。 比如要让小米小爱音箱读出文字“你好”,可以通过以下JSON报文实现: 1) 构建message 报文。 { "method": "text_to_speech", "params": { "text": "你好" } }

2) 程序具体发包的代码如下,(报文在网络中加密发送)。 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) .... message = json.dumps(message, separators=(',', ':')) message = self.miio_protocol.encrypt(message, self.token) ....... s.send(message)

----------------以下是根据代码理论推导的抓包后的解码方法(利用Token解码 )-------------------------

因此基于以上代码 分析,抓包后的信息还需要进一步进行解密才能理解。以下为推导出的操作过程。

先提问小米音箱用自身的的云服务说出一段流畅的英文,然后

步骤一 : 在局域网抓到音箱 54321端口 的控制报文UDP包, (应该是加密格式) 步骤二: 用程序,解码报文,获取正式控制指令。

根据代码,局域网抓到的包应该是用token加密后的加密报文形式 , 所以抓包以后可以用以下代码解密获取报文,

解密方法一 :(需要先安装 python-miio 库 )

import base64 from miio.protocol import MiIOProtocol token = "0123456789abcdef0123456789abcdef" encrypted_data = b'\x21\x31\x01\x00\x24\x6e..........................." (此处为抓包软件获得的UDP中的二进制加密报文)

miio_protocol = MiIOProtocol() decrypted_data = miio_protocol.decrypt(base64.b64decode(encrypted_data), bytes.fromhex(token))

print(decrypted_data.decode()) #

解密方法二 (不用python-miio 库 ,而继续用miservice 库, 理论上解密代码应该是 :)

from miservice.crypto import MiCrypto token = "0123456789abcdef0123456789abcdef" encrypted_data = b'\x21\x31\x01\x00\x24\x6e..........................." (此处为抓包软件获得的UDP中的二进制加密报文)

token_bytes = bytes.fromhex(token) crypto = MiCrypto(token_bytes) encrypted_bytes = bytes.fromhex(encrypted_message)

decrypted_bytes = crypto.decrypt(encrypted_bytes) decrypted_message = decrypted_bytes.decode('utf-8') print(decrypted_message)

有精力抓包的同学可以试下。

yihong0618 commented 1 year ago

@caitouwh 感谢,我有时间研究下,这个 issue 先关了哈