2024年3月

我想做一个尝试,看看能不能用尽量清晰的逻辑,给“AI外行人士”(当然,我也是……)引入一下LangChain,试着从工程角度去理解LangChain的设计和使用。同时大家也可以将此文档作为LangChain的“10分钟快速上手”手册,本意是希望帮助需要的同学实现AI工程的Bootstrap。

文中所有的示例代码都已托管到GitHub:
https://github.com/TuGraph-contrib/langchain-demo

喜欢实操的小伙伴可以边阅读,边复现。

1. 引言

1.1 什么是LangChain?

正式开始前,还是有必要从定义(
What
)开始。LangChain是2022年10月底,由哈佛大学的Harrison Chase发起的基于开源大语言模型的AI工程开发框架。当然也可以问一下AI:

我:LangChain是什么?
GPT-4:LangChain 是一个开源的语言模型工具链框架,旨在使研究人员和开发人员能够更轻松地构建、实验和部署以自然语言处理(NLP)为中心的应用程序。它提供了多种组件和工具,可帮助用户利用最近的语言模型进展,如大型 Transformer 模型等,并且可以与 Hugging Face 等平台集成。
LangChain 的核心理念是将语言模型用作协作工具,通过它,开发者可以构建出处理复杂任务的系统,并且可以高效地对接不同的数据源和应用程序接口(APIs)。这个框架试图简化连接不同的输入和输出流,以及在流中实现语言模型的过程。

顾名思义,LangChain中的“Lang”自然是指大语言模型,“Chain”即“链”,也就是将大模型与其他组件连接成链,借此构建AI工程应用。那么LangChain该如何(
How
)做到这一点的呢?解答这个问题之前,需要先回答什么是工程?什么是AI工程?

1.2 什么是AI工程?

我们先Review一下“工程”的百科定义:

工程是指以某组设想的
目标
为依据,应用有关的
科学
知识和
技术
手段,通过有组织的一群

将某个(或某些)现有
实体
(自然的或人造的)转化为具有预期使用价值的人造
产品
过程。

其中,“目标”定义了要解决的问题域,决定了工程的顶层设计和能力边界,同时也定义了“产品”的最终形态。提升“人”的ROI是工程设计的价值归属。“实体”是工程的生产材料输入,“科学 | 技术”是工程有序运行的基础,对它们的合理利用可以提升工程的整体产出效率。

“工程”关键概念剖析

于是,我们可以这样解读“AI工程”中的关键概念:

  • 目标
    :待解决的特定AI用户需求。如内容创作、智能问答、文档摘要、图像识别等等。

  • :实施AI工程的具体角色。可以是程序员,或者AI应用的研发团队、创业公司。
  • 科学 | 技术
    :显然是大模型与相关工具服务,以及其后的计算科学理论。
  • 实体
    :已有的文档、知识库、业务数据等生产材料。
  • 产品
    :能满足目标需求的具体产品。如聊天机器人、内容生成工具等。

1.3 如何设计LangChain?

因此,如果我们是LangChain的设计者,希望构建通用的AI工程框架。需要回答如下问题:

  1. 【目标 | 产品】
    LangChain的设计目标是什么,能解决哪些AI工程问题?
  2. 【人】
    LangChain的编程接口如何定义,才能提升AI工程师的研发效率?
  3. 【实体 | 科学 | 技术】
    LangChain的核心组件如何抽象,以提升框架的扩展能力?

当然,作为“事后诸葛”,这些问题目前有比较明确的答案:

  1. 作为AI工程框架,LangChain实际是对LLM能力的扩展和补充。如果把LLM比作人的大脑,LangChain则是人的躯干和四肢,协助LLM完成“思考”之外的“脏活累活”。它的能力边界只取决于LLM的智力水平和LangChain能提供的工具集的丰富程度。
  2. LangChain提供了LCEL(LangChain Expression Language)声明式编程语言,降低AI工程师的研发成本。
  3. LangChain提供了Models、Prompts、Indexes、Memory、Chains、Agents六大核心抽象,用于构建复杂的AI应用,同时保持了良好的扩展能力。

很明显,LLM作为LangChain能力的基础,是了解LangChain工程化设计的前提。接下来我们就从最基础的LLM API使用谈起,一步步了解LangChain的工程化构建过程及其背后的设计理念。

2. 环境准备

3. 设计推演

架构设计领域有个比较流行的术语——乐高架构,当然也可以叫可插拔架构。说白就是通过对系统基本组件的合理抽象,找到构造复杂系统的统一规律和可达路径,从而实现在降低系统实现复杂度的同时,提升系统整体的扩展性。(非官方表达,大家能Get到我的意思就好……)

LangChain实际上也遵循了乐高架构的思想。当然,作为最关键的乐高组件之一,LLM的能力自然是我们优先了解的对象,那我们就从OpenAI的API开始吧!

3.1 造梦基础——API

文本生成模型服务是OpenAI提供的最核心的API服务,自ChatGPT发布后经历过几次版本迭代。

3.1.1 Chat Completion API

当下最新的是
Chat Completion API
,是AI与LLM交互的核心入口。

代码示例参考:

import os
import requests

# API Key
api_key = os.getenv('OPENAI_API_KEY')

# 头部信息
headers = {
    'Content-Type': 'application/json',
    'Authorization': f'Bearer {api_key}'
}

# 准备数据
data = {
    'model': 'gpt-4',
    'messages': [{'role': 'user', 'content': '什么是图计算?'}],
    'temperature': 0.7
}

# 调用API
url = 'https://api.openai.com/v1/chat/completions'
response = requests.post(url, json=data, headers=headers)
answer = response.json()['choices'][0]['message']['content']
print(answer)

代码示例输出:

图计算是一种计算模型,用于处理大规模图形结构的数据,并执行各种复杂的算法和计算。这种计算模型主要用于社交网络分析、Web搜索、生物信息学、网络路由优化、数据挖掘等领域。图计算模型的核心是将数据表示为图形结构(节点和边),这样可以更好地揭示数据之间的关系和互动。在图计算中,算法通常以迭代的方式运行,每次迭代都会更新图中节点的状态,直到达到某种停止条件。

3.1.2 Completion API

早先的
Completion API
已经在2023年7月后不再维护,和最新的Chat Completion API参数和结果格式有所不同,最明显的是Prompt是以纯文本方式传递,而非Message格式。

# 准备数据
data = {
    'model': 'gpt-3.5-turbo-instruct',
    'prompt': ['什么是图计算?'],
    'max_tokens': 1024
}

# 调用API
url = 'https://api.openai.com/v1/completions'
response = requests.post(url, json=data, headers=headers)
answer = response.json()['choices'][0]['text']
print(answer)

除了文本生成服务,OpenAI也提供了大量的LLM的周边服务,以协助AI工程构建更复杂的应用能力。如:函数调用、嵌入、微调、多模态等,具体可参考
OpenAI开发文档
的内容。

3.2 智能开端——Chat

自2022年11月底ChatGPT发布以来,AI的大门才真正地向人类打开,其中给用户留下最深印象的功能,自然是智能对话。OpenAI的Chat Completion API参数支持传入消息历史,可以轻松地实现简单的对话服务。

代码示例参考:

# 对话历史
messages = []

