区域特定合同管理系统
时间:90 分钟 | 级别:高级 |
---|
合同管理受益于检索增强生成(RAG),极大地简化了冗长商业合同文本的处理。在 AI 的协助下,可以提出复杂的问题并生成充分的信息化回答,从而促进高效的文档管理。这对于拥有广泛业务关系的企业(如航运公司、建筑公司和咨询机构)来说是无价的。由于安全和监管要求(例如欧洲的 GDPR),对此类合同的访问通常仅限于授权团队成员,因此需要安全的存储实践。
公司希望他们的数据保存在特定的地理边界内并进行处理。因此,本以 RAG 为中心的教程重点介绍了如何处理特定区域的云提供商。您将使用 Aleph Alpha 的嵌入和 LLM 设置一个合同管理系统。您将在德国商业云提供商 STACKIT 上托管所有内容。在此平台上,您将运行 Qdrant Hybrid Cloud 以及您的 RAG 应用程序的其余部分。这种设置将确保您的数据存储和处理都在德国进行。
组件
合同管理平台不是一个简单的 CLI 工具,而是一个应可供所有团队成员使用的应用程序。它需要一个界面来上传、搜索和管理文档。理想情况下,系统应与组织现有技术栈集成,并继承 LDAP 或 Active Directory 的权限/访问控制。
注意:在本教程中,我们将为此类系统构建坚实的基础。但是,完整的解决方案需要根据您组织的具体设置来实现。
- 数据集 - 文档集合,使用不同格式(如 PDF 或 DOCx),从互联网上抓取
- 非对称语义嵌入 - Aleph Alpha 嵌入,用于将查询和文档转换为向量
- 大型语言模型 - Luminous-extended-control 模型,但您可以尝试 Luminous 系列中的其他模型
- Qdrant Hybrid Cloud - 用于存储向量和搜索文档的知识库
- STACKIT - 用于运行 Qdrant Hybrid Cloud 和应用程序进程的德国商业云
我们将实现上传文档、将它们转换为向量并存储在 Qdrant 中的过程。然后,我们将构建一个搜索界面来查询文档并获取答案。所有这些都假设用户在具有一定权限集的情况下与系统交互,并且只能访问他们被允许访问的文档。
先决条件
Aleph Alpha 账户
由于您将使用 Aleph Alpha 的模型,请注册其托管服务并获取 API 令牌。准备好后,将其存储为环境变量
export ALEPH_ALPHA_API_KEY="<your-token>"
import os
os.environ["ALEPH_ALPHA_API_KEY"] = "<your-token>"
STACKIT 上的 Qdrant Hybrid Cloud
请参阅我们的文档了解如何在 STACKIT 上部署 Qdrant Hybrid Cloud。部署完成后,您将获得与 Qdrant 服务器交互的 API 端点。让我们也将其存储在环境变量中
export QDRANT_URL="https://qdrant.example.com"
export QDRANT_API_KEY="your-api-key"
os.environ["QDRANT_URL"] = "https://qdrant.example.com"
os.environ["QDRANT_API_KEY"] = "your-api-key"
Qdrant 将在特定的 URL 上运行,并且访问将受到 API 密钥的限制。请确保也将它们都存储为环境变量
可选:无论何时使用 LangChain,您还可以配置 LangSmith,它将帮助我们跟踪、监控和调试 LangChain 应用程序。您可以在此处注册 LangSmith。
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY="your-api-key"
export LANGCHAIN_PROJECT="your-project" # if not specified, defaults to "default"
实现
为了构建应用程序,我们可以使用 Aleph Alpha 和 Qdrant 的官方 SDK。但是,为了简化流程,让我们使用LangChain。该框架已与这两种服务集成,因此我们可以将精力集中在开发业务逻辑上。
Qdrant 集合
Aleph Alpha 嵌入默认是高维向量,维度为 5120
。然而,该模型一个相当独特的特点是它们可以被压缩到 128
的大小,而准确性性能只有小幅下降(根据文档为 4-6%)。Qdrant 可以轻松存储原始向量,并且启用二进制量化以节省空间并加快检索速度似乎是个好主意。让我们创建一个具有此类设置的集合
from qdrant_client import QdrantClient, models
client = QdrantClient(
location=os.environ["QDRANT_URL"],
api_key=os.environ["QDRANT_API_KEY"],
)
client.create_collection(
collection_name="contracts",
vectors_config=models.VectorParams(
size=5120,
distance=models.Distance.COSINE,
quantization_config=models.BinaryQuantization(
binary=models.BinaryQuantizationConfig(
always_ram=True,
)
)
),
)
我们将使用 contracts
集合来存储文档的向量。将 always_ram
标志设置为 True
以将量化向量保存在 RAM 中,这将加快搜索过程。我们还希望限制对单个文档的访问,因此只有具有适当权限的用户才能看到它们。在 Qdrant 中,可以通过添加一个有效载荷字段来解决这个问题,该字段定义谁可以访问文档。我们将此字段命名为 roles
,并将其设置为可访问文档的角色字符串数组。
client.create_payload_index(
collection_name="contracts",
field_name="metadata.roles",
field_schema=models.PayloadSchemaType.KEYWORD,
)
由于我们使用 Langchain,roles
字段是 metadata
的嵌套字段,因此我们必须将其定义为 metadata.roles
。模式说明该字段是一个关键字,这意味着它是一个字符串或一个字符串数组。我们将使用客户名称作为角色,因此访问控制将基于客户名称。
摄取管道
语义搜索系统依赖高质量数据作为其基础。通过 Langchain 的 unstructured 集成,摄取各种文档格式(如 PDF、Microsoft Word 文件和 PowerPoint 演示文稿)变得轻而易举。但是,智能地分割文本至关重要,以避免将整个文档转换为向量;相反,它们应该被分成有意义的块。随后,提取的文档使用 Aleph Alpha 嵌入转换为向量并存储在 Qdrant 集合中。
首先定义组件并将它们连接起来
embeddings = AlephAlphaAsymmetricSemanticEmbedding(
model="luminous-base",
aleph_alpha_api_key=os.environ["ALEPH_ALPHA_API_KEY"],
normalize=True,
)
qdrant = Qdrant(
client=client,
collection_name="contracts",
embeddings=embeddings,
)
现在是时候索引我们的文档了。每个文档都是一个单独的文件,我们还需要知道客户名称才能正确设置访问控制。单个文档可能对应多个角色,因此我们将它们保存在一个列表中。
documents = {
"data/Data-Processing-Agreement_STACKIT_Cloud_version-1.2.pdf": ["stackit"],
"data/langchain-terms-of-service.pdf": ["langchain"],
}
文档可能看起来像这样
每个文档都必须先分成块;没有万能的解决方案。我们的分块算法将基于递归分割,最大块大小为 500 个字符,重叠为 100 个字符。
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100,
)
现在我们可以遍历文档,将它们分成块,使用 Aleph Alpha 嵌入模型将它们转换为向量,并将它们存储在 Qdrant 中。
from langchain_community.document_loaders.unstructured import UnstructuredFileLoader
for document_path, roles in documents.items():
document_loader = UnstructuredFileLoader(file_path=document_path)
# Unstructured loads each file into a single Document object
loaded_documents = document_loader.load()
for doc in loaded_documents:
doc.metadata["roles"] = roles
# Chunks will have the same metadata as the original document
document_chunks = text_splitter.split_documents(loaded_documents)
# Add the documents to the Qdrant collection
qdrant.add_documents(document_chunks, batch_size=20)
我们的集合已经充满了数据,我们可以开始对其进行搜索。在实际场景中,摄取过程应该是自动化的,由系统上传的新文档触发。由于我们已经在 Kubernetes 上运行 Qdrant Hybrid Cloud,我们可以轻松地将摄取管道作为作业部署到同一环境中。在 STACKIT 上,您可能会使用STACKIT Kubernetes Engine (SKE) 并在容器中启动它。Compute Engine 也是一个选项,但这完全取决于您组织的具体情况。
搜索应用程序
专业文档管理系统拥有许多功能,但语义搜索尚未成为标准。我们将构建一个简单的搜索机制,该机制可能与现有系统集成。搜索过程非常简单:我们使用相同的 Aleph Alpha 模型将查询转换为向量,然后在 Qdrant 集合中搜索最相似的文档。访问控制也已应用,因此用户只能看到他们被允许访问的文档。
我们首先创建我们选择的 LLM 实例,并将最大令牌数设置为 200,因为默认值为 64,这可能太低,无法满足我们的需求。
from langchain.llms.aleph_alpha import AlephAlpha
llm = AlephAlpha(
model="luminous-extended-control",
aleph_alpha_api_key=os.environ["ALEPH_ALPHA_API_KEY"],
maximum_tokens=200,
)
然后,我们可以将组件连接起来并构建搜索过程。RetrievalQA
是一个实现问题检索过程的类,它接受指定的检索器和大型语言模型。Qdrant
实例可以转换为检索器,并带有一个额外的过滤器,该过滤器将传递给 similarity_search
方法。过滤器是根据常规 Qdrant 查询创建的,roles
字段设置为用户的角色。
user_roles = ["stackit", "aleph-alpha"]
qdrant_retriever = qdrant.as_retriever(
search_kwargs={
"filter": models.Filter(
must=[
models.FieldCondition(
key="metadata.roles",
match=models.MatchAny(any=user_roles)
)
]
)
}
)
我们将用户角色设置为 stackit
和 aleph-alpha
,以便用户可以看到这些客户可访问的文档,而看不到其他文档。最后一步是创建 RetrievalQA
实例并使用它来搜索文档,并使用自定义提示。
from langchain.prompts import PromptTemplate
from langchain.chains.retrieval_qa.base import RetrievalQA
prompt_template = """
Question: {question}
Answer the question using the Source. If there's no answer, say "NO ANSWER IN TEXT".
Source: {context}
### Response:
"""
prompt = PromptTemplate(
template=prompt_template, input_variables=["context", "question"]
)
retrieval_qa = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=qdrant_retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": prompt},
)
response = retrieval_qa.invoke({"query": "What are the rules of performing the audit?"})
print(response["result"])
输出
The rules for performing the audit are as follows:
1. The Customer must inform the Contractor in good time (usually at least two weeks in advance) about any and all circumstances related to the performance of the audit.
2. The Customer is entitled to perform one audit per calendar year. Any additional audits may be performed if agreed with the Contractor and are subject to reimbursement of expenses.
3. If the Customer engages a third party to perform the audit, the Customer must obtain the Contractor's consent and ensure that the confidentiality agreements with the third party are observed.
4. The Contractor may object to any third party deemed unsuitable.
还有一些其他参数可以调整以优化搜索过程。k
参数定义应返回多少文档,但 Langchain 也允许我们通过选择搜索操作的类型来控制检索过程。默认是 similarity
,它只是向量搜索,但我们也可以使用 mmr
,它代表最大边际相关性。这是一种使搜索结果多样化的技术,以便用户获得最相关的文档,同时也获得最多样化的文档。mmr
搜索速度较慢,但可能更人性化。
我们的搜索应用程序已准备就绪,我们可以将其部署到与 STACKIT 上的摄取管道相同的环境中。这里的规则相同,因此您可以根据组织的具体情况使用 SKE 或 Compute Engine。
下一步
我们为合同管理系统奠定了坚实的基础,但仍有很多工作要做。如果您想让系统达到生产就绪状态,应考虑将该机制集成到您现有的技术栈中。如果您有任何疑问,欢迎在我们的 Discord 社区提问。