全局框选内容,并取得映射key 传入AI提问 实现思路

109 阅读6分钟

需求

如题,需要实现选中某个字段,去拿到对应的映射key以及选中字段,去数据库中进行检索返回要提问的问题,随后用户选择某个问题,将此问题输入到AI问答窗口中。

限制

  1. 项目过于庞大,要求敏捷开发
  2. 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,进行问题获取

优点

  1. 非侵入式,不会对dom进行批量遍历处理信息
  2. 简单粗暴
  3. 即使数据源(key)做更改,也不会影响获取真实的key

缺陷

  1. 即使可以二次数据库筛选key,但仍然精度不够

扩展

  1. 根据,dom位置,判断top,left,right,bottom,可以相对判断大概位置(未来OCR?)或者在页面大概布局中,添加一些布局的位置属性以及数据来源(接口名称),根据selection的dom信息向上查找,可以再精确一些判断(复杂页面,或者使用率较高的页面做补充)

方案二 babel插件-AST解析 + webpack-loader

大概思路

AST解析注入路径,数据源,loader注入

babel-AST节点内更改有 {} 标识内的节点属性(追加)

以webpack-loader的plugin插入形式,对指定文件类型、范围内的内容做筛查

以babel-plugin的形式开发需求。

JSXExpressionContainer内依次判断代码块类型,逐个状态处理(大括号代码块内的所有可能项)

优点

  1. 半侵入式,从编译阶段出发追加节点属性,不需要在项目内进行较大更改
  2. 足够精确,理论上可以获取到对应选中文字的key以及来源

缺点

  1. 开发难度很大,针对AST节点内数量众多的type分别做处理(判断式、函数返回变量,节点,常量、HOC等),嵌套内容需要遍历,复杂场景下需要逐步探索

  2. 比较依赖数据源的链接,如果源链接被更改,只能索引查询到对应映射的字段(因为是在静态编译阶段去处理的属性记录操作)

    1. example:
    2.  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 字段 ,但是逻辑运行来看,这样是没问题的,这就是问题所在。

  1. 逻辑复杂对应出来的就是开发时间长度的拉伸。
  2. 无法获取到动态返回数据、组件内的信息

方案三 依次在页面内做属性写入

最原始的方法往往最直接。

封装一个组件,内部将属性以及对应props的信息注入其中。

优点

  1. 精确,拿到有key必然是该值的对应属性,可以选择性注入key(若不是map遍历的),避免垃圾kv传入
  2. 可持续性,首次写入需要时间,但后续不需要二次处理

缺点

  1. 复杂,需要在当前所有项目内依次做节点注入操作,工作量大,耗时长

  2. 后续未来的开发流程需要在注入属性的时候主动添加组件

问题一结论

上述方案中,一是最为简单快捷有效的,虽然在精确度上略有失准,但是可以通过一些补偿措施进行矫正。
二从理论上来说,是最为精准的,应该是最佳实现 但是在开发难度上上升了好几个维度,且动态代码需要在其他的逻辑点去想办法注入(我觉得是可以实现的)。只是当前开发节奏来说,不允许进行此方案。
三就是最笨的办法,但是也是稳定的,适合小项目,刚开始的项目内进行使用。

所以方案一是当前环境下的最佳实现

问题二:如何和下方AI问答模块做通信(dify为入口)

目前观测信息来看,对应的 iframe 嵌入式没有开放直接的信息通信方式,所以需要稍微绕一些路:

方案一 iframe做同源

核心思路就是操作dom,模拟点击,输入来实现。

但是有个问题就是iframe内获取到dom必须得iframe和当前操作的网站地址同源(源包括域名、端口、协议)(example: baidu.com/dify )才可以。

优点

  1. 简单粗暴
  2. 开发相对快捷,不会对项目造成太大的影响

缺点

  1. 做不到深度定制化,只能在官方提供的iframe嵌套上做模拟的输入。(样式可以做简单处理(官方提供))入口
  2. 目前来看 dify略显简洁,需要确认

参考

  1. 通信type

方案二 项目内重新开发AI对话模块

不走iframe,也就没有了种种限制。

直接重新开发一份AI对话模块,若无样式功能要求可以找一些线上可用的对话模块。

在此基础上添加通信能力即可。

优点

  1. 未来可以接受深度定制化开发
  2. 通信方式多样,可以单开一个服务通过 postMessage 通信,也可以项目内 emitter 通信

缺点

  1. 相对耗时,前后端工作量相比其他方案是多不少的
  2. 需要对dify官方API有一定了解

参考:

Html 参考

问题二结论

方案一就是针对iframe去进行的通信。同源下操作dom去模拟输入和点击,逻辑简单粗暴,但是有效。 后续也可以单独封装一个应用,内部同源,并向外吐出postMessage相关信息做通信。
方案二的话就是简单直接的重新定制化开发,像dify的话是有前端直接可用的api,无需(较少)后端介入,其他的AI接入可能需要后端支持。

这个方案的应用就看需求了,若是改动较大,就是二,不然一就是最佳实现。