def chat_with_ai(message):
    # 记录历史
    messages.append({'role': 'user', 'content': message})
    print(f'me: {message}')

    # 对话请求
    data = {
        'model': 'gpt-4',
        'messages': messages,
        'temperature': 0.7
    }
    url = 'https://api.openai.com/v1/chat/completions'
    response = requests.post(url, json=data, headers=headers)

    # 解析回答
    if response.status_code == 200:
        answer = response.json()['choices'][0]['message']['content']
        messages.append({'role': 'assistant', 'content': answer})
        print(f"ai: {answer}")
    else:
        print(f'Error: {response.status_code}', response.json())

# 多轮对话
chat_with_ai('什么是图计算?')
chat_with_ai('刚才我问了什么问题?')

代码示例输出:

me: 什么是图计算?
ai: 图计算是一种计算模型,用于处理大规模图形结构数据的计算和分析。在这种计算模型中,数据被表示为图形,其中节点代表实体,边代表实体之间的关系。图计算可以用于解决许多实际问题,如社交网络分析、网络路由、生物信息学等。图计算的主要挑战是如何有效地处理大规模的图形数据,并提供快速的计算和分析结果。
me: 刚才我问了什么问题?
ai: 你问的问题是:“什么是图计算?”

3.3 初步封装——SDK

到目前为止,我们还只是用OpenAI最原始的RESTful API构建LLM工程能力,甚至连OpenAI提供的SDK都未使用。显然这不是一个高效的方式,使用前边安装的LangChain-OpenAI集成包
langchain-openai
可以大大降低代码的开发成本。

代码示例参考:

from langchain_openai import ChatOpenAI

# 调用Chat Completion API
llm = ChatOpenAI(model_name='gpt-4')
response = llm.invoke('什么是图计算?')
print(response)

代码示例输出:

content='图计算是一种计算模型,主要用于处理图形结构数据的计算和分析。图计算的对象是图,图由节点和边组成,节点代表实体对象,边代表实体对象之间的关系。图计算主要用于解决实体关系复杂、关系密集的问题,如社交网络分析、网络拓扑分析、推荐系统等。图计算的主要任务是通过对图中节点和边的计算,发现和提取出图中隐含的知识和信息。'

3.4 数据抽象——IO

对于文本生成模型服务来说,实际的输入和输出本质上都是字符串,因此直接裸调用LLM服务带来的问题是要在输入格式化和输出结果解析上做大量的重复的文本处理工作。LangChain当然考虑到这一点,提供了
Prompt

OutputParser
抽象,用户可以根据自己的需要选择具体的实现类型使用。

IO数据抽象示意[6]

代码示例参考:

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建Prompt
prompt = ChatPromptTemplate.from_template("{question}")

# 创建输出解析器
output_parser = StrOutputParser()

# 调用LLM
message = prompt.invoke({'question': '什么是图计算?'})
response = llm.invoke(message)
answer = output_parser.invoke(response)
print(answer)

3.5 组装成链——Chain

模型的IO组件确实可以减少重复的文本处理工作,但形式上依然不够清晰,这里就引入了LangChain中的关键概念:链(Chain)。

3.5.1 HelloWorld

LangChain的表达式语言(
LCEL
)通过重载
__or__
运算符的思路,构建了类似Unix管道运算符的设计,实现更简洁的LLM调用形式。

代码示例参考:

# 创建Chain
chain = prompt | llm | output_parser

# 调用Chain
answer = chain.invoke({'question': '什么是图计算?'})
print(answer)

至此,我们终于看到了LangChain版的“HelloWorld”……

3.5.2 RunnablePassthrough

当然,为了简化Chain的参数调用格式,也可以借助
RunnablePassthrough
透传上游参数输入。

代码示例参考:

from langchain_core.runnables import RunnablePassthrough

# 创建Chain
chain = {"question": RunnablePassthrough()} | prompt | llm | output_parser

# 调用Chain
answer = chain.invoke('什么是图计算?')
print(answer)

3.5.3 DAG

另外,Chain也可以分叉、合并,组合出更复杂的DAG计算图结构。

代码示例参考:

from operator import itemgetter

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建输出解析器
output_parser = StrOutputParser()

# 创建Prompt
topic_prompt = ChatPromptTemplate.from_template("生成一种'{input}'的名称")
good_prompt = ChatPromptTemplate.from_template("列举{topic}的好处:")
bad_prompt = ChatPromptTemplate.from_template("列举{topic}的坏处:")
summary_prompt = ChatPromptTemplate.from_messages(
    [
        ("ai", "{topic}"),
        ("human", "好处:\n{good}\n\n坏处:\n{bad}"),
        ("system", "生成最终结论"),
    ]
)

# 创建组合Chain
topic_chain = topic_prompt | llm | output_parser | {"topic": RunnablePassthrough()}
goods_chain = good_prompt | llm | output_parser
bads_chain = bad_prompt | llm | output_parser
summary_chain = summary_prompt | llm | output_parser
chain = (
    topic_chain
    | {
        "good": goods_chain,
        "bad": bads_chain,
        "topic": itemgetter("topic"),
    }
    | summary_chain
)

# 调用chain
answer = chain.invoke({"input": '常见水果'})
print(answer)

代码示例输出:

苹果是一种营养丰富的水果,具有帮助消化、保护心脏、降低糖尿病风险、强化免疫系统、帮助减肥、保护视力、预防哮喘、抗癌和提升记忆力等多种好处。然而,过度食用或者不适当的食用方式也可能带来一些不利影响,如引发过敏、导致腹泻、对牙齿造成伤害、可能携带农药残留、影响正常饮食和钙质吸收、增加蛀牙风险和引发胃痛等。因此,我们在享受苹果带来的好处的同时,也需要注意适量和正确的食用方式。

通过调用
chain.get_graph().print_ascii()
可以查看Chain的计算图结构。当然,使用LangSmith能更清晰的跟踪每一步的计算结果。

LangChain的计算图与Trace

Tips:开启LangSmith需要申请LangChain的
AK
,并配置环境变量:

export LANGCHAIN_TRACING_V2="true"

export LANGCHAIN_API_KEY="<Your-LangChain-AK>"

3.5.4 LangGraph

基于LCEL确实能描述比较复杂的LangChain计算图结构,但依然有DAG天然的设计限制,即不能支持“循环”。于是LangChain社区推出了一个新的项目——
LangGraph
,期望基于LangChain构建支持循环和跨多链的计算图结构,以描述更复杂的,甚至具备自动化属性的AI工程应用逻辑,比如智能体应用。其具体使用方式可以参考
LangGraph文档

LangGraph声称其设计理念受Pregel/Beam的启发,构建支持多步迭代的计算能力,这部分设计理念和我们设计的支持“流/批/图”计算一体化的图计算引擎TuGraph也十分相似,感兴趣的朋友可以访问
TuGraph Analytics
项目进行学习。

3.6 开启记忆——Memory

通过Chain,LangChain相当于以“工作流”的形式,将LLM与IO组件进行了有秩序的连接,从而具备构建复杂AI工程流程的能力。而我们都知道LLM提供的文本生成服务本身不提供记忆功能,需要用户自己管理对话历史。因此引入
Memory组件
,可以很好地扩展AI工程的能力边界。

带记忆的问答处理[6]

3.6.1 Memory接口

LangChain的
BaseMemory
接口提供了Memory的统一抽象(截至v0.1.12还是Beta版本),提供了多种类型的Memory组件的实现,我们选用最简单的
ConversationBufferMemory
实现类型。

需要注意的是,要将Memory组件应用到Chain上,需要使用子类
LLMChain
进行创建Chain。

代码示例参考:

from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, \
    HumanMessagePromptTemplate
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建Prompt
prompt = ChatPromptTemplate.from_messages([
    MessagesPlaceholder(variable_name='chat_history'),
    HumanMessagePromptTemplate.from_template('{question}')
])

