在组织中实施生成式人工智能系统(Generative AI System,简称 GenAISys)不仅仅是通过 API 集成一个独立的模型,如 GPT、Grok、Llama 或 Gemini。虽然这通常是一个起点,但我们常常误以为这就是终点。随着 AI 需求的不断增长并扩展到各个领域,要求我们实施超越简单集成预构建模型的先进 AI 系统。
一个面向业务的 GenAISys 应当在组织内提供媲美 ChatGPT 级别的功能,但其能力和特性远不止于此。它必须具备自然语言理解(NLU)、通过对话会话中多轮记忆实现的上下文感知,以及具备自主执行图像、音频和文档分析与生成的智能代理功能。可以把生成式 AI 模型看作一个具有多种功能的实体,其中包括作为智能协同工作的 AI 代理。
本章将从定义什么是面向业务的 GenAISys 开始。接着,我们将聚焦于生成式 AI 模型(如 GPT-4o)所扮演的核心角色,这类模型既能协调也能执行任务。基于此,我们将为上下文感知和记忆保留奠定基础,讨论四种生成式 AI 记忆类型:无记忆、短期记忆、长期记忆和多会话记忆。我们还将定义一种新的检索增强生成(RAG)方法,为数据检索引入额外的维度:指令和智能推理场景。通过将存储于向量库中的指令添加进提示,RAG 的能力被进一步提升。与此同时,我们将探讨 GenAISys 的一个关键组成部分——人类角色,展示 AI 系统在整个生命周期中对人类专业知识的需求。此外,我们还会定义多个实现层级,以适应 GenAISys 的范围和规模,既满足业务需求,也考虑预算和资源。
最后,我们将展示如何利用 OpenAI 的大语言模型(LLM)和多模态 API 来实现上下文感知和记忆保留。一个 GenAISys 若无坚实的记忆保留功能,无法正常运作——没有记忆就没有上下文,没有上下文则无法实现可持续生成。贯穿全书,我们会针对不同任务创建无记忆、短期记忆、长期记忆和多会话记忆模块。通过本章学习,您将获得清晰的概念框架,理解什么使 AI 系统具备面向业务的实用性,并积累构建 AI 控制器初步模块的实践经验。
简而言之,本章涵盖以下主题:
- 面向业务的 GenAISys 组成部分
- AI 控制器与智能代理功能(模型无关)
- 人类混合角色与 AI 协作
- 商业机会与应用范围
- 通过记忆保留实现上下文感知
让我们首先来定义什么是面向业务的 GenAISys。
面向业务的 GenAISys 组成部分
面向业务的 GenAISys 是一个模块化的协调器,能够无缝集成标准 AI 模型和多功能框架,实现混合智能。通过将生成式 AI 与智能代理功能、RAG、机器学习(ML)、网络搜索、非 AI 操作及多会话记忆系统相结合,我们能够为多样且复杂的任务提供可扩展且自适应的解决方案。以 ChatGPT 为例,人们常用“ChatGPT”既指代生成式 AI 模型,也指代应用本身。然而,在聊天界面背后,ChatGPT 和 Gemini 等工具其实是更大系统(在线助手)的一部分,由智能 AI 控制器全面集成和管理,以提供流畅的用户体验。
正是 Tomczak(2024 年)引导我们从将生成式 AI 模型视为单一实体,转向思考更复杂的 GenAISys 架构。他的论文中用“GenAISys”一词描述这些更复杂的平台。本书的方法是拓展 GenAISys 的视野,包含先进的 AI 控制器功能和业务生态中人类角色。GenAISys 并无单一万能架构,但本节将定义实现 ChatGPT 级功能的主要组件,包括生成式 AI 模型、记忆保留功能、模块化 RAG 及多功能能力。各组件在 GenAISys 框架中的作用如图 1.1 所示:
现在让我们定义构成 GenAISys 的 AI 控制器架构以及人类角色。
AI 控制器
一个面向业务的 GenAISys 核心是 AI 控制器,它会基于输入的上下文激活定制的 ChatGPT 级功能。与传统的有预定任务顺序的流水线不同,AI 控制器无固定执行顺序,会根据每个输入的具体上下文动态调整任务,如网页搜索、图像分析和文本生成等。这样一种基于智能代理和上下文驱动的方法,使 AI 控制器能够无缝协调各个组件,确保生成式 AI 模型的有效且连贯的表现。
要实现一个定制化的 ChatGPT 级 AI 控制器,需要大量工作和努力,但其回报是新一类能够承受现实压力、产生实际业务成果的 AI 系统。一个成熟的 AI 控制器生态能够支持多个领域的应用场景:客户支持自动化、销售线索生成、生产优化(服务与制造业)、医疗响应支持、供应链优化,甚至任何市场需求所在!因此,GenAISys 需要 AI 控制器来协调多条流水线,例如通过上下文感知理解提示意图,以及通过记忆保留支持跨会话的连续性。
GenAISys 还必须定义人类角色,以确定可访问的功能和数据。但在探讨人类角色之前,我们先拆解支撑 AI 控制器的关键组件。如图 1.1 所示,生成式 AI 模型、记忆模块、模块化 RAG 和多功能能力各自在实现灵活、上下文驱动的编排中扮演关键角色。下面我们将探讨这些元素如何协同构建一个面向业务的 GenAISys。首先定义生成式 AI 模型的角色。
生成式 AI 的模型无关方法
构建可持续的 GenAISys 需要模型可互换性——即根据需要灵活替换底层模型。生成式 AI 模型应作为系统中的一个组件,而非系统构建的核心。这样,当我们的模型被弃用、需要更新,或我们发现性能更优的模型时,就能方便地替换成更适合项目的模型。
因此,生成式 AI 模型可以是 OpenAI 的 GPT、Google 的 Gemini、Meta 的 Llama、xAI 的 Grok,或任何 Hugging Face 上的模型,只要它支持所需的任务。理想情况下,应选择涵盖文本、视觉和推理能力的多用途、多模态模型。Bommasani 等人(2021 年)对这类基础模型做了全面分析,其范围超越了大型语言模型(LLM)。
生成式 AI 模型主要有两个功能,如图 1.2 所示:
- 编排功能:根据输入确定需要触发哪些任务。输入可以是用户的提示,也可以是流水线中其他功能的系统请求。编排功能的智能代理可触发网页搜索、文档解析、图像生成、RAG、机器学习功能、非 AI 功能及任何集成进 GenAISys 的其他功能。
- 执行功能:执行编排层请求的任务,或直接基于输入执行任务。例如,像“美国首都是什么?”这样的简单查询,可能不需要复杂功能;但文档分析请求可能需要调用多个功能(分块、嵌入、存储和检索等)。
请注意,图 1.2 有一个独特之处:输入、编排和执行组件之间没有箭头指示顺序。不同于传统的硬编码线性流水线,灵活的 GenAISys 组件是无序的。我们构建这些组件后,由编排功能自动选择的场景动态决定任务的执行顺序。
这种灵活性确保系统能够适应广泛的任务。虽然我们无法构建一个能解决所有任务的系统,但可以构建一个满足公司内多种任务需求的系统。以下是两个示例工作流,说明 GenAISys 如何根据参与角色动态排列任务:
- 人类角色 可以被配置,使得在某些情况下,用户输入直接执行简单的 API 调用,提供直接的响应,比如查询某国首都。在这种情况下,生成式 AI 模型直接执行请求。
- 系统角色 可以动态配置,用来编排一组指令,比如先进行网页搜索,再对网页内容进行总结。此时,系统会经过编排流程以生成输出。
可能性是无限的;不过所有场景都会依赖记忆功能,以确保行为的一致性和上下文感知。接下来我们来看记忆模块。
构建 GenAISys 的记忆
先进的生成式 AI 模型,如 OpenAI 的 GPT、Meta 的 Llama、xAI 的 Grok、Google 的 Gemini 以及众多 Hugging Face 变体,均基于上下文驱动,无论其具体版本或性能如何。你会根据项目选择模型,但基本原则很简单:
无上下文 => 无意义的生成
当我们使用 ChatGPT 或其他协助工具时,无需担心上下文记忆问题,它们帮我们自动处理。我们只需开始对话,系统会顺畅运行,我们根据得到的回复调整提示即可。然而,当我们从头开发基于生成式 AI API 的系统时,就必须显式构建上下文感知和记忆保留功能。
在众多记忆保留策略中,使用 API 有四种突出的方法:
- 无状态、无记忆会话:请求发送至 API,返回响应,但不保留任何记忆。
- 短期记忆会话:会话期间请求与响应的交换内容存储在内存中,但会话结束后不再保留。
- 多会话长期记忆:请求与响应的交流内容被存储并在会话结束后依然保留记忆。
- 多主题跨会话长期记忆:该功能将多个会话的长期记忆相互关联。每个会话会被赋予一个角色:系统或多个用户。这在 ChatGPT 等平台中并非标准功能,但对于组织内的工作流管理至关重要。
图 1.3 总结了这四种记忆架构。我们将在后续章节“上下文感知与记忆保留”中,使用 Python 和 GPT-4o 演示每种配置的实现。
这四种记忆类型作为开发 GenAISys 的起点,可以根据需要进一步扩展。然而,实际应用中通常需要更多功能,包括:
- 人类角色,用于定义能够访问会话历史或跨多主题会话集的用户或用户组。这将使系统超越 ChatGPT 级别的平台。我们将在第 2 章《构建生成式 AI 控制器》中介绍这一内容。
- 存储策略,用于定义需要存储和丢弃的内容。我们将在第 3 章《将动态 RAG 集成到 GenAISys》中,结合 Pinecone 向量存储进一步探讨存储策略。
生成模型的记忆主要分为两类:
- 语义记忆,包含诸如科学事实等硬性知识;
- 情景记忆,包含带时间戳的个人记忆,如个人事件和商务会议。
显然,构建 GenAISys 的记忆系统需要精心设计和有意识的开发,以实现 ChatGPT 级别的记忆功能和诸如长期、多主题会话等额外配置。这个高级记忆系统的最终目标是增强模型的上下文感知能力。虽然像 GPT-4o 这样的生成式 AI 模型内置了上下文感知,但为了扩展我们所构建的上下文驱动系统(如 GenAISys)的应用范围,我们还需要整合先进的检索增强生成(RAG)功能。
RAG 作为智能多功能协同编排器
本节将解释为何在 GenAISys 中使用 RAG 来实现三大核心功能:
- 知识检索:获取针对性且细致的信息
- 上下文窗口优化:设计优化提示语
- 智能多功能编排:动态触发功能
我们先从知识检索开始讲起。
1. 知识检索
生成式 AI 模型擅长展示其通过训练学到的参数化知识,这些知识被嵌入在模型权重中。GPT、Llama、Grok 和 Gemini 等模型均基于此原理。然而,这些知识的截止日期固定,之后模型不会再自动更新数据。为更新或补充知识,我们有两个选择:
- 隐式知识:通过微调模型,将更多训练知识加入权重(参数化知识)。如果面对的是每天变化的动态数据(如天气预报、新闻、社交媒体信息),此过程具有挑战性,同时存在成本和风险,尤其当微调效果不理想时。
- 显式知识:将数据存储在文件或向量数据库中,使知识结构化、可访问、可追踪且可更新,然后通过高级查询来检索信息。
这里需要特别指出,静态的隐式知识若无动态显式知识支持,难以实现有效扩展。后续章节会详细介绍。
2. 上下文窗口优化
生成式 AI 模型不断扩大其上下文窗口的容量。例如,撰写本文时,支持的上下文长度如下:
- Llama 4 Scout:1,000 万个 token
- Gemini 2.0 Pro 实验版:200 万个 token
- Claude 3.7 Sonnet:20 万个 token
- GPT-4o:12.8 万个 token
虽然容量惊人,但大上下文窗口带来的 token 成本和计算资源消耗也相当高。此外,随着上下文变大,模型的准确率反而可能下降。而且,我们并不总是需要最大的上下文窗口,只需要最适合当前项目的即可。这就使得在必要时采用 RAG 来优化项目成为合理选择。
RAG 的分块过程将大段内容拆分成更细粒度的 token 组。对这些内容块进行嵌入后,转为向量,可以高效存储并从向量库中检索。这种方法确保每个任务只使用最相关的上下文,最大限度减少 token 用量,提高响应质量。因此,我们可以结合利用生成式 AI 的参数隐式知识和 RAG 对向量库中大量显式非参数数据的支持。我们还可以进一步将 RAG 用作编排器。
3. 智能多功能编排器
AI 控制器通过生成式 AI 模型与 RAG 连接。RAG 用于灵活地将各种指令增强到模型输入中。初看起来,利用 RAG 检索指令似乎有些反直觉——但仔细想想,如果我们将指令存储为向量,并检索最适合当前任务的一组指令,就能快速且灵活地启用智能代理功能,产生高效结果,而无需每次调整指令策略时都重新微调模型。
这些指令充当了针对特定任务优化的提示语。从这个角度看,RAG 成为 AI 系统编排层的一部分。像 Pinecone 这样的向量存储可以存储并返回这类功能信息,如图 1.4 所示:
这些场景的编排通过以下方式实现:
- 场景检索:AI 控制器将从向量数据库(如 Pinecone)接收结构化指令(场景),这些指令会根据用户查询进行适配。
- 动态任务激活:每个场景会指定一系列任务,例如网页搜索、机器学习算法、标准 SQL 查询或我们所需的任何功能。
将传统功能和机器学习功能加入 GenAISys,能够显著增强其能力。GenAISys 的模块化架构使这种多功能方法更为高效,具体应用场景包括:
- 通过网页搜索进行实时检索,以增强输入内容;
- 文档分析,用于处理文档并填充向量库;
- 文档搜索,从向量库中检索已处理文档的部分内容;
- 机器学习,如 K-均值聚类(KMC)进行数据分组,k 近邻算法(KNN)用于相似度搜索;
- 使用 SQL 查询对结构化数据集执行基于规则的检索;
- 以及项目或工作流中所需的任何其他功能。
RAG 依然是 GenAISys 的关键组件,我们将在第 3 章《将动态 RAG 集成到 GenAISys》中构建它,并在该章节进一步增强系统的多功能特性。
接下来,我们将讨论构成任何 GenAISys 骨干的人类角色。
人类角色
与普遍认知相反,像 ChatGPT 这样成功部署和运行的 GenAISys,实际上在其整个生命周期中都高度依赖人类的参与。尽管这些工具看起来能够轻松完成复杂任务,但其背后有多层次的人类专业知识、监督和协调,保障其平稳运行。
软件专业人员首先需要设计架构、处理海量数据集,并在配备尖端计算资源的百万美元服务器上对系统进行微调。部署后,还需庞大团队持续监控、验证并解读系统输出,针对错误、新兴技术和监管变化不断调整优化。此外,将这些系统部署到企业内网、面向公众的网站、研究环境或学习管理系统中,更需要跨多个领域的跨职能协调。
这些任务要求高度的专业知识和合格的团队支持。因此,人类不仅不可替代,而且至关重要!他们是 AI 系统的架构师、监督者、策展人和守护者,负责创建并维护这些系统。
GenAISys 的实施与治理团队
实施 GenAISys 需要技术能力和团队协作,赢得最终用户的支持。这是 AI 控制器设计、用户角色和期望之间的协同挑战。任何认为部署真实世界 AI 系统只是简单获得某个模型(如最新的 GPT、Llama 或 Gemini)的人,若仔细考察所需资源,就会发现其中的真正挑战。AI 系统的开发、部署和维护可能涉及大量人力资源。当然,并非每个组织都需要涵盖所有角色,但我们必须认识到所涉及的技能范围,包括但不限于:
- 项目经理(PM)
- 产品经理
- 项目负责人(Program Manager)
- 机器学习工程师(MLE)/数据科学家
- 软件开发人员/后端工程师(BE)
- 云工程师(CE)
- 数据工程师(DE)及隐私经理
- UI/UX 设计师
- 合规与监管官员
- 法律顾问
- 安全工程师(SE)及安全官员
- 各领域的主题专家(Subject-matter Experts)
- 质量保证工程师(QAE)及测试人员
- 技术文档撰写员
- 系统维护与支持技术员
- 用户支持
- 培训师
以上仅为部分示例,足以展示构建和运营一个全规模 GenAISys 涉及的众多不同角色。图 1.5 展示了设计与实施 GenAISys 是一个持续的过程,人力资源在每个阶段都不可或缺。
我们可以看到,GenAISys 的生命周期是一个永无止境的过程:
- 业务需求会随着市场约束不断演变
- GenAISys 设计必须随每一次业务变动而调整
- AI 控制器规范必须适应技术进步
- 实施过程必须配合不断变化的业务规格
- 用户反馈驱动持续改进
现实中的 AI 运行高度依赖人类能力——尤其是 AI 无法复制的上下文理解和技术洞察。AI 可以有效自动化多种任务,但正是人类带来了将这些系统与真实业务目标对齐的深刻洞见。
接下来,我们进一步探讨 RACI 热力图,说明为何人类是 GenAISys 的关键组成部分。
GenAISys RACI
组织一个 GenAISys 项目需要的人力资源远超 AI 自动化所能提供的范围。RACI 是一种责任分配矩阵,帮助定义每项任务或决策中的角色和责任,明确谁负责(Responsible)、谁承担最终责任(Accountable)、谁被咨询(Consulted)及谁需被通知(Informed)。RACI 非常适合管理构建 GenAISys 的复杂性,为系统生命周期中日益增多的人类角色提供结构化管理框架,并为协调其参与提供务实方法。
与任何复杂项目一样,参与 GenAISys 的团队需要跨学科协作,RACI 助力明确各自职责。RACI 中的每个字母代表一种角色类型:
- R(Responsible) :积极执行任务的人,负责任务的正确完成。例如,机器学习工程师(MLE)负责使用 ML 算法处理数据集。
- A(Accountable) :对任务成败承担最终责任的人,监督负责执行任务的人员。例如,产品负责人(PO)需确保 MLE 按时且符合规范完成任务,若未完成,则 PO 需承担责任。
- C(Consulted) :提供意见、建议和反馈,但不负责执行的人。例如,零售领域的主题专家帮助 MLE 理解 ML 算法目标。
- I(Informed) :需要了解任务进展或结果,但不参与执行的人。例如,数据隐私官(DPO)希望知晓系统安全功能情况。
一个 RACI 热力图通常会包含项目中各人类角色的图例。以下是我们将用于构建热力图的角色:
- MLE:开发并集成 AI 模型
- DE:设计数据管理流水线
- BE:构建 API 交互
- 前端工程师(FE):开发最终用户功能
- UI/UX 设计师:设计用户界面
- 云工程师/DevOps 工程师(CE/DevOps):管理云基础设施
- 提示工程师(PE):设计优化提示语
- 安全工程师(SE):负责数据安全与访问
- 数据隐私官(DPO):管理数据治理和合规
- 法律/合规官(LC):审查项目法律范围
- 质量保证工程师(QAE):测试 GenAISys
- 产品负责人(PO):定义产品范围和规模
- 项目经理(PM):协调资源与时间安排
- 技术文档撰写员(TW):编写文档
- 供应商经理(VM):与外部供应商和服务提供商沟通
并非每个 GenAISys 项目都会包含所有角色,但根据项目范围和规模,许多角色都是关键。接下来,列出上述角色在典型生成式 AI 项目中的关键职责:
- 模型:AI 模型开发
- 控制器:API 与多模态组件的编排
- 流水线:数据处理与集成工作流
- UI/UX:用户界面和体验设计
- 安全:数据保护和访问控制
- DevOps:基础设施、扩展与监控
- 提示:模型交互的设计与优化
- 质量保证:测试和质量保障
我们已经定义了角色和任务,现在可以展示它们如何映射到现实场景。图 1.6 展示了一个 GenAISys 的 RACI 热力图示例。
例如,在这张热力图中,机器学习工程师(MLE)承担以下职责:
- 对模型(如 GPT-4o)负责且承担最终责任(R 和 A)
- 对模型提示语负责且承担最终责任(R 和 A)
- 作为专家被咨询,参与控制器、流水线及测试(QA)(C)
- 被告知用户界面/用户体验(UI/UX)、安全和 DevOps 相关信息(I)
我们可以用一句简单的规则来总结 GenAISys:
没有人类 -> 没有系统!
我们看到,人类在 GenAISys 的整个生命周期中都是不可或缺的,从设计到维护与支持,包括不断根据用户反馈进行演进。人类一直存在,也将长期存在!
接下来,让我们探讨 GenAISys 可以带来的商业机会。
商业机会与应用范围
我们往往无法获得 OpenAI、Meta、xAI 或 Microsoft Azure 等拥有数十亿美元资源的公司那样的支持,来构建类似 ChatGPT 的平台。前一节已经展示,表面上看似简单、无缝的 ChatGPT 界面背后,实际上是复杂且昂贵的基础设施层、稀缺的人才以及持续的改进和演进,这些资源只有大型企业才能承担。因此,从一开始更明智的路径是明确我们所在的项目类别,利用现有模块和库的力量来构建我们的 GenAISys。无论是营销、金融、生产还是支持等应用场景,我们都需要找到合适的范围和规模,来实现一个切实可行的 GenAISys。
任何 GenAISys 项目的第一步是定义项目目标(机会),包括其范围和规模,正如我们前面提到的。在此阶段,还需要评估风险,如成本、保密性和资源可用性(风险管理)。
根据资源、目标、用例复杂度和预算的不同,我们可以将 GenAISys 项目划分为三种主要的业务实施类型,如图 1.7 所示:
- 混合方法:利用现有 AI 平台
- 小范围小规模:聚焦型 GenAISys
- 全规模生成式多代理 AI 系统:完整的 ChatGPT 级生成式 AI 平台
让我们从混合方法开始,这是一种无需过度构建即可交付业务成果的实用方案。
混合方法
混合框架通过结合现成可用的 SaaS 平台与仅在必要时开发的定制组件(如网页搜索和数据清洗),帮助你最大限度地减少开发成本和时间。这样,你可以利用生成式 AI 的强大能力,而无需从零开发所有内容。下面我们来看其关键特性和一些示例用例。
关键特性
- 依赖成熟的网络服务,如 OpenAI 的 GPT API、AWS、Google AI 或 Microsoft Azure,这些平台提供核心的生成式功能。
- 通过集成领域专用的向量库和组织内部专有数据集,定制项目。
- 聚焦于有针对性的功能开发,比如客户支持自动化或营销活动生成。
用例示例
- 构建针对法律、医疗或产品相关客户查询的领域专用向量库。
- 在社交媒体平台上构建具备实时能力的客户支持系统。
这一类别使得以更少的成本和开发投入,实现更多的功能成为可能。混合系统既可以作为独立的 GenAISys,也可以作为更大型生成式 AI 平台中的子系统,用于那些不需要全规模开发的场景。
接下来,我们来看一个小范围、小规模的 GenAISys 如何帮助我们更进一步。
小范围小规模
小规模的 GenAISys 可能包含一个智能的、由生成式 AI 驱动的 AI 控制器,连接到一个向量库。该架构使系统能够检索数据、触发指令,并调用诸如网页搜索或机器学习等额外功能,而无需搭建全规模的基础设施。
关键特性
- 明确界定的盈利系统,旨在以最优的开发时间和成本实现合理的目标
- AI 控制器编排指令场景,进而触发 RAG、网页搜索、图像分析及适合你需求的定制任务
- 重点关注高优先级、高产出的功能
用例示例
- 用于任何类型文档的检索与摘要,通过内容分块与嵌入实现细致分析的 GenAISys
- 利用实时网页搜索增强 GPT 或 Llama 等模型,绕过其数据截止日期限制——非常适合天气预报或新闻监控等无需持续微调的应用
这一类别比混合方法更进一步,同时仍保持现实可控,适合中小型企业或大型组织内的单个部门使用。
全规模 GenAISys
如果你在一个拥有庞大预算和先进基础设施的组织中,与专家团队一起工作,那么这个类别适合你。你的团队可以构建一个全规模的 GenAISys,逐步接近 ChatGPT 级别平台的能力。
关键特性
- 完整的 AI 控制器,管理和编排复杂的自动化工作流,包括 RAG、指令场景、多模态功能和实时数据
- 需要大量计算资源和高技能的开发团队
- 可以将本书中构建的 GenAISys 视为一个 alpha 版本——一个模板,可根据需要在组织内任意复制、配置和部署
用例示例
- GenAISys 已在医疗领域应用,协助患者诊断和疾病预防。例如,巴黎的居里研究所(Institut Curie)拥有非常先进的 AI 研究团队:institut-curie.org/。
- 许多大型组织已开始使用 GenAISys 进行欺诈检测、天气预测和法律咨询。
- 你可以加入这些拥有资源构建可持续 GenAISys 的大型组织,无论是在云平台、本地服务器,还是两者兼用。
这三类——混合、小规模和全规模——为构建 GenAISys 提供了不同路径,具体取决于你的组织目标、预算和技术能力。本书将探讨构成 GenAISys 的关键组件。到书末,你将具备参与任一类别项目的能力,并能为所参与项目提供现实且技术扎实的建议。
现在,让我们揭开盖子,开始在代码中构建上下文感知和记忆保留功能。
上下文感知与记忆保留
本节中,我们将开始使用 Python 实现上下文感知与记忆保留的模拟示例,以阐释前文“构建 GenAISys 的记忆”部分介绍的概念。目标是展示管理上下文和记忆的实用方法——这两大功能随着生成式 AI 平台的发展愈发重要。
请打开 GitHub 仓库(github.com/Denis2054/B…)中 chapter01 文件夹里的 Contextual_Awareness_and_Memory_Retention.ipynb 文件。你会发现该 notebook 分为五个主要部分:
- 环境搭建,构建可复用函数,并将其存储在仓库的 commons 目录中,方便全书中按需复用
- 无状态且无记忆的会话,涵盖语义记忆和情景记忆
- 会话期间的短期记忆,用于上下文感知
- 跨多次会话的长期记忆,实现不同会话间的上下文保留
- 多主题跨会话的长期记忆,扩展长期记忆至之前独立的多个会话
目标是通过明确的流程展示每种记忆类型。这些示例目前故意保持手动操作,但将在下一章中开始构建的 AI 控制器中实现自动化管理。
由于生成模型的概率性特征,你可能会发现同一提示在多次运行中输出不同。请确保在单次会话中完整运行整个 notebook,因为此 notebook 中的记忆保留在不同单元中是显式实现的。在第 2 章中,这一功能将变为持久化,并由 AI 控制器全面管理。
第一步是安装环境。
环境搭建
我们需要为 GenAISys 项目创建一个 commons 目录。该目录将包含本书 GitHub 仓库中所有 notebook 通用使用的主要模块和库。这样做的目的在于专注于系统的维护与支持设计。通过将主要模块和库集中放置在一个目录中,我们可以集中管理这一资源,避免在每个 notebook 中重复配置步骤。此外,本节内容也将作为本书 GitHub 仓库中所有 notebook 的参考点。我们只需描述一次资源的下载过程,之后在全书中重复使用,从而构建我们的教学用 GenAISys。
因此,我们可以从 commons 目录下载 notebook 所需资源,并安装依赖。
第一步是下载 grequests.py,这是贯穿本书将使用的实用脚本。它包含一个函数,可以直接从 GitHub 下载所需文件:
!curl -L https://raw.githubusercontent.com/Denis2054/Building-Business-Ready-Generative-AI-Systems/master/commons/grequests.py --output grequests.py
该脚本的目的是通过调用 grequests 中的 download 函数,从仓库的任意目录下载文件:
import sys
import subprocess
from grequests import download
download([directory], [file])
该函数使用 curl 命令从指定目录和文件名下载文件,同时包含基本的错误处理,以防命令执行失败。
代码开始时导入了 subprocess,用于处理路径和命令。download 函数包含两个参数:
def download(directory, filename):
directory:GitHub 仓库中存放文件的子目录filename:要下载的文件名
接着定义了 GitHub 仓库的基础 URL,指向我们需要的原始文件:
base_url = 'https://raw.githubusercontent.com/Denis2054/Building-Business-Ready-Generative-AI-Systems/main/'
现在需要用目录和文件名参数拼接完整文件 URL:
file_url = f"{base_url}{directory}/{filename}"
函数中定义了 curl 命令:
curl_command = f'curl -o {filename} {file_url}'
最后执行下载命令:
subprocess.run(curl_command, check=True, shell=True)
check=True:如果curl命令失败会触发异常shell=True:通过 shell 执行命令
使用 try-except 块进行错误处理:
try:
# 准备带 Authorization 头的 curl 命令
curl_command = f'curl -o {filename} {file_url}'
# 执行 curl 命令
subprocess.run(curl_command, check=True, shell=True)
print(f"成功下载文件 '{filename}'。")
except subprocess.CalledProcessError:
print(f"下载文件 '{filename}' 失败。请检查 URL 和网络连接。")
现在,我们拥有一个独立的下载脚本,将在本书中贯穿使用。接下来,让我们下载程序所需的资源。
下载 OpenAI 资源
本 notebook 需要三个资源:
requirements01.py:用于安装我们需要的指定版本的 OpenAI 库openai_setup.py:用于初始化 OpenAI API 密钥openai_api.py:包含一个可复用的函数,用于调用 GPT-4o 模型,避免在多个单元或 notebook 中重复编写相同代码
我们将在全书中重复使用这些函数来进行标准的 OpenAI API 调用。你可以随时返回本节,复查安装过程。其他场景需要时会补充到 commons 目录。
可以使用 download() 函数下载这些文件:
from grequests import download
download("commons", "requirements01.py")
download("commons", "openai_setup.py")
download("commons", "openai_api.py")
第一个资源:requirements01.py。
安装 OpenAI
requirements01.py 确保安装指定版本的 OpenAI 库,以避免与其他已安装库冲突。代码先卸载现有版本,强制安装指定版本,并验证安装结果。函数带有错误处理机制,执行安装命令:
def run_command(command):
try:
subprocess.check_call(command)
except subprocess.CalledProcessError as e:
print(f"命令失败:{' '.join(command)}\n错误信息:{e}")
sys.exit(1)
函数第一步是卸载当前已安装的 OpenAI 库(如果存在):
print("正在安装 'openai' 版本 1.57.1...")
run_command([sys.executable, "-m", "pip", "install", "--force-reinstall", "openai==1.57.1"])
然后安装指定版本的 OpenAI:
run_command(
[ sys.executable, "-m", "pip", "install", "--force-reinstall", "openai==1.57.1" ]
)
最后,函数验证 OpenAI 是否正确安装:
try:
import openai
print(f"'openai' 版本 {openai.__version__} 已安装。")
except ImportError:
print("安装后无法导入 'openai' 库。")
sys.exit(1)
函数执行结束时的输出应为:
'openai' 版本 1.57.1 已安装。
现在我们可以初始化 OpenAI API 密钥了。
OpenAI API 密钥初始化
在 notebook 中初始化 OpenAI API 密钥有两种方法:
- 使用 Google Colab Secrets:在 Google Colab 左侧面板点击钥匙图标(如图 1.8 所示),然后点击“添加新密钥”,并添加你的密钥,命名为你将在 notebook 中使用的密钥变量名:
然后,我们可以使用 Google 提供的函数,在 openai_setup.py 文件中的 initialize_openai_api 函数里初始化密钥:
# 导入库
import openai
import os
from google.colab import userdata
# 初始化 OpenAI API 密钥的函数
def initialize_openai_api():
# 通过名称获取密钥
API_KEY = userdata.get('API_KEY')
if not API_KEY:
raise ValueError("userdata 中未设置 API_KEY!")
# 在环境变量和 OpenAI 中设置 API 密钥
os.environ['OPENAI_API_KEY'] = API_KEY
openai.api_key = os.getenv("OPENAI_API_KEY")
print("OpenAI API 密钥初始化成功。")
如果将 google_secrets 设为 True,则激活此方法:
google_secrets = True
if google_secrets == True:
import openai_setup
openai_setup.initialize_openai_api()
自定义安全方法
你也可以选择自定义方法,或通过在代码中输入密钥来初始化 API,做法是将 google_secrets 设为 False,取消注释下面代码,并直接输入你的 API 密钥,或者使用你选择的任何方法:
if google_secrets == False: # 取消注释代码并选择任意方法初始化 API_KEY
import os
# API_KEY = [你的 API_KEY]
# os.environ['OPENAI_API_KEY'] = API_KEY
# openai.api_key = os.getenv("OPENAI_API_KEY")
# print("OpenAI API 密钥初始化成功。")
无论哪种方式,代码都会创建一个环境变量:
os.environ['OPENAI_API_KEY'] = API_KEY
openai.api_key = os.getenv("OPENAI_API_KEY")
至此,OpenAI API 密钥已完成初始化。接下来我们将导入一个自定义的 OpenAI API 调用函数。
OpenAI API 调用
下一步目标是在 openai_api.py 中创建一个 OpenAI API 调用函数,我们可以通过两行代码导入:
# 从自定义的 OpenAI API 文件导入函数
import openai_api
from openai_api import make_openai_api_call
该函数设计为调用时接收四个变量,并无缝显示结果:
# API 函数调用
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role)
print(response)
函数的参数说明如下:
input:包含输入(用户或系统),例如 “夏威夷在哪里?”mrole:定义系统角色,例如 “你是一名地质学专家” 或简写为 “System”mcontent:系统预期的内容,例如 “你是一名地质学专家”user_role:定义用户角色,例如 “user”
函数代码的第一部分定义了本 notebook 中使用的模型,并用发送的参数创建 API 调用所需的消息对象:
def make_openai_api_call(input, mrole, mcontent, user_role):
# 定义模型参数
gmodel = "gpt-4o"
# 创建消息对象
messages_obj = [
{
"role": mrole,
"content": mcontent
},
{
"role": user_role,
"content": input
}
]
接着,我们用字典定义本 notebook 的 API 调用参数:
# 在名为 params 的字典中定义所有参数:
params = {
"temperature": 0,
"max_tokens": 256,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0
}
参数说明:
temperature:控制回答的随机性。0 表示确定性输出,较高值(如 0.7)生成更有创造性的回答。max_tokens:限制回答的最大 token 数。top_p:实现核采样,通过从累计概率为 1 的顶部 token 中采样控制回答多样性。frequency_penalty:降低重复 token 的频率,避免冗余。0 表示不处罚,2 表示强处罚。此处因 OpenAI 模型性能高,设为 0 即足够。presence_penalty:通过处罚已出现内容鼓励新内容,参数同频率惩罚。此处同样因模型性能高,无需启用。
然后初始化 OpenAI 客户端,创建 API 调用实例:
client = OpenAI()
最后,发送模型名称、消息对象及解包参数,完成 API 调用:
# 执行 API 调用
response = client.chat.completions.create(
model=gmodel,
messages=messages_obj,
**params # 解包参数字典
)
函数结束时返回 API 响应的内容:
# 返回响应内容
return response.choices[0].message.content
该函数帮助我们专注于 GenAISys 架构,无需在 notebook 中重复引入大量库和函数。
在 notebook 中,我们有如下过程:
- 程序向函数提供输入、角色和消息内容
messages_obj保存对话历史- API 行为参数定义在
params字典中 - 使用 OpenAI 客户端向模型发起 API 调用
- 函数仅返回 AI 的回答内容
GenAISys 会包含多个组件,其中就包括生成模型。你可以选择适合项目的模型。本书中所用模型仅供教学,不作为推荐。
现在,让我们构建并运行一个无状态且无记忆的会话。
1. 无状态且无记忆的会话
无状态且无记忆的会话适用于只需单次临时交互、且请求之间不存储任何信息的场景。本节中的示例均为无状态且无记忆:
- 无状态 表示每个请求将独立处理
- 无记忆 意味着没有机制记住之前的交互
语义查询
该请求期望得到纯粹的语义和事实性回答:
uinput = "Hawai is on a geological volcano system. Explain:"
mrole = "system"
mcontent = "You are an expert in geology."
user_role = "user"
现在,调用 OpenAI API 函数:
# 函数调用
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role)
print(response)
如你所见,回复纯属语义内容:
夏威夷位于中太平洋的火山热点上,该热点负责夏威夷群岛的形成。这个热点是地球地幔深处的岩浆上升到表面的区域,产生火山活动……
下一个查询是情景记忆类型。
带有语义内涵的情景查询
本例中的查询属于情景记忆类型,基于个人经历。然而,由于对夏威夷的描述,查询中带有一定的语义色彩。以下是消息内容,颇具诗意:
# API 消息
uinput = "I vividly remember my family's move to Hawaii in the 1970s, how they embraced the warmth of its gentle breezes, the joy of finding a steady job, and the serene beauty that surrounded them. Sum this up in one nice sentence from a personal perspective:"
mrole = "system"
mcontent = "You are an expert in geology."
user_role = "user"
mcontent 复用了语义查询示例中的内容(“You are an expert in geology”),但在本例中它对回答的影响不大。由于用户输入高度个人化且叙事性强,系统提示作用有限。
如有必要,我们可以在调用函数前插入外部信息。例如,添加当天从家人处收到的短信:
text_message = 'I agree, we had a wonderful time there.'
uinput = text_message + uinput
text_message = "Hi, I agree, we had a wonderful time there."
现在调用函数:
# 调用函数
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role)
print(response)
我们看到,回复主要是情景性的,同时包含一些语义信息:
1970 年代搬到夏威夷对我的家人来说是一次改变人生的经历,他们在岛上的温柔微风中找到了快乐,在稳定的工作中感受到安全,并沉浸于新家那宁静的美景之中。
无状态且无记忆的验证
之前我们没有添加任何记忆保留功能,使对话成为无状态。让我们验证一下:
# API 消息
uinput = "What question did I just ask you?"
mrole = "system"
mcontent = "You already have this information"
user_role = "user"
当我们调用函数时,对话内容会被忘记:
# API 函数调用
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role
)
print(response)
输出结果确认该会话是无记忆的:
抱歉,我无法回忆之前的交互或问题。请您重复一下您的问题好吗?
API 调用是无状态的,因为 OpenAI API 不会在请求之间保留记忆。如果我们直接使用 ChatGPT,则该会话中的交流内容会被记忆。这一点对实现方式有重大影响,意味着我们必须自己构建记忆机制,赋予 GenAISys 有状态行为。
接下来让我们从第一层开始:短期记忆。
2. 短期记忆会话
本节目标是通过两步过程模拟短期记忆会话:
首先,我们启动一个从用户输入到生成响应的会话:
用户输入 => 生成式模型 API 调用 => 响应
为了完成这一步,我们运行会话直到获得响应:
uinput = "Hawai is on a geological volcano system. Explain:"
mrole = "system"
mcontent = "You are an expert in geology."
user_role = "user"
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role)
print(response)
响应内容被存储在 response 变量中:
“夏威夷属于一个被称为热点的火山系统,热点是地球地幔中的一个区域,地热以热羽流的形式自地球深处上升。正是这个热点促成了夏威夷群岛的形成。过程如下:…”
下一步是将之前的交互信息与后续问题一起输入到新的提示中:
- 说明当前对话会话为:
- 添加用户的初始输入:“Hawai is on a geological volcano system. Explain:”
- 添加之前调用得到的响应
- 添加用户的新输入:“用不超过20个词简短总结你之前的回答。”
这里的目的是压缩会话记录。我们并不总是需要压缩对话,但在较长的会话中,巨大的上下文窗口会迅速堆积。此技术有助于保持 token 数量较低,既节省成本,也提升性能。在本例中,我们只管理了一条响应,如果需要也可以将整个交互保存在内存中,但这个示例为后续扩展至更长对话奠定了良好习惯。
当提示语组装完成后:
- 调用 API 函数
- 展示响应
代码示例如下:
ninput = "Sum up your previous response in a short sentence in a maximum of 20 words."
uinput = (
"The current dialog session is: " +
uinput +
response +
ninput
)
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role
)
print("New response:", "\n\n", uinput, "\n", response)
输出为对话的简洁总结:
New response: Hawaii's islands form from volcanic activity over a stationary hotspot beneath the moving Pacific Plate.
此功能在此示例中非必需,但为我们日后遇到更长对话做好准备。
请注意:由于会话仍仅存在内存中,如果 notebook 断开连接,对话内容将丢失。尚未存储到磁盘或数据库。
3. 多次会话的长期记忆
本节中,我们模拟长期记忆,方法是延续先前会话的对话。不同之处在于,我们不仅记住单次会话的对话内容,而是复用过去会话的内容来延续对话。此时,“会话”一词的含义更广泛。传统的协助工具场景中,一个用户在一个独立的会话中与一个模型交互。而这里,我们融合多个会话,支持多个子会话。多个用户可在共享环境中与模型交互,有效地创建了一个具有分支记忆线程的全局会话。可以把模型想象成参加正在进行的 Zoom 或 Teams 会议的嘉宾。你可以让 AI 嘉宾参与讨论或保持安静——当它加入时,可能需要回顾之前内容。
为避免重复之前会话的初始步骤,我们复用了刚才运行的短期记忆会话内容。假设之前的会话已经结束,但我们想从中断处继续:
session01 = response
print(session01)
输出包含短期记忆会话的最终响应:
Hawaii's islands form from volcanic activity over a stationary hotspot beneath the moving Pacific Plate.
本节的流程基于之前的会话,类似于你在离开一段时间后与在线协助工具重新开始对话:
保存之前的会话 => 载入之前的会话 => 将其添加到新会话的场景中
首先测试 API 是否会自行记忆内容:
uinput = "Is it safe to go there on vacation"
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role
)
print(response)
输出显示它忘记了之前的对话:
I'm sorry, but I need more information to provide a helpful response. Could you specify the location you're considering for your vacation? …
API 忘记了之前的调用,因为无状态 API 不会保留过往对话。决定提示中包含哪些内容由我们自行掌控。我们有几个选择:
- 是否想记住全部内容,即使消耗大量 token?
- 是否想总结部分或全部之前的对话?
在真实的 GenAISys 中,当输入触发请求时,AI 控制器会决定对任务应用哪种最佳策略。
代码现在将之前会话的上下文和记忆与新请求关联:
ninput = "Let's continue our dialog."
uinput = ninput + session01 + "Would it be safe to go there on vacation?"
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role
)
print("Dialog:", uinput, "\n")
print("Response:", response)
回复显示系统现在记住了之前的会话,且拥有足够信息给出合理回答:
Response: Hawaii is generally considered a safe destination for vacation, despite its volcanic activity. The Hawaiian Islands are formed by a hotspot beneath the Pacific Plate, which creates volcanoes as the plate moves over it. While volcanic activity is a natural and ongoing process in Hawaii, it is closely monitored by the United States Geological Survey (USGS) and other agencies…
接下来,让我们构建跨不同主题的多次会话长期记忆模拟。
4. 多主题跨会话的长期记忆
本节展示如何将两个独立会话合并为一个。这不是标准的 ChatGPT 类平台所提供的功能。通常,当我们开启一个新话题时,协助工具只记住当前会话中的内容。但在企业环境中,我们可能需要更灵活的功能,尤其是多用户协作时。在这种情况下,AI 控制器可以配置成允许同一组的用户查看并合并由其他组员生成的会话。
假设我们想总结两个独立的对话——一个关于夏威夷的火山系统,另一个关于组织一次亚利桑那州的地质考察。我们首先保存之前的长期记忆会话:
session02 = uinput + response
print(session02)
然后我们可以从另一个地点亚利桑那开始一个独立的多用户子会话:
ninput = "I would like to organize a geological visit in Arizona."
uinput = ninput + "Where should I start?"
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role
)
# print("Dialog:", uinput, "\n")
我们现在期望得到关于亚利桑那的回应,忽略夏威夷内容:
Response: Organizing a geological visit in Arizona is a fantastic idea, as the state is rich in diverse geological features. Here's a step-by-step guide to help you plan your trip:…
回复令人满意。现在,让我们模拟多主题的长期记忆,将两个会话内容合并,并提示系统进行总结:
session02 = response
ninput = "Sum up this dialog in a short paragraph:"
uinput = ninput + session01 + session02
response = openai_api.make_openai_api_call(
uinput, mrole, mcontent, user_role
)
# print("Dialog:", uinput, "\n") # 可选
print("Response:", response)
系统输出显示长期记忆功能有效。我们看到第一部分关于夏威夷:
Response: The dialog begins by explaining the formation of Hawaii's volcanic islands as the Pacific Plate moves over a stationary hotspot, leading to active volcanoes like Kilauea….
接着回复转向亚利桑那部分:
It then transitions to planning a geological visit to Arizona, emphasizing the state's diverse geological features. The guide recommends researching key sites such as the Grand Canyon…
至此,我们已经涵盖了 GenAISys 的核心记忆模式——从无状态与短期记忆,到多用户、多主题的长期记忆。
现在,让我们总结本章内容,迈向下一层级!
总结
一个面向业务的 GenAISys 提供与 ChatGPT 类平台相当的功能。它将生成式 AI 模型、智能代理功能、检索增强生成(RAG)、记忆保留以及一系列机器学习和非 AI 功能整合起来,由 AI 控制器协调管理。与传统的流水线不同,控制器不遵循固定步骤序列,而是根据上下文动态编排任务。
GenAISys 通常运行在如 GPT-4o 这样的模型上,或其他最适合你用例的模型。但正如我们所见,仅有 API 访问权限远远不够。上下文感知和记忆保留是必不可少的。虽然类似 ChatGPT 的工具默认提供这些功能,但在自定义系统中,我们必须自行构建它们。
我们探讨了四种记忆类型:无记忆、短期记忆、长期记忆和跨主题记忆。同时区分了语义记忆(事实性知识)与情景记忆(个人带时间戳的信息)。上下文感知高度依赖记忆,但上下文窗口有限制。即使扩大窗口大小,模型在复杂任务中的细节把握仍可能不足。这时,先进的 RAG 技术发挥作用——将内容拆分为更小块,进行嵌入,并存储在如 Pinecone 这样的向量库中,扩展系统“记忆”和推理的能力。
我们还看到,无论 GenAISys 多么先进,没有人类专业知识是无法正常运行的。从设计、部署、维护到迭代,人类在系统生命周期中始终至关重要。随后,我们概述了基于资源和目标的三种现实实施模型:利用现有 AI 平台的混合系统、针对特定业务需求的小规模系统,以及构建 ChatGPT 级性能的全规模系统。
最后,我们动手用 GPT-4o 和 Python 构建了一系列记忆模拟模块。这些示例为下一步奠定基础:AI 控制器将负责管理 GenAISys 的记忆、上下文和编排。现在,我们已准备好构建 GenAISys 的 AI 控制器!