草庐IT

ChatGPT分享-如何开发一个LLM应用

misotofu 2023-03-28 原文

1​背景​

ChatGPT引起巨大的业界震撼,各行各业都在讨论大语言模型、通用人工智能。AI经历了五十多年的发展,现在正处于产业结构水平化发展的关键时期。这一变化源于NLP领域范式的转变,从“预训练+微调”向“预训练、提示、预测”模式演进。在这一新模式下,下游任务适应预训练模型,使得一个大型模型能适用于多个任务。这一变化为AI产业的水平化分工奠定了基础,大型语言模型成为基础设施,Prompt Engineering公司层出不穷,专注于连接用户和模型。AI产业的分工初步形成,包括底层基础设施(云服务商)、大型模型、Prompt Engineering平台和终端应用。随着AI产业变革,开发者可以充分利用大型语言模型(LLM)和Prompt Engineering来开发创新应用。

2Prompt-Ops下的应用

目前要开发一个基于LLM的应用,我们面临最大的工程上的问题是什么?

  • 大语言模型不能联网,无法获取最新的信息
  • 大语言模型没有我们私有的数据,无法回答垂直方向的问题
  • 大语言模型的开放API(text-davinci-003)没有像ChatGPT那样优秀的上下文能力
  • 大语言模型无法驱动其他工具。

2.1   Langchain等工程框架解决了这些工程上的问题

以Langchain为例简单来说:LangChain是 LLM 底层能力的封装,是一种 Prompt Engineering或者说是Prompt-Ops。

  • 它可以接入各种不同LLM的服务,抽象了各种大语言模型的调用
  • 它可以创建各种PromptTemplate,实现定制化的Prompt模版
  • 它可以创建链来组合调用PromptTemplate
  • 它可以通调用各种工具,实现GPT-3目前不擅长的事情,比如搜索/数学/链接私有数据库/Python代码
  • 它可以使用代理, 驱动LLM 来确定采取哪些行动以及采取何种顺序。动作可以是使用工具并观察其输出,也可以是返回给用户。
  • 它可以通过它的Memory模块,来实现对话历史的建模。

2.2    一些Langchain的开发例子

2.2.1   结合搜索的GPT

这是一个用ChatGPT和LangChain开发的Demo对比的例子,输入的都是“谁是周杰伦的老婆?她当前的年龄乘以0.23是多少?”。可以看出ChatGPT或者GPT-3.5因为没有搜索能力,回答的结果是错误的。右边用LangChain结合OpenAI的GPT-3.5的API则输出了正确的结果,他会逐步去搜索获得正确信息,得出正确结果,而且中间的过程是框架自动处理的,我除了输入问题没有其他操作。

2.2.2   将自然语言转为Python代码并自行纠错

这是一个非常令人震惊的例子,在这个流程中,它自己发现函数未定义的报错并自行纠正。

2.2.3   使用GPT-3 + Statmuse + Langchain查询NBA数据

Fuzzy API composition: querying NBA stats with GPT-3 + Statmuse + Langchain

使用Langchain与体育数据搜索网站相结合,提问复杂的数据问题并得到准确的回复。例如:“波士顿凯尔特人队在这个 2022-2023 赛季的 NBA 赛季场均防守得分是多少?与他们上赛季的平均水平相比,百分比变化如何?”

2.2.4   连接Python REPL打开浏览器播放音乐

一个蛮科幻的场景,我用Langchain接入了Python REPL工具,输入“给我放一首歌”,它导入了webBrowser包,调用代码打开了浏览器,给我播放了一首 《never gonna give you up》

def pythonTool():
bash = BashProcess()
python_repl_util = Tool(
"Python REPL",
PythonREPL().run,
"""A Python shell. Use this to execute python commands.
Input should be a valid python command.
If you expect output it should be printed out.""",
)
command_tool = Tool(
name="bash",
descriptinotallow="""A Bash shell. Use this to execute Bash commands. Input should be a valid Bash command.
If you expect output it should be printed out.""",
func=bash.run,
)
# math_tool = _get_llm_math(llm)
# search_tool = _get_serpapi()
tools = [python_repl_util, command_tool]
agent = initialize_agent(tools, llm, agent="zero-shot-react-description", verbose=True)
agent.run("给我播放一首音乐")

2.2.5   连接私有数据

连接私有数据对第三方企业做LLM应用来说非常重要。下面举几个例子

  • LangchainJs的文档,结合了Langchain:AI-powered search for LangchainJS Documentation,你可以直接问文档内容、技术细节。

  • 数据库产品 Supabase也做了同样的事情,将自己的文档与ChatGPT相连接,使得开发者可以很方便的询问/查找自己遇到的技术问题。https://supabase.com/docs

  • 政府信息问答的一个例子:Co-pilot for government
