需求
如题,需要实现选中某个字段,去拿到对应的映射key以及选中字段,去数据库中进行检索返回要提问的问题,随后用户选择某个问题,将此问题输入到AI问答窗口中。
限制
- 项目过于庞大,要求敏捷开发
- AI问答窗口为iframe嵌入
问题一:文字框选链接AI自动取得问题
先拿到框选文字,以及node,取得完整文字:
sel即为框选中的组信息,里面包含选中node,父级node等信息
如何获取对应key??
方案一 接口数据kv反转
接口数据kv反转,Map记录所有包含val的key
原先
const a = {
b: 1,
c: 2,
d: {
e: 2
}
}
现在
const mapA = {
'1': ['a.b'],
'2': ['a.c', 'a.d.e']
}
sel中的文字信息进行has判断,得到所有的key
然后跟后端表中拥有key做对比(后端做也行) 取得相对精确的key,进行问题获取
优点
- 非侵入式,不会对dom进行批量遍历处理信息
- 简单粗暴
- 即使数据源(key)做更改,也不会影响获取真实的key
缺陷
- 即使可以二次数据库筛选key,但仍然精度不够
扩展
- 根据,dom位置,判断top,left,right,bottom,可以相对判断大概位置(未来OCR?)或者在页面大概布局中,添加一些布局的位置属性以及数据来源(接口名称),根据selection的dom信息向上查找,可以再精确一些判断(复杂页面,或者使用率较高的页面做补充)
方案二 babel插件-AST解析 + webpack-loader
大概思路
AST解析注入路径,数据源,loader注入
babel-AST节点内更改有 {} 标识内的节点属性(追加)
以webpack-loader的plugin插入形式,对指定文件类型、范围内的内容做筛查
以babel-plugin的形式开发需求。
JSXExpressionContainer内依次判断代码块类型,逐个状态处理(大括号代码块内的所有可能项)
优点
- 半侵入式,从编译阶段出发追加节点属性,不需要在项目内进行较大更改
- 足够精确,理论上可以获取到对应选中文字的key以及来源
缺点
-
开发难度很大,针对AST节点内数量众多的type分别做处理(判断式、函数返回变量,节点,常量、HOC等),嵌套内容需要遍历,复杂场景下需要逐步探索
-
比较依赖数据源的链接,如果源链接被更改,只能索引查询到对应映射的字段(因为是在静态编译阶段去处理的属性记录操作)
- example:
-
const reqData = { data: { userName: 'Arthas' } } const storeData = { data: { name: '' } } useEffect(() => { // res 等同于 reqData reqFn().then(res => { storeData.data.name = res.data.userName }) }, []) <div> {storeData.data.name} // Arthas </div>
实际框选中 Arthas 后,索引查到的是 storeData.data.name,这明显是不符合预期的,应该拿到 userName 字段 ,但是逻辑运行来看,这样是没问题的,这就是问题所在。
- 逻辑复杂对应出来的就是开发时间长度的拉伸。
- 无法获取到动态返回数据、组件内的信息
方案三 依次在页面内做属性写入
最原始的方法往往最直接。
封装一个组件,内部将属性以及对应props的信息注入其中。
优点
- 精确,拿到有key必然是该值的对应属性,可以选择性注入key(若不是map遍历的),避免垃圾kv传入
- 可持续性,首次写入需要时间,但后续不需要二次处理
缺点
-
复杂,需要在当前所有项目内依次做节点注入操作,工作量大,耗时长
-
后续未来的开发流程需要在注入属性的时候主动添加组件
问题一结论
上述方案中,一是最为简单快捷有效的,虽然在精确度上略有失准,但是可以通过一些补偿措施进行矫正。
二从理论上来说,是最为精准的,应该是最佳实现 但是在开发难度上上升了好几个维度,且动态代码需要在其他的逻辑点去想办法注入(我觉得是可以实现的)。只是当前开发节奏来说,不允许进行此方案。
三就是最笨的办法,但是也是稳定的,适合小项目,刚开始的项目内进行使用。
所以方案一是当前环境下的最佳实现
问题二:如何和下方AI问答模块做通信(dify为入口)
目前观测信息来看,对应的 iframe 嵌入式没有开放直接的信息通信方式,所以需要稍微绕一些路:
方案一 iframe做同源
核心思路就是操作dom,模拟点击,输入来实现。
但是有个问题就是iframe内获取到dom必须得iframe和当前操作的网站地址同源(源包括域名、端口、协议)(example: baidu.com/dify )才可以。
优点
- 简单粗暴
- 开发相对快捷,不会对项目造成太大的影响
缺点
- 做不到深度定制化,只能在官方提供的iframe嵌套上做模拟的输入。(样式可以做简单处理(官方提供))入口
- 目前来看 dify略显简洁,需要确认
参考
- 通信type
方案二 项目内重新开发AI对话模块
不走iframe,也就没有了种种限制。
直接重新开发一份AI对话模块,若无样式功能要求可以找一些线上可用的对话模块。
在此基础上添加通信能力即可。
优点
- 未来可以接受深度定制化开发
- 通信方式多样,可以单开一个服务通过 postMessage 通信,也可以项目内 emitter 通信
缺点
- 相对耗时,前后端工作量相比其他方案是多不少的
- 需要对dify官方API有一定了解
参考:
Html 参考
问题二结论
方案一就是针对iframe去进行的通信。同源下操作dom去模拟输入和点击,逻辑简单粗暴,但是有效。
后续也可以单独封装一个应用,内部同源,并向外吐出postMessage相关信息做通信。
方案二的话就是简单直接的重新定制化开发,像dify的话是有前端直接可用的api,无需(较少)后端介入,其他的AI接入可能需要后端支持。
这个方案的应用就看需求了,若是改动较大,就是二,不然一就是最佳实现。