由于强大的机器学习模型(特别是大型语言模型,如 Claude、Meta 的 LLama 2 等)被托管平台/服务作为 API 调用公开,生成式 AI 开发已经民主化。这使开发人员摆脱了对基础设施的担忧,并让他们专注于核心业务问题。这也意味着开发人员可以自由使用最适合其解决方案的编程语言。Python 通常是 AI/ML 解决方案的首选语言,但该领域具有更大的灵活性。
在这篇文章中,您将看到如何利用 Go 编程语言将向量数据库和诸如检索增强生成 (RAG) 等技术与 langchaingo 一起使用。如果您是想要如何构建和学习生成式 AI 应用程序的 Go 开发人员,那么您来对地方了!
如果你正在寻找有关使用 Go for AI/ML 的介绍性内容,请随时查看我以前的博客和这个领域的开源项目。
首先,让我们退后一步,在深入了解本文的动手部分之前了解一些背景信息。
大型语言模型 (LLM) 和其他基础模型已经在大量数据语料库上进行了训练,使它们能够在许多自然语言处理 (NLP) 任务中表现出色。但最重要的限制之一是,大多数基础模型和 LLM 使用静态数据集,该数据集通常具有特定的知识截止点(例如,2022 年 1 月)。
例如,如果你要问一个发生在截止时间之后的事件,它要么无法回答(这很好),要么更糟,自信地回答不正确的回答——这通常被称为幻觉。
我们需要考虑这样一个事实,即 LLM 只根据他们接受训练的数据做出回应——这限制了他们准确回答有关专业或专有主题的问题的能力。例如,如果我要问一个关于特定AWS服务的问题,LLM可能会(也可能不会)给出准确的答案。如果 LLM 可以使用官方的 AWS 服务文档作为参考,那不是很好吗?
它通过在响应生成过程中动态检索外部信息来增强 LLM,从而将模型的知识库扩展到其原始训练数据之外。基于RAG的解决方案包含一个向量存储,可以对其进行索引和查询以检索最新和相关的信息,从而将LLM的知识扩展到其训练截止点之外。当配备 RAG 的 LLM 需要生成响应时,它首先查询向量存储以查找与查询相关的相关最新信息。这个过程确保模型的输出不仅基于其预先存在的知识,而且用最新信息进行扩充,从而提高其响应的准确性和相关性。
虽然这篇文章只关注 RAG,但还有其他方法可以解决这个问题,每种方法都有其优点和缺点:
我在上一段中多次提到向量存储。这些只不过是存储和索引向量嵌入的数据库,这些向量嵌入是文本、图像或实体等数据的数值表示。嵌入帮助我们超越基本搜索,因为它们代表了源数据的语义含义——因此有了语义搜索这个词,它是一种理解单词的含义和上下文以提高搜索准确性和相关性的技术。矢量数据库还可以存储元数据,包括对嵌入的原始数据源(例如,Web 文档的 URL)的引用。
得益于生成式人工智能技术,矢量数据库也出现了爆炸式增长。其中包括已建立的 SQL 和 NoSQL 数据库,您可能已经在体系结构的其他部分(例如 PostgreSQL、Redis、MongoDB 和 OpenSearch)中使用这些数据库。但也有为矢量存储定制的数据库。其中一些包括 Pinecone、Milvus、Weaviate 等。
好吧,让我们回到 RAG...
概括地说,基于 RAG 的解决方案具有以下工作流。这些通常作为有凝聚力的管道执行:
归根结底,这是作为更大应用程序的一部分的集成,其中上下文数据(语义搜索结果)提供给 LLM(以及提示)。
每个工作流步骤都可以使用不同的组件执行。博客中使用的包括:
您将了解这些单独的部分是如何工作的。我们将在随后的博客中介绍此体系结构的其他变体。
确保您拥有:
我们可以使用一个 Docker 镜像!
docker run --name pgvector --rm -it -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres ankane/pgvector
通过从其他终端登录 PostgreSQL(使用 )来激活扩展:pgvectorpsql
# enter postgres when prompted for password
psql -h localhost -U postgres -W
CREATE EXTENSION IF NOT EXISTS vector;
克隆项目存储库:
git clone https://github.com/build-on-aws/rag-golang-postgresql-langchain
cd rag-golang-postgresql-langchain
此时,我假设您的本地计算机已配置为使用 Amazon Bedrock
我们要做的第一件事是将数据加载到 PostgreSQL 中。在这种情况下,我们将使用现有的网页作为信息来源。我已经使用过这个开发人员指南——但请随意使用你自己的!请确保在后续步骤中相应地更改搜索查询。
export PG_HOST=localhost
export PG_USER=postgres
export PG_PASSWORD=postgres
export PG_DB=postgres
go run *.go -action=load -source=https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-general-nosql-design.html
应获得以下输出:
loading data from https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-general-nosql-design.html
vector store ready - postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable
no. of documents to be loaded 23
给它几秒钟。最后,如果一切顺利,您应该会看到以下输出:
data successfully loaded into vector store
要进行验证,请返回终端并检查表:psql
\d
您应该会看到几个表 — 和 .这些是创建的,因为我们没有明确指定它们(没关系,方便入门! 包含集合名称,同时存储实际的嵌入。langchain_pg_collectionlangchain_pg_embeddinglangchaingolangchain_pg_collectionlangchain_pg_embedding
| Schema | Name | Type | Owner |
|--------|-------------------------|-------|----------|
| public | langchain_pg_collection | table | postgres |
| public | langchain_pg_embedding | table | postgres |
您可以自省这些表:
select * from langchain_pg_collection;
select count(*) from langchain_pg_embedding;
select collection_id, document, uuid from langchain_pg_embedding LIMIT 1;
您将在langchain_pg_embedding表中看到 23 行,因为这是我们的网页源被拆分为 langchain 文档的数量(当您加载数据时,请参阅上面的应用程序日志)
快速绕道了解其工作原理......
数据加载实现在 load.go 中,但让我们看看我们如何访问向量存储实例(在 common.go 中):
brc :=bedrockruntime.NewFromConfig(cfg)
embeddingModel, err :=bedrock.NewBedrock(bedrock.WithClient(brc), bedrock.WithModel(bedrock.ModelTitanEmbedG1))
//...
store, err=pgvector.New(
context.Background(),
pgvector.WithConnectionURL(pgConnURL),
pgvector.WithEmbedder(embeddingModel),
)
回到 load.go 中的加载过程。为此,我们首先使用内置的 HTML 加载器以 ( 函数) 切片的形式获取数据。schema.DocumentgetDocslangchaingo
docs, err :=documentloaders.NewHTML(resp.Body).LoadAndSplit(context.Background(), textsplitter.NewRecursiveCharacter())
然后,我们将其加载到 PostgreSQL 中。与其自己写所有东西,不如使用向量存储抽象,使用高级函数:langchaingoAddDocuments
_, err=store.AddDocuments(context.Background(), docs)
伟大。我们设置了一个简单的管道来获取数据并将其摄取到 PostgreSQL 中。让我们好好利用吧!
让我们问一个问题。我将使用“我可以使用哪些工具来设计 dynamodb 数据模型?”,这与我用作数据源的文档相关 - 请根据您的场景随意调整它。
export PG_HOST=localhost
export PG_USER=postgres
export PG_PASSWORD=postgres
export PG_DB=postgres
go run *.go -action=semantic_search -query="what tools can I use to design dynamodb data models?" -maxResults=3
您应该会看到类似的输出 — 请注意,我们选择最多输出三个结果(您可以更改它):
vector store ready==============similarity search results==============similarity search info - can build new data models from, or design models based on, existing data models that satisfy
your application's data access patterns. You can also import and export the designed data
model at the end of the process. For more information, see Building data models with NoSQL Workbench
similarity search score - 0.3141409============================similarity search info - NoSQL Workbench for DynamoDB is a cross-platform, client-side GUI
application that you can use for modern database development and operations. It's available
for Windows, macOS, and Linux. NoSQL Workbench is a visual development tool that provides
data modeling, data visualization, sample data generation, and query development features to
help you design, create, query, and manage DynamoDB tables. With NoSQL Workbench for DynamoDB, you
similarity search score - 0.3186116============================similarity search info - key-value pairs or document storage. When you switch from a relational database management
system to a NoSQL database system like DynamoDB, it's important to understand the key differences
and specific design approaches.TopicsDifferences between relational data
design and NoSQLTwo key concepts for NoSQL designApproaching NoSQL designNoSQL Workbench for DynamoDB
Differences between relational data
design and NoSQL
similarity search score - 0.3275382============================
现在你在这里看到的是前三个结果(感谢)。-maxResults=3
请注意,这不是我们问题的答案。这些是来自我们的向量存储的结果,它们在语义上接近查询——这里的关键字是语义。多亏了 中的向量存储抽象,我们能够轻松地将源数据摄取到 PostgreSQL 中,并使用该函数获得与我们的查询相对应的顶级结果(参见 query.go 中的函数):langchaingoSimilaritySearchNsemanticSearch
请注意,(在撰写本文时)langchaingo 中的 pgvector 实现使用余弦距离向量运算,但 pgvector 也支持 L2 和内积 - 有关详细信息,请参阅 pgvector 文档。
好的,到目前为止,我们有:
这是 RAG(检索增强生成)的垫脚石 - 让我们看看它的实际效果!
为了执行基于 RAG 的搜索,我们运行与上面相同的命令(几乎),只是 () 略有变化:actionrag_search
export PG_HOST=localhost
export PG_USER=postgres
export PG_PASSWORD=postgres
export PG_DB=postgres
go run *.go -action=rag_search -query="what tools can I use to design dynamodb data models?" -maxResults=3
这是我得到的输出(在您的情况下可能略有不同):
Based on the context provided, the NoSQL Workbench for DynamoDB is a tool that can be used to design DynamoDB data models. Some key points about NoSQL Workbench for DynamoDB:
- It is a cross-platform GUI application available for Windows, macOS, and Linux.
- It provides data modeling capabilities to help design and create DynamoDB tables.
- It allows you to build new data models or design models based on existing data models.
- It provides features like data visualization, sample data generation, and query development to manage DynamoDB tables.
- It helps in understanding the key differences and design approaches when moving from a relational database to a NoSQL database like DynamoDB.
因此,总而言之,NoSQL Workbench for DynamoDB 似乎是一个专门为建模和使用 DynamoDB 数据模型而设计的有用工具。
正如你所看到的,结果不仅仅是“这是你的查询的前 X 个响应”。相反,这是对这个问题的精心设计的回答。让我们再次窥探幕后花絮,看看它是如何工作的。
与摄取和语义搜索不同,基于 RAG 的搜索不会直接由向量存储实现公开。为此,我们使用一条链来处理以下事项:langchaingolangchaingo
这是链的样子(参考 query.go 中的函数):ragSearch
result, err :=chains.Run(
context.Background(),
chains.NewRetrievalQAFromLLM(
llm,
vectorstores.ToRetriever(store, numOfResults),
),
question,
chains.WithMaxTokens(2048),
)
这只是一个例子。我尝试了一个不同的问题,并将 maxResults 增加到 10,这意味着将使用向量数据库中的前 10 个结果来制定答案。
go run *.go -action=rag_search -query="how is NoSQL different from SQL?" -maxResults=10
结果(同样,对你来说可能不同):
Based on the provided context, there are a few key differences between NoSQL databases like DynamoDB and relational database management systems (RDBMS):
1. Data Modeling:
- In RDBMS, data modeling is focused on flexibility and normalization without worrying much about performance implications. Query optimization doesn't significantly affect schema design.
- In NoSQL, data modeling is driven by the specific queries and access patterns required by the application. The data schema is designed to optimize the most common and important queries for speed and scalability.
2. Data Organization:
- RDBMS organizes data into tables with rows and columns, allowing flexible querying.
- NoSQL databases like DynamoDB use key-value pairs or document storage, where data is organized in a way that matches the queried data shape, improving query performance.
3. Query Patterns:
- In RDBMS, data can be queried flexibly, but queries can be relatively expensive and don't scale well for high-traffic situations.
- In NoSQL, data can be queried efficiently in a limited number of ways defined by the data model, while other queries may be expensive and slow.
4. Data Distribution:
- NoSQL databases like DynamoDB distribute data across partitions to scale horizontally, and the data keys are designed to evenly distribute the traffic across partitions, avoiding hot spots.
- The concept of "locality of reference," keeping related data together, is crucial for improving performance and reducing costs in NoSQL databases.
In summary, NoSQL databases prioritize specific query patterns and scalability over flexible querying, and the data modeling is tailored to these requirements, in contrast with RDBMS where data modeling focuses on normalization and flexibility.
边做边学是一个很好的方法。如果您到目前为止已经遵循并执行了该应用程序,那就太好了!
我建议您尝试以下方法:
这是一个简单示例,可让您更好地了解构建基于 RAG 的解决方案的各个步骤。这些可能会根据实现而略有变化,但高级想法保持不变。
我用作框架。但这并不总是意味着你必须使用一个。如果您需要在应用程序中进行精细控制或框架不符合您的要求,您还可以删除抽象并直接调用 LLM 平台 API。与大多数生成式 AI 一样,这个领域正在迅速发展,我对 Go 开发人员有更多选择来构建生成式 AI 解决方案持乐观态度。langchaingo
如果您有任何反馈或问题,或者您希望我围绕此主题介绍其他内容,请随时在下面发表评论!
祝您建造愉快!
原文标题:How To Use Retrieval Augmented Generation (RAG) for Go Applications
原文链接:https://dzone.com/articles/how-to-use-retrieval-augmented-generation-rag-for
作者:Abhishek Gupta
编译:LCR
ready()类似于 onLoad()事件
ready()可以写多个,按顺序执行
$(document).ready(function(){})等价于$(function(){})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ready事件</title>
<script src="js/jquery-3.4.1.js" type="text/javascript"></script>
<script type="text/javascript">
// 文档载入完便触发ready方法
$(document).ready(function(){
$("div").html("ready go...");
})
// $(document).ready(function(){})==$(function(){})
$(function(){
$("p").click( function () {
$(this).hide();
});
});
$(function(){
$("#btntest").bind("click",function(){
$("div").html("剁吧...");
});
});
</script>
</head>
<body>
<h3>页面载入时触发ready()事件</h3>
<div></div>
<input id="btntest" type="button" value="剁手" />
<p>aaa</p>
<p>bbbb</p>
<p>ccc</p>
<p>dddd</p>
</body>
</html>
为被选元素添加一个或多个事件处理程序,并规定事件发生时运行的函数。
$(selector).bind( eventType [, eventData], handler(eventObject));
eventType :是一个字符串类型的事件类型,就是你所需要绑定的事件。
这类类型可以包括如下:
blur, focus, focusin, focusout, load, resize, scroll, unload, click, dblclick
mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter
mouseleave,change, select, submit, keydown, keypress, keyup, error
[, eventData]:传递的参数,格式:{名:值,名2:值2}
handler(eventObject):该事件触发执行的函数
<script type="text/javascript">
$(function(){
/*$("#test").bind("click",function(){
alert("世界会向那些有目标和远见的人让路!!");
});*/
/*
* js的事件绑定
ele.onclick=function(){};
* */
// 等同于上面的放方法
$("#test").click(function(){
alert("世界会向那些有目标和远见的人让路!!");
});
/*
1.确定为哪些元素绑定事件
获取元素
2.绑定什么事件(事件类型)
第一个参数:事件的类型
3.相应事件触发的,执行的操作
第二个参数:函数
* */
$("#btntest").bind('click',function(){
// $(this).attr('disabled',true);
$(this).prop("disabled",true);
})
});
</script>
<body>
<h3>bind()方简单的绑定事件</h3>
<div id="test" style="cursor:pointer">点击查看名言</div>
<input id="btntest" type="button" value="点击就不可用了" />
</body>
<script type="text/javascript">
$(function(){
// 绑定click 和 mouseout事件
/*$("h3").bind('click mouseout',function(){
console.log("绑多个事件");
});*/
// 链式编程
$("h3").bind('click',function(){
alert("链式编程1");
}).bind('mouseout',function(){
$("#slowDiv").show("slow");//让slowDiv显示
});
/*$("#test").click(function(){
console.log("点击鼠标了....");
}).mouseout(function () {
console.log("移出鼠标了...");
});*/
$("#test").bind({
click:function(){
alert("链式编程1");
},
mouseout:function(){
$("#slowDiv").show("slow");
}
});
});
</script>
<body>
<h3>bind()方法绑多个事件</h3>
<div id="test" style="cursor:pointer">点击查看名言</div>
<div id="slowDiv"style=" width:200px; height:200px; display:none; ">
人之所以能,是相信能
</div>
</body>
本文是全套java入门到架构师教程之前端开发部分-jQuery的教学文档,如需全套java入门道架构师教程请留言或私信
一)回顾
break:结束循环、流程不再继续;
continue:结束这一次,然后再继续;
注意:虽然 continue可以继续,但是continue后面的内容不会被执行!
数据类型:1.简单数据类型 2.复杂数据类型
简单数据类型存储单值:内存的栈—客栈;
复杂数据类型存储复杂值:内存的堆—一堆;
运行方法 string number null undefined Boolean
简单类型:var a=1;全部放在栈内;运算速度快!— 蓝屏— 内存被完全占满了— 重启
复杂数据类型:数组 Object var arr –栈 值 – 堆
很多学生的信息,就不能用很多变量来存储!
(二)数组字面量
VAR ARR=[];
通过遍历给arr 赋值,当i不连续的时候,会变成稀疏数组
For(var i=0;i <=10;i++){
}
(三)函数
函数就是方法,可以理解成一个功能!
1.函数的声明 function 方法名(){
功能实现;
}
2.函数的调用 方法名();
(四)函数的参数
参数分为形式参数、实际参数
Function fn(形式参数){
Console.log(形式参数);
}
fn(实际参数);
参数详解:
1.实参比形参多,多余的实参会被忽略;
2.实参比形参少,undefind; NaN 实参比形参少的时候,不会报错,但是不会参与运算;
3.形参与实参参数名一样的时候,他们也没有任何关系;
(五)函数的返回值
在函数中运算完成之后,把结果返回出函数体,以便后续使用;
Function fn(形式参数){
Console.log(形式参数);
Return 结果;
}
计算完成之后,不在方法体中进行运算。而是计算完成之后通过return返回之后,再运算;
Var result=fn(实际参数);
返回值详解:
1.Return后面的内容不会再执行;
2.如果函数没有返回值,调用会返回undefined;
3.一般运算之后,都会把结果返回出方法体,有利于程序的再次调用
(六)函数的相互调用
Function fn2(){
Console.log(“函数fn2的调用的过程”);
fn1();
}
Fn2();
Function fn1(){
Console.log(“函数fn2”);
}
比较三个数的大小?
(七)重载?
在其他语言里,函数名相同,但参数不同的情况称之为重载;
在JS中,函数名相同,参数不同也被认为是同一函数,JS中没有重载;
(八)函数的两种声明方式
8.1 函数声明
Function fn(){
}
Fn();
8.2 函数表达式
var fn2=function(){
};
注意:
1.函数表达式后面要加;
2.Fn()会函数声明内部提升的过程;
3.函数表达式不会提升,会提示fn2 is not function;
(九)匿名函数
函数没有函数名的情况,通过变量名来调用函数,这个函数不需要名字;
(十)变量的作用域
全局变量 局部变量
全局变量:声明在最外围,在整个script中都能使用;
在函数内部声明,但是没有var也是全局变量——但是不要这样写!
var a=10;
function fn(){
console.log(a);
}
局部变量:在函数内部声明的变量,只能在内部使用;
var b=20;
function fn(){
var d=10;
}
(十一)递归
递归就是自己调用自己的方法
function fn (){
fn();
}
Fn();
注意:递归如果没有退出条件就是死循环
var i=1;
function fn(){
i++;
if(i<10){
fn();
}
}
fn();
(十二)回调函数
把函数当做参数进行传递的就是回调函数!
function fn1(){
retrun 100;
}
function fn2(fn){
console.log( fn() + 100);
}
Fn2(fn1);
(十三)对象的方法
给对象设置的匿名函数与调用
var person=new Object();
person.name=“tom”;
person.age=18;
person.eat=function(){
console.log(“吃饭在呢!”);
}
Person.eat();
(十四)对象的字面量
var stu={};
stu.name=“tom”;
stu.age=18;
stu.sayHi=function (){
console.log(“HI”);
}
var stu2={
name:tom,
age:18,
sayHi=function(){
console.log(“HI”);
}
}
www.Codecombat.cn
*请认真填写需求信息,我们会在24小时内与您取得联系。