法律公文和政策条款一般都非常复杂繁琐,这个demo中将旧金山政府的信息用Langchain与GPT结合,做到询问其中细节能获得准确回复的效果。

> Entering new AgentExecutor chain...
I need to find out the size limit for a storage shed without a permit and then search for sheds that are smaller than that size.
Action: SF Building Codes QA System
Action Input: "What is the size limit for a storage shed without a permit in San Francisco?"
Observation: The size limit for a storage shed without a permit in San Francisco is 100 square feet (9.29 m2).


Thought:Now that I know the size limit, I can search for sheds that are smaller than 100 square feet.
Action: Google
Action Input: "Storage sheds smaller than 100 square feet"
Observation: Results 1 - 24 of 279 ...


Thought:I need to filter the Google search results to only show sheds that are smaller than 100 square feet and suitable for backyard storage.
Action: Google
Action Input: "Backyard storage sheds smaller than 100 square feet"
Thought:I have found several options for backyard storage sheds that are smaller than 100 square feet and do not require a permit.
Final Answer: The size limit for a storage shed without a permit in San Francisco is 100 square feet. There are many options for backyard storage sheds that are smaller than 100 square feet and do not require a permit, including small sheds under 36 square feet and medium sheds between 37 and 100 square feet.

2.3   结合私有数据问答

LLM应用与私有数据交互非常重要,我看到无数人在问一些ChatGPT无法回答的问题了:问认不认识谁、问自己公司业务细节、问各种可能不包含在预训练数据集里的东西。这些都已用Langchain和LlaMaIndex来解决。试想一下,将私有数据与LLM相结合,将改变数据原有的访问方式,通过问答能很自然地获取到自己需要的信息,这是比当前的搜索/打标分类都要高效的数据交互方式。

2.3.1   如何构建一个基于私有数据的LLM问答系统

向量数据库现在看起来是构建LLM App中很关键的一个组件。首先 LLM 的预训练和微调过程不可能包含我们所期待的私有数据,因此如何将LLM关联到私有数据成为一个很关键的需求。而且LLM的“接口”-自然语言通常不是像Key-Value的映射那样精确地。而且在这一阶段我们希望LLM去理解我们的知识库,而不是简单的在其中搜索相同的字符串,我们希望询问关于我们知识库的细节,并给出一定理解后的答案(以及来源),这样匹配向量这样的搜索方式是一个非常合适且关键的解决方案。还有一个关键点是,LLM在每次调用是按token计费(即文本量),并且目前的接口的上下文有着4096 tokens的限制。,因此面对庞大的数据,我们也不可能将所有的数据一次性传给LLM。因此才有了第一张图那个流程图的结构。本地预先将我们私有的数据转成向量存在Qdrant里,用户问答时,将用户的问题转为向量,然后去Qdrant里进行搜索(相似性匹配)得到Top K个结果,然后将这些结果(注意这里的结果已经是自然语言了)传给LLM进行总结输出。

2.3.2   结合私有数据问答的抽象流程

这里使用Langchain社区博客的流程图为例

私有数据分割成小于LLM上下文的分块,创建向量后存入向量数据库

将问题计算向量后在向量数据库进行相似性搜索,算出相关性较高的top k个结果后拼接prompt送往LLM获得答案。

2.3.3   重要组件

  • OpenAI Ada模型:text-embedding-ada-002模型可以快速编码一个1536维的向量,我们可以使用这个向量来计算文本之间的相似性。
  • Langchain / LLamaIndex:Langchain包含多种文本拆分器与文档连接器,方便将文件进行拆分并且在向量数据库中索引;LlamaIndex 可以从向量存储加载数据,类似于任何其他数据连接器。然后可以在 LlamaIndex 数据结构中使用此数据。
  • 向量数据库,选型比较多:Chroma / FAISS / Milvus / PGVector / Qdrant / Pinecone等等。

2.3.4   OpenAI私有部署与成本的问题

再来聊聊最近那个OpenAI私有部署的新闻,如果用Langchain来做链接,面对庞大的私有数据,用一个embedding模型(OpenAI的ada)计算输入问题向量,用Qdrant等向量数据库来管理私有数据的向量和向量搜索,用Langchain来做中间的链接虽然可以解决问题,但是token的消耗却是不容忽视的成本问题。私有部署+微调可能能解决大部分前面提到的问题。可能是有钱大公司用Model instance和fine-tuning,小公司独立开发者用Langchain等框架。更未来OpenAI的LLM服务能力外溢,可能不需要Prompt了,甚至把Langchain的功能都能包括了,LLM应用的开发接入也许只需要一个接口调用。

2.4    2023年的LLM应用技术栈