# 创建Memory
memory = ConversationBufferMemory(memory_key='chat_history',
                                  return_messages=True)
# 创建LLMChain
llm_chain = LLMChain(llm=llm, memory=memory, prompt=prompt)

# 调用LLMChain
print(llm_chain.predict(question='什么是图计算?'))
print(llm_chain.predict(question='刚才我问了什么问题?'))

代码示例输出:

图计算是一种计算类型,主要处理的数据结构是图。图是由节点(或顶点)和边组成的,节点代表实体,边代表实体之间的关系。在图计算中,主要解决的问题是如何在图的结构上进行有效的计算和分析。
你问的问题是:“什么是图计算?”

这里可以看到,创建带Memory功能的Chain,并不能使用统一的LCEL语法。调用
LLMChain
使用的是predict而非invoke方法,直接调用invoke会返回一个
LLMResult
类型的结果。因此,
LLMChain
也不能使用管道运算符接
StrOutputParser
。这些设计上的问题,个人推测也是目前Memory模块还是Beta版本的原因之一吧。

3.6.2 History接口

但是,LangChain提供了工具类
RunnableWithMessageHistory
,支持了为Chain追加History的能力,从某种程度上缓解了上述问题。不过需要指定Lambda函数get_session_history以区分不同的会话,并需要在调用时通过config参数指定具体的会话ID。

SessionHistory必须是History接口类型
BaseChatMessageHistory
,用户可以根据需要选择不同的存储实现。这里为了简化,全局只用了一份内存类型的
ChatMessageHistory

代码示例参考:

from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, \
    HumanMessagePromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建输出解析器
output_parser = StrOutputParser()

# 创建Prompt
prompt = ChatPromptTemplate.from_messages([
    MessagesPlaceholder(variable_name="chat_history"),
    HumanMessagePromptTemplate.from_template("{question}")
])

# 创建Chain
chain = prompt | llm | output_parser

# 添加History
history = ChatMessageHistory()
chain_with_history = RunnableWithMessageHistory(
    chain,
    lambda session_id: history,
    input_messages_key="question",
    history_messages_key="chat_history",
)

# 调用Chain
print(chain_with_history.invoke({'question': '什么是图计算?'},
                                config={"configurable": {"session_id": None}}))
print(chain_with_history.invoke({'question': '刚才我问了什么问题?'},
                                config={"configurable": {"session_id": None}}))

调用形式看起来是复杂了一些,不过代码结构相比Memory组件更清晰一些,聊胜于无……

3.7 消除幻觉——RAG

拥有记忆后,确实扩展了AI工程的应用场景。但是在专有领域,LLM无法学习到所有的专业知识细节,因此在面向专业领域知识的提问时,无法给出可靠准确的回答,甚至会“胡言乱语”,这种现象称之为LLM的“幻觉”。

检索增强生成(RAG)把信息检索技术和大模型结合起来,将检索出来的文档和提示词一起提供给大模型服务,从而生成更可靠的答案,有效的缓解大模型推理的“幻觉”问题。

如果说LangChain相当于给LLM这个“大脑”安装了“四肢和躯干”,RAG则是为LLM提供了接入“人类知识图书馆”的能力。

基于RAG的问答处理流程[1]

相比提示词工程,RAG有更丰富的上下文和数据样本,可以不需要用户提供过多的背景描述,即能生成比较符合用户预期的答案。相比于模型微调,RAG可以提升问答内容的时效性和可靠性,同时在一定程度上保护了业务数据的隐私性。

但由于每次问答都涉及外部系统数据检索,因此RAG的响应时延相对较高。另外,引用的外部知识数据会消耗大量的模型Token资源。因此,用户需要结合自身的实际应用场景做合适的技术选型。

大语言模型优化技术[1]

借助LCEL提供的
RunnableParallel
可以清晰描述RAG的计算图结构,其中最关键的部分是通过context键注入向量存储(Vector Store)的查询器(Retriever)。

代码示例参考:

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores.faiss import FAISS
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings, ChatOpenAI

# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建Prompt
prompt = ChatPromptTemplate.from_template('基于上下文:{context}\n回答:{input}')

# 创建输出解析器
output_parser = StrOutputParser()

# 模拟文档
docs = [Document(page_content="TuGraph是蚂蚁开源的图数据库产品")]

# 文档嵌入
splits = RecursiveCharacterTextSplitter().split_documents(docs)
vector_store = FAISS.from_documents(splits, OpenAIEmbeddings())
retriever = vector_store.as_retriever()

# 创建Chain
chain_no_context = RunnablePassthrough() | llm | output_parser
chain = (
    {"context": retriever, "input": RunnablePassthrough()}
    | prompt | llm | output_parser
)

# 调用Chain
print(chain_no_context.invoke('蚂蚁图数据库开源了吗?'))
print(chain.invoke('蚂蚁图数据库开源了吗?'))

代码示例输出:

蚂蚁图数据库目前没有公开信息表明已经开源。开源状态可能会随时间和公司政策变化,建议直接查阅蚂蚁集团或相关开源平台的官方信息获取最新和准确的消息。
是的,蚂蚁的图数据库产品TuGraph是开源的。

向量存储的写入与查询[6]

结合示例和向量数据库的存取过程,我们简单理解一下RAG中关键组件。

  • DocumentLoader
    :从外部系统检索文档数据。简单起见,示例中直接构造了测试文档对象。实际上LangChain提供了文档加载器
    BaseLoader
    的接口抽象和大量实现,具体可根据自身需要选择使用。
  • TextSplitter
    :将文档分割成块,以适应大模型上下文窗口。示例中采用了常用的
    RecursiveCharacterTextSplitter
    ,其他参考LangChain的
    TextSplitter
    接口和实现。
  • EmbeddingsModel
    :文本嵌入模型,提供将文本编码为向量的能力。文档写入和查询匹配前都会先执行文本嵌入编码。示例采用了OpenAI的
    文本嵌入模型服务
    ,其他参考LangChain的
    Embeddings
    接口和实现。
  • VectorStore
    :向量存储,提供向量存储和相似性检索(ANN算法)能力。LangChain支持的向量存储参考
    VectorStore
    接口和实现。示例采用了Meta的
    Faiss
    向量数据库,本地安装方式:
    pip install faiss-cpu
    。需要额外提及的是,对于图数据库,可以将相似性搜索问题转化为图遍历问题,并具备更强的知识可解释性。蚂蚁开源的
    TuGraph数据库
    目前正在做类似的技术探索。
  • Retriever
    :向量存储的查询器。一般和VectorStore配套实现,通过as_retriever方法获取,LangChain提供的Retriever抽象接口是
    BaseRetriever

3.8 使用工具——Tool

“会使用工具”是人类和动物的根本区别。

要构建更强大的AI工程应用,只有生成文本这样的“纸上谈兵”能力自然是不够的。工具不仅仅是“肢体”的延伸,更是为“大脑”插上了想象力的“翅膀”。借助工具,才能让AI应用的能力真正具备无限的可能,才能从“认识世界”走向“改变世界”。

这里不得不提到OpenAI的Chat Completion API提供的
函数调用
能力(注意这里不是
Assistant的函数调用
),通过在对话请求内附加tools参数描述工具的定义格式(原先的functions参数已过期),LLM会根据提示词推断出需要调用哪些工具,并提供具体的调用参数信息。用户需要根据返回的工具调用信息,自行触发相关工具的回调。下一章内容我们可以看到工具的调用动作可以通过Agent自主接管。

