之前玩过一次RAG(检索增强生成),链接,十分简陋,现在已经无用了,时隔1年半以后又再需要实现一次。其实现在做RAG已经算相对成熟了,要求不高的话甚至可以直接跑langchain-chatchat这类现成的。因为要应付交差,可能还是得开发。这一次又是没用框架,借助了AI,折腾了3天,回头看经验没攒多少,但因为很久没写过什么像样的代码了,还是找回了点曾经的感觉。功能很简单,简单的数据管理和对话,撑死就做个本科毕设而已。
RAG的架构图已经有很多, 就是对用户的问题增加一个知识库检索的过程,然后把检索到的资料连同问题一起询问大模型得到最终答案。中间的细节会有很多,如果要求不高,其实就是在普通的对话过程前增加一个向量数据库的数据管理。
向量数据库这里选择用嵌入式开发,找了一下有chromadb和milvus-lite两个,由于水平下降,折腾一天milvus失败了,换成chroma。回想一下没坚持跑通milvus应该不是很正确,可能是受到milvus各文章不统一,还有AI辅助代码不准的影响。其实milvus的接口明显更好看。
大模型调用直接选择了网络API,用的siliconflow(顺便做一波邀请,给我涨点米)。它还提供了词嵌入的接口,用起来还算方便,而且有免费bge-m3模型。包括对话模型也有一些免费的比如GLM4-9B、qwen2.5-7B 。先照着官网文档用requests写了获取embeddings的,然后又找到它推荐用openai库进行对话交互。
整个过程除了废了的第一天以外,花费最多的应该就是前端。streamlit虽然简单,但是很多功能都得现查现用。第一次用navigation,我记得很多年前只能用sidebar.radio + 第三方美化库来实现; 第一次搞对话聊天的交互,官网有文档,确实也简单,不过我一直想做成聊天分成两部分,右侧用来显示检索结果。首先用columns布局来分。但是聊天输入也就是st.chat_input() 功能无法在column里置底,试了多次就是不行。也就是说st.chat_input只有放在纯粹的页面才会置地,否则就是跟正常控件布局一样,代码写在哪,页面就展现在哪。这点从官网文档中原话 :When st.chat_input is used in the main body of an app, it will be pinned to the bottom of the page. 研究了chat_message的输出方式才解决这个问题,就是把输入框和输出框分开用。
另外还有一个折腾的地方是右侧检索结果的展示, 我希望结果固定,不要跟着页面滚动。因为正常左侧聊天内容不断往上堆积,聊天输入置底会让页面往下滚动,如果此时右侧正常的输出检索结果,那就得滚上去看内容。于是问怎么破,反正没有一个AI解决了,都是说用css,但是用起来总有问题,只有kimi给了个正确的方式,用了个“<div style="position: fixed;” 而且居然宽度正好合适。但是输出这段div 和st其他输出方式又不关联,所以最后只能用最笨的办法,就是直接把结果拼在这段html源码里输出。最后还研究了一下APIKEY这类安全相关信息的存放位置,保存进 ./streamlit/secrets.toml似乎更专业点。
编码感受还是AI辅助对那些重复而不重要的地方反而效果更好,比如文本分chunk,我没用langchain,让AI模仿那个splitter,一次通过。比如获取网络请求什么的。通义灵码有个AI程序员,能帮你直接对着代码去修改,体验比纯对话好,但是效果还是一般,下次试试cline。
如果用了langchain其他框架估计核心代码10行搞定了,从质量和积累经验的角度是有点后悔,不过还是那句话写代码折腾对我来说还是挺值得的,代码就先不放了,等交差完了再说。下一步我肯定是要用AI开发一些小功能,比如能批量解决把内容手动贴到word、网页里,估计又要浪费很多时间去踩坑。