2023 用来简单搭建 AI Demo 的最新技术栈:

  • 托管: Vercel
  • 前端: Next.js
  • 后端: Vercel with flask
  • 数据库: Supabase
  • AI 模型: OpenAI / Replicate / Hugging Face
  • LLM框架层: LangChain / LLaMaIndex
  • 向量存储/搜索: Pinecone / FAISS

2.5   Prompt-Ops 目前最大的问题

一些关于 Langchain 这类Prompt-Ops这类工具的反对观点:stream.thesephist.com主要问题是在这类工具/框架,将自然语言作为代码和LLM的连接,使用非确定性语言本身作为控制流,有点疯狂。而且本身评估模型输出效果现在是个很麻烦的事,没有很好的解决方案,很多都是维护一个巨大的电子表格,靠人去评估。(也有用LLM评估LLM的方案,还比较早期)所以要投入生产,真实面对用户而不是作为twitter演示可能还有很多工作要做。

详细说说测试环节面临的巨大挑战。假如你的产品有一套研发阶段效果很好的prompt,交给测试后,可能测试上百条上千条就能看出问题了。由于效果无法保证,真正推出给c端用户会面临很大的挑战。而且没有用微调服务或者model instance的话,如果OpenAI更新了模型,你的生产环境的prompt可能需要全部重新测试一下效果。你的prompt也需要和代码一样按版本来管理,不管有没有prompt变更,每个版本上线前都需要进行回归测试。没有好的自动化评估方案的话,大量的case都需要测试人工来看会耗费非常多的人力。

结合私有数据的LLM应用目前开发起来在工程上已经有很多不错的方案了,很容易跑出效果不错的demo,但还是需要非常谨慎对待这样一种应用。毕竟我们不只是要做一个在社交媒体或者Leader面前演示的项目。提供给用户输入的是一个对话框,自然语言宽泛到即使你测试上万条结果也可能出现意想不到的结果,毕竟像new bing和chatGPT这样的产品也会被Prompt Injection。面对这种不确定性,工程上如何去避免,测试如何去覆盖都是一个成熟产品待解决或者说还有很多工作可以做的问题。

但我觉得也不必完全否定这类Prompt-Ops工具/框架,毕竟现阶段确实能做出很多不错的demo来验证想法。

3未来可能的一些产品形态

聊聊ChatGPT API开放后LLM应用可能的形态。

  • 对话聊天,是最直观的应用方式,在API上做好对话历史的管理。
  • 虚拟角色聊天,在基础对话聊天上,对API的prefix_message上做一些角色定义的prompt,可以实现类似Character.ai的效果。更深入可能是作为游戏角色、虚拟人、XR助手等。
  • 类似Notion的文本辅助撰写工具,目前Notion、FlowUs都做了类似的应用。未来各家社区的发布器集成也是一个趋势,减低用户发布门槛,提升发布质量。
  • 数据总结性工具,实现Chat-Your-Data,提供文档的输入给用户,让用户可以与自己提供的数据聊天,本质只涉及到互联网公开的数据与用户私有的数据。
  • 大企业的Chat-Your-Data,各家大公司在原有业务基础上,结合大企业的私有数据,提供更优质的服务。比如结合用户点评的大众点评,能够用“想去放neo-soul和R&B音乐的酒吧”,比如我们的商详页,能够总结所有用户对这个商品的评价,并且可以让用户对这个商品的资料进行问答。
  • 与政务、医疗、教育等领域结合,融合线上机构官网、线下大屏,提供更好的市民服务。
  • 与其他工具比如IFTTT或者各种私有协议相结合,实现LLM可以访问更多的工具和系统,举个例子:iot场景,Office Copilot。
LLM应用实际是一种新的人机交互方式,能够让用户用自然语言与我们目前的系统沟通,很多应用甚至可以简化到只有一个聊天窗口。

4总结

目前来说由于通用大模型训练/部署的高成本来说,产业水平化分工的条件基本成熟,世界上并不需要很多个大模型,做LLM的应用将会是中小型企业和个人开发者的必然选择。新形态的编程/工程范式需要工程师去及时学习理解。目前的开源技术栈已经能满足大部分产品的需求,可以尝试快速实践demo来验证想法。

参考资料:

  • https://blog.langchain.dev/tutorial-chatgpt-over-your-data/
Tutorial: ChatGPT Over Your Data

  • https://qdrant.tech/articles/langchain-integration/
Question Answering with LangChain and Qdrant without boilerplate

  • https://mp.weixin.qq.com/s/VZ6n4qlDx4bh41YvD1HqgQ
Atom Capital:深入探讨ChatGPT带来的产业变革

有关ChatGPT分享-如何开发一个LLM应用的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  10. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

随机推荐