LLM Tools执行流程示意[6]

为了简化代码实现,我们用LangChain的注解
@tool
定义了一个测试用的“获取指定城市的当前气温”的工具函数。然后通过
bind_tools
方法绑定到LLM对象即可。需要注意的是这里需要用
JsonOutputToolsParser
解析结果输出。

代码示例参考:

import random

from langchain_core.output_parsers.openai_tools import JsonOutputToolsParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI


# 定义Tool
@tool
def get_temperature(city: str) -> int:
    """获取指定城市的当前气温"""
    return random.randint(-20, 50)


# 创建LLM
llm = ChatOpenAI(model_name='gpt-4')

# 创建JSON输出解析器
output_parser = JsonOutputToolsParser()

# 创建Chain
chain = (
    RunnablePassthrough()
    | llm.bind_tools(tools=[get_temperature])
    | output_parser
)

# 调用Chain
print(chain.invoke('杭州今天多少度?'))

代码示例输出:

[{'type': 'get_temperature', 'args': {'city': '杭州'}}]

实际上LangChain提供了大量的内置工具和工具库的支持。@tool只是提供了简洁的工具创建的支持,要定制复杂的工具行为需要自行实现
BaseTool
工具接口。同时工具库接口
BaseToolkit
下也有大量的实现,如向量存储、SQL数据库、GitHub等等。用户可以根据自身需求选用或自行扩展。

3.9 走向智能——Agent

通用人工智能(AGI)将是AI的终极形态,几乎已成为业界共识。类比之,构建智能体(Agent)则是AI工程应用当下的“终极形态”。

3.9.1 什么是Agent?

引用LangChain中Agent的定义,可以一窥Agent与Chain的区别。

Agent的核心思想是使用大型语言模型(LLM)来选择要采取的行动序列。在Chain中行动序列是硬编码的,而Agent则采用语言模型作为推理引擎来确定以什么样的顺序采取什么样的行动。

Agent相比Chain最典型的特点是“自治”,它可以通过借助LLM专长的推理能力,自动化地决策获取什么样的知识,采取什么样的行动,直到完成用户设定的最终目标。

LangChain Agent工作流程示意图[17]

因此,作为一个智能体,需要具备以下核心能力:

  • 规划
    :借助于LLM强大的推理能力,实现任务目标的规划拆解和自我反思。
  • 记忆
    :具备短期记忆(上下文)和长期记忆(向量存储),以及快速的知识检索能力。
  • 行动
    :根据拆解的任务需求正确地调用工具以达到任务的目的。
  • 协作
    :通过与其他智能体交互合作,完成更复杂的任务目标。

Agent的核心能力[14]

3.9.2 构建智能体

我们使用Agent继续完成前边Tool部分没有完成的例子。这里使用create_openai_tools_agent方法创建一个简单的OpenAI工具Agent,AgentExecutor会自动接管工具调用的动作。如果希望给Agent添加记忆能力,依然可以采用前边Memory章节提过的
RunnableWithMessageHistory的方案

代码示例参考:

import random

from langchain.agents import create_openai_tools_agent, \
    AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, \
    HumanMessagePromptTemplate, SystemMessagePromptTemplate
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI

# 创建LLM
llm = ChatOpenAI()

# 定义Tool
@tool
def get_temperature(city: str) -> int:
    """获取指定城市的当前气温"""
    return random.randint(-20, 50)


# 创建Agent提示词模板
prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template('You are a helpful assistant'),
    MessagesPlaceholder(variable_name='chat_history', optional=True),
    HumanMessagePromptTemplate.from_template('{input}'),
    MessagesPlaceholder(variable_name='agent_scratchpad')
])

# 创建Agent
tools = [get_temperature]
agent = create_openai_tools_agent(llm, tools, prompt=prompt)

# 执行Agent
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
print(agent_executor.invoke({'input': '今天杭州多少度?'})['output'])

代码示例输出:

> Entering new AgentExecutor chain...
Invoking:
get_temperature
with
{'city': 'Hangzhou'}
16 今天杭州的气温是16度。

> Finished chain.
今天杭州的气温是16度。

需要补充说明的是,LangChain提供了
Hub
功能,帮助大家管理共享Agent的提示词模板。上述示例代码的Agent提示词模板和
hwchase17/openai-tools-agent
的定义等价。

通过代码
prompt = hub.pull("hwchase17/openai-tools-agent")
可以直接引用创建prompt。

hwchase17/openai-tools-agent的定义

4. LangChain架构

从API到Agent,我们“脑暴”了一个完整AI工程应用的发展轨迹,借此我相信大家对LangChain的设计理念应该有了进一步的理解。

最后,我们再看一眼LangChain的产品架构。除了本文未介绍的LangServe——将Chain部署为RESTful服务,其他不再赘述。

LangChain产品架构[6]

5. 尾记

可能会有小伙伴疑问,为啥我一个搞图计算的,在这捣鼓起AI工具来了。抛开“拥抱行业,跟进趋势”这样的大口号不谈,单纯从工作需要角度,使用AI工具加速内容的产出,本身就可以帮助我节省大量的时间和精力,空出的“闲暇”时间去带娃也是极好的……

线上关于LangChain的文章,虽不说汗牛充栋,但也能随手拈来。作为“后入场”者,我过去从事了多年的数仓、中台、云产品的架构和研发,最近两三年一直在搞图计算这样的基础软件,就想试试结合自身多面的工程经验去理解LangChain背后的设计理念,给大家一个别样的视角去观察、体验和思考AI工程。这两天被“AI程序员”的新闻刷屏了,其中引用的Adam Rackis的一条Twitter令我感受颇深:“
做好磨练和深入学习的准备,熟练地引导人工智能取得好的结果可能才是未来程序员能体现出来的价值。
”,或许这正是AI工程的用武之地吧。

6. 参考资料

  1. Retrieval-Augmented Generation for Large Language Models: A Survey:
    https://arxiv.org/abs/2312.10997
  2. Chain-of-Thought Prompting Elicits Reasoning in Large Language Models:
    https://arxiv.org/pdf/2201.11903.pdf
  3. Tree of Thoughts: Deliberate Problem Solving with Large Language Models:
    https://arxiv.org/pdf/2305.10601.pdf
  4. ReAct: Synergizing Reasoning and Acting in Language Models:
    https://arxiv.org/abs/2210.03629
  5. API-Bank: A Comprehensive Benchmark for Tool-Augmented LLMs:
    https://arxiv.org/abs/2304.08244
  6. LangChain Docs:
    https://python.langchain.com/
  7. OpenAI Docs:
    https://platform.openai.com/docs
  8. LangGraph Docs:
    https://python.langchain.com/docs/langgraph
  9. LangChain:[
    https://github.com/langchain-ai/langchain
    ]
  10. LangGraph:[
    https://github.com/langchain-ai/langgraph
    ]
  11. TuGraph Analytics:
    https://github.com/TuGraph-family/tugraph-analytics
  12. TuGraph DB:
    https://github.com/TuGraph-family/tugraph-db
  13. Langchain-Chatchat:
    https://github.com/chatchat-space/Langchain-Chatchat
  14. LLM Powered Autonomous Agents:
    https://lilianweng.github.io/posts/2023-06-23-agent/
  15. Emerging Architectures for LLM Applications:
    https://a16z.com/emerging-architectures-for-llm-applications/
  16. Prompt Engineering Guide:
    https://www.promptingguide.ai/
  17. Bond Copilot: Unleashing Refinitiv Data Library Search API with AI (LLM) :
    https://developers.lseg.com/en/article-catalog/article/bond-copilot--unleashing-rd-lib-search-api-with-ai-llm-langchain

