前言
上篇文章我们大致演示了一下ai_agent
的食用方法。这里我们做一下核心模块runtime
的设计和实现。
一个agent也好,workflow也好,他们单个实现起来并不复杂,困难的是如何将他们有机的组合起来,能够按照一定的逻辑流转起来。并且能够层层嵌套,能力无限。
理解起来比较抽象,我们先看几个重点方向,和现实中的例子:
1. 简单的workflow场景
我们可以使用一个简单的workflow来处理一些场景,比如奶茶店的智能推荐,那么它的一个流程大致如下:
graph LR
User --用户画像+query+上下文+其他--> pormpt
pormpt --> llm
llm --> answer
answer --> 奶茶卡片
- 这个流程就能看到,我们将llm,memory,prompt都当做一个节点,按照图中的顺序执行最终会得到一个推荐结果。
- 可以说这是一个workflow的基础功能。
2. smartflow场景
对于有些问题,我们是不能够将完全预测出执行流程,比如Least-to-Most
和smartflow
的场景,这些节点往往是在执行过程中慢慢生成出来的。
我们还是举个例子, 假设有这样一个agent:评阅大师,它的目标是评阅优化输入文案。那么它的工作流程可能是这样的。
graph TD
user -->|文案| pf[评分llm]
pf -->|评分>80| 输出
pf -->|评分<80 \n 待优化方向| yhp[追加上下文]
yhp --> yh[优化llm]
yh --> pf
- 工作方式一目了然,可以理解为一个评分llm,一个优化llm。对于一个文案,评分llm不断给出需要优化的点,并追加到上下文中,优化llm则根据要求不断优化,直到评分>80。
- 我们的流程设计肯定不能是DAG,因为出现环了。
3. multi-agent场景
multi-agent
常用来解决复杂问题,对流程设计的要求也更高。比较典型的场景是狼人杀。它的流程可能长这样。
graph TD
xdyt[入夜] --> zcr
zcr[主持人] -->|第一轮| lrfy[狼人开刀]
lrfy --> zcr
zcr[主持人agent] -->|第二轮| nwfy[女巫药]
nwfy --> zcr
zcr[主持人agent] -->|第三轮| yyjfy[预言家查人]
yyjfy --> zcr
zcr[主持人agent] -->|第四轮| syrfy[所有人发言]
syrfy --> gp[归票]
gp --> zcr
zcr -->|某个阵容胜利| 结束
- 上图画的游戏规则并不严谨,但大体能感受到一个
multi-agent
的工作方式。这里面的每个角色都可以理解为一个agent,主持人可以是一个固定的脚本。 - 这个场景要求每个agent都有自己的prompt,有独立的memory等,将一个agent放大,那么这个agent也应该是一个worlflow,由无数的节点拼接而来。也就要求我们流程设计能够层层嵌套,能力无限。
4. NL2code场景
保持开放是一个非常重要的能力,一来是能够让程序员直接写脚本。二是大模型有时候也会自己写脚本,也就是NL2Code
场景。
我之前参与过一个专项,其中的一个能力是用户自由操作文档,比如文档归类,概要摘取等,结果通过text2sql
存到数据库中。
这种场景下,仅仅提供有限功能的节点是不足够的。必须具备无限扩展的能力。
5. 开放域
在开放域中,agent将具备更自由的意志。举几个典型的场景:聊天室
游戏NPC
虚拟宠物
在这种应用中,agent更应该是一个完备的ai,具备更长的生命周期,更强的主观能动性。
agent从运行到结束,要像人一样,生下来就有意识,直到死亡。
简单的做法是先构建一个single agent,并让它至少有个memory+tool+设定这几个模块,最好是采用多模态大模型做基座模型。然后不断循环调用这个agent,从而达到和我一样的牛马状态。当然成本肯定极高。
另一种方法是做双循环,还是这个agent,一个循环是外界有输入后再调用agent,另一个循环是定时唤起agent的主观意识。从而节能减排。
不管是哪种方式,我更倾向于从agent外部解决。而非agent本身。
总结
现实中应用肯定不局限于这几种情况,但通过一定的流程编辑基本都可以解决,只是复杂性会比较高。
runtime 设计
一个agent的runtime大概可以看成是由这几部分构成:调度+节点服务+执行计划+上下文。
节点 service
一个service可以理解成一个原子能力,比如llm,比如tool,也可能是function。
我们这里做一个抽象:
pub trait Service: Send + Sync {
async fn call(&self, flow: Flow) -> anyhow::Result<Output>;
}
执行计划
这个也很好理解,我们需要按照这个计划不断执行service。
pub trait Plan: Send + Sync {
fn next(&self, ctx: Arc<Context>, node_id: &str) -> NextNodeResult;
fn set(&self, nodes: Vec<PlanNode>);
fn update(
&self,
node_code: &str,
update: Box<dyn FnOnce(Option<&mut PlanNode>) -> anyhow::Result<()>>,
) -> anyhow::Result<()>;
}
上下文Context
这个所谓的上下文Context
可以理解为 执行一个任务的具体消息,比如任务编码,堆栈信息,状态等。
- 这里有一个子上下文的概念,也就是说在一个
service
里面可以执行一个子计划,也就是能力无限的实现。并且父子上下文会共享堆栈。todo:也会共享状态。 - 这里没有做共享设计,每个
Context
会记录自己归属的运行时。也就是每个运行时都是独立的,为了多租户设计的。
pub struct Context {
pub parent_code: Option<String>,
//任务流名称
pub code: String,
//状态
pub status: AtomicU8, //0:init 1:running, 2:success, 3:error
//堆栈信息
pub stack: Arc<Mutex<ContextStack>>,
//执行计划
pub plan: Arc<dyn Plan>,
//全局扩展字段
pub extend: Mutex<HashMap<String, Box<dyn Any + Send + Sync + 'static>>>,
//结束时回调
pub over_callback: Option<Mutex<Vec<Box<dyn FnOnce(Arc<Context>) + Send + Sync + 'static>>>>,
pub(crate) runtime: Arc<Runtime>,
}
调度
调度也很简单,就是按照plan
不断执行service
- 这里做了中间层设计,也就是每个service都是一个‘洋葱’,一层层进,一层层出。
- 每个service都是异步执行的,就是说如果你的
plan
存在并行结构,那么两个并行的分支会一起执行。
fn exec_next_node(ctx: Arc<Context>, node_code: &str) {
let nodes = ctx.plan.next(ctx.clone(), node_code);
...
for i in nodes {
match ctx.runtime.nodes.get(i.node_type_id.as_str()) {
...
};
//将service和middle封装成一个flow执行
let flow = Flow::new(i, ctx.clone(), middle);
tokio::spawn(async move {
let ctx = flow.ctx.clone();
let result = tokio::spawn(async move {
... //处理一些堆栈信息
//执行flow
if let Err(e) = flow.call().await {
//错误处理
} else {
//执行成功则继续执行下一个
Runtime::exec_next_node(ctx, code.as_str());
}
})
.await;
... //检查错误,和故障恢复
});
}
}
Service layer
为了方便service的实现,我们将service再包装一层。如下:
pub trait ServiceLayer: Sync + Send {
//service的配置类型
type Config;
//输出结果
type Output;
//执行过程
async fn call(
&self,
code: String,
ctx: Arc<Context>,
cfg: Self::Config,
) -> anyhow::Result<Self::Output>;
}
再为这个包装层加一个自动json解析的实现。这样入参和出参可以直接绑定到struct。
impl<T, In, Out> Service for LayerJson<T, In, Out>
where
T: ServiceLayer<Config = In, Output = Out>,
In: for<'a> Deserialize<'a> + Send + Sync,
Out: Serialize + Send + Sync,
{
async fn call(&self, flow: Flow) -> anyhow::Result<Output> {
... //类型解析和参数的初步处理
//解析入参
let cfg = match serde_json::from_str::<In>(node_config.as_str()) {
... //错误处理
};
//调用执行过程
let output = self.handle.call(code, ctx, cfg).await ? ;
//解析出参
let raw = match serde_json::to_value(&output) {
Ok(o) => o,
Err(e) => return anyhow::anyhow!("code[{}],output json error:{}", node_info, e).err(),
};
Output::new(raw).raw_to_ctx().ok()
}
}
测试
尾语
整体runtime模块还是比较简单,比较薄的
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。