我们在使用
pandas
处理完数据之后,最终总是要把数据作为一个文件保存下来,
那么,保存数据最常用的文件是什么呢?
我想大部分人一定会选择
csv
或者
excel

刚接触数据分析时,我也是这么选择的,不过,今天将介绍几种不一样的存储数据的文件格式。
这些文件格式各有自己的一些优点,希望本文能让你以后的数据存储方式能有不一样的选择,从而存储的更加专业。

1. 准备数据

本次使用的数据来自A股2023年全年的日交易数据。
数据下载地址:
https://databook.top/。

导入数据:

import pandas as pd

fp = "d:/share/历史行情数据-不复权-2023.csv"

df = pd.read_csv(fp)
df

image.png
总共大约
120多万
条数据。

准备好测试数据之后,开始测试各种文件的效果。

2. CSV

使用
pandas
做数据分析时,保存数据最常用的格式一定是
CSV
(或者
excel
)。
因为
CSV
格式易于分享,用
excel
或者文本编辑器都能直接打开。

但是当数据集规模比较大时,它的磁盘利用率和读写效率究竟如何呢?
首先我们把准备的数据保存为一个测试用的
test.csv
文件。

df = pd.read_csv(fp)
df.to_csv("d:/share/test.csv", index=None)

看看文件大小:

ls .\test.csv


    目录: D:\share


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2024/03/15  10:41:27      101411037 test.csv

test.csv
文件大约:
\(101411037/1024/1024 \approx 96.7MB\)

读取效率:

%%timeit
df = pd.read_csv("d:/share/test.csv")
1.73 s ± 54.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

写入的效率:

%%timeit
df.to_csv("d:/share/test.csv", index=None)
13.2 s ± 402 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

3. Pickle

Pickle
格式是一种用于序列化和反序列化
Python对象结构

二进制格式

它的主要特点是能够将程序中运行的对象信息保存到文件中。

下面,我们先把准备的数据保存为一个测试用的
test.pkl
文件。

# pickle file
df = pd.read_csv(fp)
df.to_pickle("d:/share/test.pkl")

文件大小:

ls .\test.pkl


    目录: D:\share


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2024/03/15  10:49:09      116904470 test.pkl

test.pkl
文件大约:
\(116904470/1024/1024 \approx 666666.5MB\)

读取效率:

%%timeit
df = pd.read_pickle("d:/share/test.pkl")
136 ms ± 5.09 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

写入效率:

%%timeit
df.to_pickle("d:/share/test.pkl")
182 ms ± 7.42 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

4. Parquet

Parquet
格式是一种列式存储格式,被广泛应用于大数据处理领域。
它采用了压缩和编码技术,能够有效地存储和压缩数据,同时保持数据的结构和模式。

下面,我们先把准备的数据保存为一个测试用的
test.parquet
文件。

# parquet file
df = pd.read_csv(fp)
df.to_parquet("d:/share/test.parquet")

文件大小:

ls .\test.parquet


    目录: D:\share


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2024/03/15  10:52:08       32964728 test.parquet

test.parquet
文件大约:
\(32964728/1024/1024 \approx 31.4MB\)

读取效率:

%%timeit
df = pd.read_parquet("d:/share/test.parquet")
200 ms ± 8.54 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

写入效率:

%%timeit
df.to_parquet("d:/share/test.parquet")
1.23 s ± 62.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

5. Feather

Feather
格式是一种高速、轻量且易于使用的二进制文件格式,专门用于存储数据帧。
Feather的主要目标是提供高性能的读写操作。

下面,我们先把准备的数据保存为一个测试用的
test.feather
文件。

# feather file
df = pd.read_csv(fp)
df.to_feather("d:/share/test.feather")

文件大小:

ls .\test.feather


    目录: D:\share


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2024/03/15  11:17:15       57347098 test.feather

test.feather
文件大约:
\(57347098/1024/1024 \approx 54.7MB\)

读取效率:

%%timeit
df = pd.read_feather("d:/share/test.feather")
130 ms ± 5.29 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

写入效率:

%%timeit
df.to_feather("d:/share/test.feather")
277 ms ± 14.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

6. 总结

pandas
能够保存的文件格式有很多,比如还有
XML

JSON

HTML
等等,
上面列出的
4种
是我认为比较典型,且在数据分析领域用的比较多的格式。

4种
格式的比较总结如下:

磁盘占用 读取效率 写入效率
csv 96.7 MB 1.73 s ± 54.7 ms 13.2 s ± 402 ms
pickle 666666.5 MB 136 ms ± 5.09 ms 182 ms ± 7.42 ms
parquet 31.4 MB 200 ms ± 8.54 ms 1.23 s ± 62.4 ms
feather 54.7 MB 130 ms ± 5.29 ms 277 ms ± 14.8 ms

综合来看,当我们的
数据量不大
的时候,用
CSV
格式比较好,分享方便,可以用多种编辑器直接打开查看。
当数据规模变大了之后,如果数据需要
长期存储
,而且不需要经常的更新写入,那么用
parquet
格式(它的磁盘占用最低,读取性能好,写入性能略逊);
如果只要
短期存储
的话,用
fetdher
格式更好(磁盘占用不算高,读写性能都不错)。

至于
pickle
格式,它的读写性能也不错,但它的最大优势是可以保存
python
对象的状态,只用来保存数据的话,这个优势难以体现。
而且它的磁盘占用比较大,单纯保存数据的话,不建议使用这种格式。

全面介绍云计算安全的意义、安全模型、云安全挑战、云安全最佳实践和技术解决方案,最终深入研究云安全案例,让大家对云安全有全面的理解。

关注【TechLeadCloud】,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理专业人士,上亿营收AI产品研发负责人。

file

一、引言

在数字化时代,云计算已成为企业转型的催化剂,为业务创新提供了无限动力。云平台的弹性、可伸缩性和成本效益,正推动着从初创公司到跨国企业的全面云端迁移。然而,这股浪潮背后,云安全问题也如影随形。因此,理解并应对云安全的挑战,是现代IT专业人士的必修课。

云安全的重要性远超出传统IT安全的范畴。它不仅仅是保护数据不被未经授权访问,更是保障企业的生命线—数据的完整性和可用性。在AWS S3桶错误配置导致数百万用户数据泄露的事件中,我们见证了安全缺失对企业声誉和财务的严重影响。同样,通过对Kaseya VSA软件供应链攻击的分析,我们理解到在云环境中,即使是间接的依赖也可能成为攻击的跳板。

云计算的安全意义

云计算为安全意义增加了几层新的维度。传统上,安全围绕在固定的物理边界内部署的资源进行。然而,在云中,动态变化的资源、分布式架构和多租户环境将安全问题复杂化。例如,IaaS模式下,AWS的虚拟私有云(VPC)提供了网络隔离,但如果配置不当,可能会意外暴露敏感数据。

安全在云计算发展中的角色

安全在云计算的发展中扮演着基石的角色。云服务商(CSP)和客户之间的“共责模型”揭示了这一点。在这种模型中,CSP负责保护基础设施,而客户则负责保护其数据。例如,Azure提供先进的防御措施来保护其数据中心,但客户需要负责其数据的加密和访问控制。

现实例证

  • 数据泄露
    :2017年,由于S3桶的错误配置,Verizon Communications泄露了数百万客户的数据。这一事件凸显了即使在云服务提供商提供先进安全特性的情况下,客户配置的重要性。
  • 供应链攻击
    :2021年的Kaseya VSA供应链攻击波及了上千家公司。这一事件展示了即使是基于云的服务,也可能因为供应链中的弱环节而遭受重创。

云安全既是一场技术的较量,也是一场策略的游戏。通过具体的例证,我们能够将抽象的安全概念和原则,与日常运营的实际情况联系起来,使得云安全的探讨更贴近实际、更具体化。


二、云计算与安全模型

I
file
云计算的安全模型是建立在其服务模型之上的,包括基础设施即服务(IaaS)、平台即服务(PaaS)和软件即服务(SaaS)。每个模型都有其独特的安全需求和责任分配,了解这些差异对确保数据和应用的安全至关重要。

IaaS, PaaS, SaaS概述

IaaS
提供了虚拟化的计算资源。在这一模型下,用户负责管理操作系统、中间件、应用程序以及数据。例如,AWS的EC2允许用户在虚拟机上运行和控制应用程序,但同时也要求用户自行维护这些虚拟机的安全。

PaaS
为用户提供了一个平台,让他们能够开发、运行和管理应用程序,而不必担心底层基础设施。Heroku就是一个PaaS的例子,它抽象化了运行环境的管理,但用户仍需确保其应用程序的代码安全。

SaaS
将软件作为一项服务直接交付给用户,通常是通过浏览器访问。在SaaS模型中,如Salesforce或Google Workspace,服务提供商管理了几乎所有的安全责任,但用户依然需要管理他们的用户账号和数据访问权限。

不同模型的安全考量

每种模型的安全考量根据控制层面的不同而变化。在
IaaS
中,用户必须确保操作系统级别的安全,例如,定期打补丁和监控虚拟机的安全。这类似于管理自有的数据中心,但在一个由云服务提供商维护的基础设施上操作。


PaaS
中,安全考量转向应用层面。用户需关注应用级别的漏洞,例如,确保代码中没有安全缺陷。此外,用户必须对存储在平台上的数据进行加密和访问控制。

而在
SaaS
中,用户的安全职责主要集中在数据治理和访问管理。例如,使用Google Workspace时,用户需遵守强密码策略和两因素认证,以保护账户不被未经授权的用户访问。

现实例证

  • IaaS配置错误
    :在2019年,Capital One银行的数据泄露就是因为一个错误配置的AWS WAF(Web Application Firewall),导致攻击者访问了存储在S3桶中的敏感数据。
  • PaaS代码漏洞
    :2017年,通过一个在Struts 2 PaaS平台上的漏洞,黑客成功地攻击了Equifax,泄露了1.43亿美国消费者的个人信息。
  • SaaS权限滥用
    :2018年,Snapchat的一名员工成为了一个钓鱼攻击的受害者,导致了数千名员工的个人信息被不当地访问。

理解不同云计算模型下的安全责任是实现云安全的关键。每种模型都有其独特的挑战,但也提供了相应的安全控制。在本文的后续部分,我们将深入探讨每种服务模型的安全最佳实践和解决方案,以帮助读者在各自的云计算旅程中维持坚固的安全防线。


三、云安全的挑战

file
云安全的挑战与传统IT安全的挑战在本质上是共通的:保护数据和系统不受未授权的访问、泄露、篡改和破坏。但在云计算的环境中,这些挑战被放大并且带有新的复杂性。本节将详细讨论云安全面临的具体挑战,以及这些挑战如何在不同层面影响组织。

数据安全和隐私

在云环境中,数据的安全和隐私保护是最根本的挑战。云服务的多租户特性意味着不同用户的数据可能存储在相同的物理设备上,而数据加密、访问控制和隐私保护措施则是确保信息隔离的关键。

现实例证
:在2019年,一个大型CRM服务提供商发生了数据泄露,使得包含敏感信息的数百万条客户记录暴露于公共云存储服务中。调查发现,这是由于员工配置错误和访问权限过宽导致的。

配置管理

错误的云配置是数据泄露的一大原因。自动化和正确的配置管理是防止数据暴露的关键,而这需要深刻理解云服务商提供的配置选项和安全模型。

现实例证
:在2020年,一个社交媒体公司由于错误配置了其AWS S3存储桶,导致超过5亿用户的个人数据被公开。

身份与访问管理(IAM)

在云环境中,细粒度的身份和访问管理是至关重要的。它涉及用户认证和授权策略的设计,以确保只有授权用户能够访问资源。

现实例证
:2020年,一个在线零售商面临严重的安全漏洞,因为它的IAM权限过于宽松,使得未授权的第三方可以访问其云数据库。

端点安全

随着远程工作模式的普及,员工可能会从世界各地的多种设备接入云服务。因此,维护端点安全,确保所有接入点都是受信任的,变得尤为重要。

现实例证
:在一起著名的端点安全事件中,一个企业的员工在未加密的家用网络上使用了公司的设备,导致敏感数据遭到拦截和泄露。

供应链攻击

云服务提供商和第三方供应商的软件可能会成为供应链攻击的目标,这种攻击对云安全构成了间接但极其严重的威胁。

现实例证
:SolarWinds Orion平台的供应链攻击影响了包括政府机构和私营企业在内的多个组织,展示了供应链安全的重要性。

云安全的挑战需要跨学科的知识和综合性的解决方案。通过理解这些挑战,并将其与实际案例相关联,我们可以构建起一个既深入又实用的安全知识框架,来指导实际操作。接下来,我们将探讨如何在云环境中构建和实施有效的安全策略,以应对这些挑战。


四、安全最佳实践

file

为了抵御云计算环境中的安全威胁,采用一系列最佳实践是关键。这些最佳实践包括技术措施、策略制定和员工培训等方面,它们共同构建了企业云安全的防线。

数据加密与保护

数据在传输和静态时都应进行加密,以防止未授权访问和泄露。使用强加密标准和管理好加密密钥至关重要。

实例
: 例如,即使在2018年GitHub的一个大型数据泄露事件中,由于所有数据都进行了加密,攻击者无法读取任何敏感信息。

综合性的身份管理

实施多因素认证和最小权限原则,确保用户仅能访问其执行职责所需的信息和资源。

实例
: 亚马逊员工在使用AWS服务时,通过精细化的权限管理和多因素认证策略,即使内部网络被侵入,也极大地限制了潜在的损害。

持续监控与自动响应

使用自动化工具进行实时监控,确保所有异常行为都能立即被检测并得到响应。

实例
: 领先的云服务商如Azure提供了高级监控服务,能够自动检测并响应异常登录尝试,防止潜在的安全事件扩散。

灾难恢复计划

制定并定期更新灾难恢复计划,确保在发生安全事件时能够迅速恢复正常业务。

实例
: 在2021年一个知名企业的云服务遭受DDoS攻击时,他们能够快速切换到备份系统,并几乎不影响客户体验。

第三方安全评估

定期进行第三方安全评估,以保持客观的安全视角和提升安全措施。

实例
: Dropbox在引入第三方安全服务评估后,改进了其应用的安全设计,并增加了对用户数据的保护。

员工安全意识培训

持续对员工进行安全意识培训,因为人为错误往往是安全漏洞的主要原因。

实例
: 一家大型电子商务公司通过对员工的定期安全培训,成功减少了由员工疏忽导致的安全事件。

通过这些最佳实践的实施,企业可以提升其云安全姿态,从而有效降低安全风险。


五、云安全技术解决方案

云安全技术解决方案是保护云计算环境的核心,包含了一系列旨在防护基础设施、应用程序和数据的工具和服务。本节将深入探讨云安全技术,并以现实案例加以说明。

防火墙和入侵检测系统(IDS)

云防火墙服务和入侵检测系统为云服务提供第一层防护,监控恶意活动并提供实时防护。

现实例证
:使用AWS Shield的企业客户,如Netflix,在2017年受到大规模DDoS攻击时,依靠该服务成功抵御攻击,保障了服务的连续性和用户体验。

身份和访问管理(IAM)服务

IAM服务管理用户身份,并控制对云资源的访问权限。通过精细的策略制定,可以有效防止未授权的访问。

现实例证
:使用Google Cloud的IAM工具,一家跨国公司成功地为其全球团队实施了细粒度访问控制,同时保障了合规性和数据安全。

加密服务

加密服务确保数据在传输过程中和静止时的安全性。密钥管理服务(KMS)则是管理和控制加密密钥的关键工具。

现实例证
:Microsoft Azure客户,如Adobe Systems,利用Azure Key Vault来保护和管理其密钥,加密存储和传输的敏感数据。

安全配置管理

配置管理工具可以帮助企业监控云资源的配置状态,确保它们符合安全最佳实践。

现实例证
:利用Palo Alto Networks的Prisma Cloud,多家企业能够自动化地检查其云资源配置,及时发现并修复潜在的安全隐患。

容器安全

容器技术虽提高了部署的灵活性,但也带来新的安全挑战。容器安全解决方案有助于保护容器化应用程序的整个生命周期。

现实例证
:Twistlock(现为Prisma Cloud的一部分)为Docker和Kubernetes环境提供了全面的安全解决方案,帮助企业确保其容器部署的安全。

安全信息和事件管理(SIEM)服务

SIEM服务集中收集、分析并呈现安全相关信息,帮助企业更快地识别和响应安全事件。

现实例证
:Splunk作为SIEM解决方案,为许多企业提供了数据洞察和安全事件响应机制,比如帮助美国一家银行及时识别并阻止了一次复杂的网络攻击。

通过采用这些技术解决方案,组织能够在云环境中建立一个强大且灵活的安全防御体系。这些解决方案的有效应用,不仅依赖于技术本身,还需要结合明确的安全策略和流程。


六、云安全案例深入研究

深入分析实际案例是理解云安全挑战和解决方案的有效方法。本节将探讨几个标志性的云安全事件,分析事件的发生、处理过程及其教训。

案例一:Capital One 数据泄露

2019年,美国金融服务巨头Capital One遭受了严重的数据泄露,导致超过1亿个客户的个人信息被泄露。

教训与分析

  • 配置错误
    :攻击者通过一个配置错误的Web Application Firewall(WAF)访问了存储在AWS S3桶中的数据。
  • 最小权限原则
    :缺乏严格的权限设置,给了攻击者获取更多数据的机会。
  • 实时监控的重要性
    :事件突显了企业需要有能力监控和响应可疑活动的重要性。

案例二:Zoom 安全漏洞

在2020年的疫情期间,Zoom的使用量激增,但随之而来的是一系列安全问题,包括未经授权的访问和数据泄露。

教训与分析

  • 端到端加密的必要性
    :Zoom最初缺少端到端加密,使会议容易受到窃听。
  • 软件安全开发生命周期
    :软件开发过程中需要内嵌安全设计思维,从而减少后期修补的需要。
  • 用户教育的作用
    :教育用户识别钓鱼链接和保护个人会议室,可以大幅降低风险。

案例三:SolarWinds 供应链攻击

2020年底,被广泛使用的SolarWinds Orion软件受到了复杂的供应链攻击,导致多个政府机构和私营企业的网络安全受到影响。

教训与分析

  • 供应链安全的复杂性
    :此事件表明供应链安全是一个复杂的问题,需要全面的安全机制来保护。
  • 行为分析的必要性
    :通过行为分析工具,异常活动可能会更早被检测到。
  • 跨组织合作的重要性
    :此类跨国攻击揭示了国际安全合作的必要性。

每个案例都为云安全领域提供了独特的洞见,并强调了在安全策略、工具和流程中综合考虑和执行的重要性。通过研究这些案例,企业可以在制定自己的云安全计划时,考虑到这些复杂的情况和多维度的防护措施。在后续章节中,我们将探讨如何根据这些案例学习,并将教训应用于建立一个更为坚固的云安全策略。

关注【TechLeadCloud】,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理专业人士,上亿营收AI产品研发负责人。

如有帮助,请多关注
TeahLead KrisChang,10+年的互联网和人工智能从业经验,10年+技术和业务团队管理经验,同济软件工程本科,复旦工程管理硕士,阿里云认证云服务资深架构师,上亿营收AI产品业务负责人。

转载请备注出处:
https://www.cnblogs.com/zhiyong-ITNote

微调框架概述

模型的微调有多种方式,对于入门的来说,一般都是基于官方的文档微调;最近发现很多开源库,其目的就是支持应用多种微调策略来微调模型,简化模型的微调门槛。比如
ChatGLM-Efficient-Tuning

LLaMA-Factory
。其架构逻辑如下:

最近试玩了这两个框架,个人觉得蛮好的,先不说实际的调试效果,这取决于多种因素,总的来说还是很方便快捷的。方便快捷的基于多种微调策略调试LLM;同时支持多种数据集类型。

LLaMA-Factory

这个开源库相比较其余的库,更全面,更方便。有如下几点我是比较喜欢的。

  1. 训练方法
    1. 如图,多种训练方法都支持,很全面。不过这里的预训练,我理解为是增量预训练;即准备大量的文本数据训练。
    2. 支持全参数、部分参数、LoRA等策略微调。
    3. 降低门槛,一键训练。对于学习来说,可以增加知识面及使用。

image.png

  1. 数据集
    1. 支持多种数据集:增量预训练数据集、指令微调数据集、偏好数据集;在官方文档都是有说明的。
    2. 每次微调前,我都需要斟酌数据集的准备、格式等;但开源库已经准备的很齐全而且各种场景的数据格式都有,直接参考即可;比如单轮对话、多轮对话、指令格式等等。这就极大的方便了数据集的准备。
  2. 其它
    1. 当然还有分布式训练、web界面操作等等

ChatGLM-Finetuning

Finetuning
是专门基于GLM系列的微调库,我个人也试用,还是很方便快速的,而且文档比较清晰,只是在部署时比较简陋,但对于要学习了解微调及LLM一些原理来说,还是很适合入门钻研的。

应用

目前绝大多数的大模型都是基于基座模型(GLM、QWen、LlaMa、BaiChuan)等微调训练而来,不过实现的逻辑却是有多种,要么基于官方的微调文档,要么基于开源微调库实现。
CareGPT
就是基于开源微调库LLaMA-Factory实现的医疗领域大模型。其架构设计如下:

在其架构设计中,有两个部分比较值得关注:

  1. 微调框架的集成
    1. 通过集成了微调框架,调用底层具备的能力,准备多种格式的数据集微调模型。
  2. 数据开放
    1. 基于开源医疗数据集,准备增量预训练预料、指令监督预料、SFT预料等等;扩充基座模型的领域知识能力。

总结

基于个人使用及学习的角度,介绍了微调框架的概述及其应用。在这里面的道道还是蛮多的,有一定的大模型知识再基于这些库去做参考去做应用,将极大的降低LLM的应用门槛。更有甚者可以了解底层的实现逻辑。

转载请备注出处:
https://www.cnblogs.com/zhiyong-ITNote

首发于个人公众号
image

支持.Net/.Net Core/.Net Framework,可以部署在Docker, Windows, Linux, Mac。

RabbitMQ作为一款主流的消息队列工具早已广受欢迎。相比于其它的MQ工具,RabbitMQ支持的语言更多、功能更完善。

1.发送消息、获取消息、使用消息

本文提供一种市面上
最/极
简单的使用RabbitMQ的方式,只需要会调用以下三个方法,
你就几乎可以掌握整个RabbitMQ的使用:

(1)     SendMessage,发送一个消息

(2)     GetMessage,获取一个消息

(3)     UseMessage,使用一个消息(连续使用)

为了调用以上三个方法,首先需要从NuGet引用DeveloperSharp.RabbitMQ包。

然后,对RabbitMQ消息服务器的链接信息进行配置(分.Net Core与.Net Framework两种情况):

若是在.Net Core环境下,你则需要在DeveloperSharp.json文件中添加“DeveloperSharp.RabbitMQ”节点(如下配置示例),并把DeveloperSharp.json文件放到程序执行目录中(即bin目录下与dll、exe等文件的同一目录中,放错了位置会报错)(注意:有些.Net Core版本在Visual Studio“调试”时,不会在bin目录下生成全部的dll、exe,此时需要把此配置文件放在应用程序的“根目录”下)。

{"DeveloperSharp.RabbitMQ":[{"HostName":"135.208.12.236","VirtualHost":"/","UserName":"sa","Password":"aevin.gang","Port":5672}]
}

若是在.Net Framework环境下,你则需要在App.config/Web.config里面添加如下配置:

  <appSettings>
    <addkey="RabbitMQConnectionString"value="hostName=135.208.12.236,virtualHost=/,port=5672,userName=sa,password=aevin.gang" />
  </appSettings>

说明:上述配置中分别设置了RabbitMQ应用所在的服务器IP地址hostName、虚拟主机virtualHost、端口port、用户名userName、密码password(请把这五项的对应值修改成你自己那边的RabbitMQ的对应值)

下面,我们给出一个使用了上述SendMessage、GetMessage、UseMessage三个方法的示例。该示例的功能说明如下:

  • 先向RabbitMQ服务器上名为“aa”的队列发送了5个消息,
  • 然后从RabbitMQ服务器上的“aa”队列中获取,并打印出第1个消息,
  • 最后再连续从RabbitMQ服务器上的“aa”队列中获取剩余4个消息,并把它们写入名为fj.txt的文件。

代码如下:

usingDeveloperSharp.RabbitMQ;--------------------------

        static void Main(string[] args)
{
//发送5个消息(使用SendMessage) RabbitMQHelper.SendMessage("aa", "世界1,你好!");
RabbitMQHelper.SendMessage(
"aa", "世界2,你好!");
RabbitMQHelper.SendMessage(
"aa", "世界3,你好!");
RabbitMQHelper.SendMessage(
"aa", "世界4,你好!");
RabbitMQHelper.SendMessage(
"aa", "世界5,你好!");//获取1个消息(使用GetMessage) string OneMessage = RabbitMQHelper.GetMessage("aa").Message;
Console.WriteLine(OneMessage);
//向fj.txt这个文本文件中写入4个消息(使用UseMessage) RabbitMQHelper.UseMessage("aa", t =>{
System.IO.File.AppendAllText(
"D:/fj.txt", t.Message);return true;
});
}

运行结果如下:

【控制台显示出】:
世界1,你好!

【fj.txt文件中显示出】:
世界2,你好!世界3,你好!世界4,你好!世界5,你好!

(上述示例中,由于SendMessage是同步方法,故“世界1,你好!”~“世界5,你好!”会按顺序显示。若我们把SendMessage方法全部改为异步的SendMessageAsync,则显示结果将不再是按顺序来的,很有可能显示成类似这样:“世界3,你好!世界1,你好!世界5,你好!世界2,你好!世界4,你好!”)

三个方法的详细功能说明(辅助参考):

1)发送一个消息void SendMessage(string QueueName, string Message, Dictionary<string, object> Header = null)//异步方法:SendMessageAsync
2)获取一个消息
RabbitMQMessage GetMessage(
stringQueueName)//异步方法:GetMessageAsync 3)使用一个消息(连续使用)void UseMessage(string QueueName, Func<RabbitMQMessage, bool?>Use)//异步方法:UseMessageAsync 附加说明:
(I)Use返回值为true时,代表当前消息已被有效处理并会被服务器删除。然后程序自动进入下一条消息的使用。
若Use返回值为false时,代表当前消息未被有效处理但仍会被服务器删除。然后程序自动进入下一条消息的使用。
若Use返回值为null时,代表当前消息会被服务器重新队列分配到其它可用的实例上再处理。然后程序自动进入下一条消息的使用。
若Use内部发生未被处理的异常,程序会停止。
(II)RabbitMQMessage对象定义如下:
public classRabbitMQMessage
{
public stringMessage;public IDictionary<string, object>Header;public string Id; //此处系统自动生成的Id是分布式唯一Id。 }

最后说明一点:一旦调用三个方法,会与RabbitMQ服务器建立“长链接”。若要结束“长链接”,可在“任务管理器”中杀死进程。

2.延时队列 & 死信队列

有些场景下,我们希望为使用的消息设定有效期。在有效期内,这些消息有效可用;但过期后,这些消息将变得无效不可用,同时,它们还将自动被丢弃进一个称之为“死信”的队列。

为了说明这些概念,我们还是来举一个具体的例子。该例子的功能说明如下:

  • 首先,在RabbitMQ服务器上定义一个名为"bbq"、且其中存放的消息会在60秒后过期失效的队列。
  • 然后,在RabbitMQ服务器上定义一个与"bbq"队列对应的死信队列。并连续从该死信队列中获取消息并把它们写入名为BB.txt的文件。
  • 最后,向RabbitMQ服务器上的"bbq"队列发送3个消息。

代码如下:

using DeveloperSharp.RabbitMQ;//从NuGet引用DeveloperSharp.RabbitMQ包
--------------------------

            //定义bbq队列,其中存放的消息会在60秒后过期
            var myQ = RabbitMQHelper.SetQueue("bbq", 60000);//定义与bbq队列对应的死信队列
            var expQ = RabbitMQHelper.GetQueue("bbq");//向BB.txt这个文本文件中连续写入死信队列中的消息
            expQ.UseMessage(t =>{
System.IO.File.AppendAllText(
"D:/BB.txt", t.Message);return true;
});
//向bbq队列发送3个消息 myQ.SendMessage("jinA");
myQ.SendMessage(
"jinB");
myQ.SendMessage(
"jinC");

运行以上程序:

60秒之内,【BB.txt文件】中没有内容

60秒以后,【BB.txt文件】中显示出:
jinAjinBjinC

通过以上例子,我们可简单预测一下,延时队列&死信队列常用在“限时消费”、“过期处理”等场景。生活中最常见示例如:订单请在10分钟内支付完毕、等等之类功能...

(其它说明:为了演示便利,文本前面给出的几个代码示例中,把SendMessage、GetMessage、UseMessage三个方法都放到了同一段代码程序中,这样做不好,偶尔会造成一些资源冲突。在实际使用中,最好是把它们分开分别放到三段不同的代码程序中,以获取最佳效果。比如:三个按钮、三个服务,之类...)

使用消息服务对解耦分布式系统、实现发布/订阅、提高系统性能、等方面都有巨大用处,相信本文会扩展你的思维认知,让你在相关技术解决方案上有更多灵活思路+联想空间!