Ax - 构建由 LLM 驱动的代理 (Typescript)
快速构建智能代理——灵感来自“智能工作流”和 Stanford DSPy 论文的力量。与多个 LLM 和 VectorDB 无缝整合,构建 RAG 管道或协作代理,可以解决复杂问题。高级功能包括流式验证、多模态 DSPy 等。
我们对代理的关注
我们从“llmclient”改名为“ax”,以突出我们在智能工作流方面的重点。我们同意像“Andrew Ng”等许多专家的观点,智能工作流是解锁大语言模型真正潜力的关键,并且可以通过上下文学习实现很多目标。此外,我们还是 Stanford DSPy 论文的忠实粉丝,这个库是将所有这些结合在一起,构建强大框架的结果,供您使用。
为什么选择 Ax?
- 支持各种 LLM 和 Vector DB
- 从简单签名自动生成提示
- 构建可以调用其他代理的代理
- 将任何格式的文档转换为文本
- RAG、智能分块、嵌入、查询
- 与 Vercel AI SDK 一起使用
- 在流式传输期间进行输出验证
- 支持多模态 DSPy
- 使用优化器进行自动提示调整
- OpenTelemetry 跟踪/可观察性
- 生产就绪的 Typescript 代码
- 轻量级、零依赖
什么是提示签名?
高效的类型安全提示从简单的签名自动生成。提示签名由"task description" inputField:type "field description" -> "outputField:type
组成。提示签名的理念基于“Demonstrate-Search-Predict”论文中的工作。
您可以有多个输入和输出字段,并且每个字段可以是类型string
、number
、boolean
、JSON
或这些中的任何一个数组,例如string[]
。当未定义类型时,默认为string
。当使用 JSON
类型时,鼓励底层 AI 生成正确的 JSON。
支持的 LLM
提供商 | 最佳模型 | 测试情况 |
---|---|---|
OpenAI | GPT: 所有 4 个模型 | 🟢 100% |
Azure OpenAI | GPT: 所有 4 个模型 | 🟢 100% |
Together | 多个 OSS 模型 | 🟢 100% |
Cohere | CommandR, Command | 🟢 100% |
Anthropic | Claude 2, Claude 3 | 🟢 100% |
Mistral | 7B, 8x7B, S, L | 🟢 100% |
Groq | Lama2-70B, Mixtral-8x7b | 🟢 100% |
DeepSeek | Chat 和 Code | 🟢 100% |
Ollama | 所有模型 | 🟢 100% |
Google Gemini | Gemini: Flash, Pro | 🟢 100% |
Hugging Face | OSS 模型 | 🟡 50% |
Reka | Core, Flash, Edge | 🟡 50% |
安装
npm install @ax-llm/ax
# 或者
yarn add @ax-llm/ax
示例:使用思维链总结文本
import { AxAI, AxChainOfThought } from '@ax-llm/ax';
const textToSummarize = `
The technological singularity—or simply the singularity[1]—is a hypothetical future point in time at which technological growth becomes uncontrollable and irreversible, resulting in unforeseeable changes to human civilization.[2][3] ...`;
const ai = new AxAI({
name: 'openai',
apiKey: process.env.OPENAI_APIKEY as string
});
const gen = new AxChainOfThought(
ai,
`textToSummarize -> shortSummary "summarize in 5 to 10 words"`
);
const res = await gen.forward({ textToSummarize });
console.log('>', res);
示例:构建代理
使用代理提示(框架)来构建与其他代理协同工作的代理以完成任务。通过提示签名,代理很容易制作。试试看代理示例。
# npm run tsx ./src/examples/agent.ts
const researcher = new AxAgent(ai, {
name: 'researcher',
description: '研究代理',
signature: `physicsQuestion "物理问题" -> answer "用要点回复"`
});
const summarizer = new AxAgent(ai, {
name: 'summarizer',
description: '摘要代理',
signature: `text "要汇总的文本" -> shortSummary "5 到 10 个字总结"`
});
const agent = new AxAgent(ai, {
name: 'agent',
description: '研究复杂主题的代理',
signature: `question -> answer`,
agents: [researcher, summarizer]
});
agent.forward({ questions: "宇宙中有多少个原子" })
支持的 Vector DB
矢量数据库是构建 LLM 工作流程的关键。我们对流行的矢量数据库和我们自己的内存矢量数据库进行了简洁的抽象。
提供商 | 测试情况 |
---|---|
内存中的 | 🟢 100% |
Weaviate | 🟢 100% |
Cloudflare | 🟡 50% |
Pinecone | 🟡 50% |
// 使用 LLM 从文本中创建嵌入
const ret = await this.ai.embed({ texts: 'hello world' });
// 创建内存中的矢量数据库
const db = new axDB('memory');
// 插入到矢量数据库中
await this.db.upsert({
id: 'abc',
table: 'products',
values: ret.embeddings[0]
});
// 使用嵌入查询相似条目
const matches = await this.db.query({
table: 'products',
values: embeddings[0]
});
或者,您可以使用 AxDBManager
来处理智能分块、嵌入和查询,几乎让一切变得容易。
const manager = new AxDBManager({ ai, db });
await manager.insert(text);
const matches = await manager.query(
'John von Neumann关于人类智能与奇点的观点。'
);
console.log(matches);
RAG 文档
使用 PDF、DOCX、PPT、XLS 等文档结合 LLM 是一件很痛苦的事。我们借助 Apache Tika(一种开源的文档处理引擎),让这一过程变得简单。
启动 Apache Tika
docker run -p 9998:9998 apache/tika
将文档转换为文本,并使用 AxDBManager
进行嵌入以便检索,该管理器还支持重排和查询重写。提供了两个默认实现 AxDefaultResultReranker
和 AxDefaultQueryRewriter
。
const tika = new AxApacheTika();
const text = await tika.convert('/path/to/document.pdf');
const manager = new AxDBManager({ ai, db });
await manager.insert(text);
const matches = await manager.query('查找一些文本');
console.log(matches);
多模态 DSPy
当使用 GPT-4o
和 Gemini
等支持多模态提示的模型时,我们支持使用图像字段,这与整个 DSPy 管道兼容。
const image = fs
.readFileSync('./src/examples/assets/kitten.jpeg')
.toString('base64');
const gen = new AxChainOfThought(ai, `question, animalImage:image -> answer`);
const res = await gen.forward({
question: '这只动物属于哪个科?',
animalImage: { mimeType: 'image/jpeg', data: image }
});
流式传输
我们支持在流式传输期间解析输出字段和执行功能。这允许在不等待整个输出的情况下进行快速失败和错误修正,节省代币和成本并减少延迟。断言是一种强大的方式,能够确保输出符合您的要求;它们也适用于流式传输。
// 设置提示程序
const gen = new AxChainOfThought(
ai,
`startNumber:number -> next10Numbers:number[]`
);
// 添加断言以确保输出字段中不包含数字 5
gen.addAssert(({ next10Numbers }: Readonly<{ next10Numbers: number[] }>) => {
return next10Numbers ? !next10Numbers.includes(5) : undefined;
}, '不允许数字 5');
// 在启用流式传输的情况下运行程序
const res = await gen.forward({ startNumber: 1 }, { stream: true });
上述示例允许您验证流式传输的整个输出字段值。此验证在流式传输和非流式传输时都适用,并在字段值可用时触发。对于真正的流式传输验证,请查看下面的示例。这将大大提高性能并在生产中节省代币。
// 添加断言以确保所有行都以数字和点开头。
gen.addStreamingAssert(
'answerInPoints',
(value: string) => {
const re = /^\d+\./;
// 按行拆分值,修剪每行,
// 过滤掉空行并检查所有行是否都匹配正则表达式
return value
.split('\n')
.map((x) => x.trim())
.filter((x) => x.length > 0)
.every((x) => re.test(x));
},
'行必须以数字和点 开头。例如:1. 这是一个行。'
);
// 在启用流式传输的情况下运行程序
const res = await gen.forward(
{
question: '提供一份加速 LLM 推理优化措施的清单。'
},
{ stream: true, debug: true }
);
快速 LLM 路由器
一种特殊的路由器,仅使用嵌入而不使用 LLM 调用来智能地路由用户请求。
使用此路由器高效地将用户查询路由到特定的路由,这些路由设计用于处理特定的问题或任务。每条路由都针对特定的领域或服务区域。与其使用缓慢或昂贵的 LLM 来决定如何处理用户输入,不如使用我们的快速“语义路由器”,它使用廉价且快速的嵌入查询。
# npm run tsx ./src/examples/routing.ts
const customerSupport = new AxRoute('customerSupport', [
'我如何退货?',
'我的订单在哪里?',
'你能帮我退款吗?',
'我需要更新我的送货地址',
'我的产品到达时损坏了,我该怎么办?'
]);
const technicalSupport = new AxRoute('technicalSupport', [
'我如何安装您的软件?',
'我登录时遇到问题',
'你能帮我配置设置吗?',
'我的应用程序不断崩溃',
'如何更新到最新版本?'
]);
const ai = new AxAI({ name: 'openai', apiKey: process.env.OPENAI_APIKEY as string });
const router = new AxRouter(ai);
await router.setRoutes(
[customerSupport, technicalSupport],
{ filename: 'router.json' }
);
const tag = await router.forward('我需要帮忙我的订单');
if (tag === "customerSupport") {
...
}
if (tag === "technicalSupport") {
...
}
Vercel AI SDK 集成
安装 ax 提供程序包
npm i @ax-llm/ax-ai-sdk-provider
然后在 AI SDK 中使用它,您可以使用 AI 提供程序或代理提供程序
const ai = new AxAI({
name: 'openai',
apiKey: process.env['OPENAI_APIKEY'] ?? "",
});
// 使用提供程序创建一个模型
const model = new AxAIProvider(ai);
export const foodAgent = new AxAgent(ai, {
name: 'food-search',
description:
'使用该代理根据客户需求查找餐馆',
signature,
functions
})
// 获取 vercel ai sdk 状态
const aiState = getMutableAIState()
// 创建一个针对特定任务的代理
const foodAgent = new AxAgentProvider({
agent: foodAgent,
updateState: (state) => {
aiState.done({ ...aiState.get(), state })
},
generate: async ({ restaurant, priceRange }) => {
return (
<BotCard>
<h1>{restaurant as string} {priceRange as string}</h1>
</BotCard>
)
}
})
// 使用 AI SDK 中构建聊天 UI 的关键部分 streamUI
const result = await streamUI({
model,
initial: <SpinnerMessage />,
messages: [
// ...
],
text: ({ content, done, delta }) => {
// ...
},
tools: {
// @ts-ignore
'find-food': foodAgent,
}
})
支持 OpenTelemetry
追踪和观测您的 LLM 工作流对于构建生产工作流至关重要。OpenTelemetry 是行业标准,我们支持新的 gen_ai
属性命名空间。
import { trace } from '@opentelemetry/api';
import {
BasicTracerProvider,
ConsoleSpanExporter,
SimpleSpanProcessor
} from '@opentelemetry/sdk-trace-base';
const provider = new BasicTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
trace.setGlobalTracerProvider(provider);
const tracer = trace.getTracer('test');
const ai = new AxAI({
name: 'ollama',
config: { model: 'nous-hermes2' },
options: { tracer }
});
const gen = new AxChainOfThought(
ai,
`text -> shortSummary "用 5 到 10 个字总结"`
);
const res = await gen.forward({ text });
{
"traceId": "ddc7405e9848c8c884e53b823e120845",
"name": "聊天请求",
"id": "d376daad21da7a3c",
"kind": "SERVER",
"timestamp": 1716622997025000,
"duration": 14190456.542,
"attributes": {
"gen_ai.system": "Ollama",
"gen_ai.request.model": "nous-hermes2",
"gen_ai.request.max_tokens": 500,
"gen_ai.request.temperature": 0.1,
"gen_ai.request.top_p": 0.9,
"gen_ai.request.frequency_penalty": 0.5,
"gen_ai.request.llm_is_streaming": false,
"http.request.method": "POST",
"url.full": "http://localhost:11434/v1/chat/completions",
"gen_ai.usage.completion_tokens": 160,
"gen_ai.usage.prompt_tokens": 290
}
}
调整提示 (程序)
您可以使用更大的模型来调整提示,以帮助其更高效地运行并获得更好的结果。这是通过使用像 AxBootstrapFewShot
的优化器以及流行的 HotPotQA
数据集中的示例来完成的。优化器生成演示 demo
,当与提示一起使用时,有助于提高其效率。
// 从 huggingface 下载 HotPotQA 数据集
const hf = new AxHFDataLoader({
dataset: 'hotpot_qa',
split: 'train'
});
const examples = await hf.getData<{ question: string; answer: string }>({
count: 100,
fields: ['question', 'answer']
});
const ai = new AxAI({
name: 'openai',
apiKey: process.env.OPENAI_APIKEY as string
});
// 设置要调整的程序
const program = new AxChainOfThought<{ question: string }, { answer: string }>(
ai,
`question -> answer "用简短的 2 或 3 个词回答"`
);
// 设置一个 Bootstrap Few Shot 优化器来调整上述程序
const optimize = new AxBootstrapFewShot<
{ question: string },
{ answer: string }
>({
program,
examples
});
// 设置一个评价指标 em, f1 分数是衡量检索性能的流行方式。
const metricFn: AxMetricFn = ({ prediction, example }) =>
emScore(prediction.answer as string, example.answer as string);
// 运行优化器并记得保存结果以供以后使用
const result = await optimize.compile(metricFn);
<img width="853" alt="tune-prompt" src="https://github.com/dosco/llm-client/assets/832235/f924baa7-8922-424c-9c2c-f8b2018d8
为了使用上面 ChainOfThought
程序生成的演示
const ai = new AxAI({
name: 'openai',
apiKey: process.env.OPENAI_APIKEY as string
});
// 设置程序使用调整后的数据
const program = new AxChainOfThought<{ question: string }, { answer: string }>(
ai,
`question -> answer "in short 2 or 3 words"`
);
// 加载调优数据
program.loadDemos('demos.json');
const res = await program.forward({
question: '大卫·格雷戈里继承了什么城堡?'
});
console.log(res);
内置函数
功能 | 名称 | 描述 |
---|---|---|
JS 解释器 | AxJSInterpreter | 在沙箱环境中执行 JS 代码 |
Docker 沙箱 | AxDockerSession | 在 docker 环境中执行命令 |
嵌入适配器 | AxEmbeddingAdapter | 获取并传递嵌入到您的函数 |
查看所有示例
使用 tsx
命令运行示例。这使得节点可以运行 typescript 代码。它还支持使用 .env
文件传递 AI API 密钥,而不是将它们放在命令行中。
OPENAI_APIKEY=openai_key npm run tsx ./src/examples/marketing.ts
示例 | 描述 |
---|---|
customer-support.ts | 提取客户通讯中的有价值信息 |
food-search.ts | 使用多个 API 来查找餐饮选择 |
marketing.ts | 生成简短有效的营销短信 |
vectordb.ts | 块化、嵌入和搜索文本 |
fibonacci.ts | 使用 JS 代码解释器计算斐波那契数 |
summarize.ts | 生成大块文本的简短摘要 |
chain-of-thought.ts | 使用连锁思维提示回答问题 |
rag.ts | 使用多跳检索回答问题 |
rag-docs.ts | 将 PDF 转换为文本并嵌入进行 RAG 搜索 |
react.ts | 使用函数调用和推理回答问题 |
agent.ts | 代理框架,代理可以使用其他代理、工具等 |
qna-tune.ts | 使用优化器提高提示效率 |
qna-use-tuned.ts | 使用优化调整后的提示 |
streaming1.ts | 流式传输时输出字段验证 |
streaming2.ts | 流式传输时逐个输出字段验证 |
smart-hone.ts | 代理在智能家居中寻找小狗 |
multi-modal.ts | 使用图像输入以及其他文本输入 |
balancer.ts | 根据成本等在各个 LLM 之间平衡 |
docker.ts | 使用 docker 沙箱通过描述查找文件 |
我们的目标
大型语言模型(LLMs)正在变得非常强大,并且已经达到了可以作为您整个产品后端的地步。然而,从使用正确的提示、模型、流式传输、函数调用、错误修正等方面,仍然有很多复杂性需要管理。我们旨在将所有这些复杂性打包成一个维护良好、易于使用的库,可以与所有最先进的 LLM 一起工作。此外,我们正在使用最新的研究将新的功能(如 DSPy)添加到库中。
如何使用这个库?
1. 选择一个 AI 来工作
// 选择一个 LLM
const ai = new AxOpenAI({ apiKey: process.env.OPENAI_APIKEY } as AxOpenAIArgs);
2. 根据您的用例创建一个提示签名
// 签名定义您的提示程序的输入和输出
const cot = new ChainOfThought(ai, `question:string -> answer:string`, { mem });
3. 执行这个新的提示程序
// 传入上面签名中定义的输入字段
const res = await cot.forward({ question: '我们在模拟中吗?' });
4. 或者如果您只想直接使用 LLM
const res = await ai.chat([
{ role: "system", content: "帮助客户回答他的问题" }
{ role: "user", content: "我在找一台有96GB RAM的Macbook Pro M2?" }
]);
如何使用函数调用
1. 定义函数
// 定义一个或多个函数和一个函数处理器
const functions = [
{
name: 'getCurrentWeather',
description: '获取某个地点的当前天气',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: '要获取天气的地点'
},
units: {
type: 'string',
enum: ['imperial', 'metric'],
default: 'imperial',
description: '使用的单位'
}
},
required: ['location']
},
func: async (args: Readonly<{ location: string; units: string }>) => {
return `当前 ${args.location} 的天气是 72 度`;
}
}
];
2. 将函数传递给提示
const cot = new AxReAct(ai, `question:string -> answer:string`, { functions });
启用调试日志
const ai = new AxOpenAI({ apiKey: process.env.OPENAI_APIKEY } as AxOpenAIArgs);
ai.setOptions({ debug: true });
联系我们
如有疑问或想加入 Discord,请与我们联系 twitter/dosco
常见问题
1. LLM 找不到要使用的正确函数
改进函数命名和描述。非常明确地说明函数的功能。此外,确保函数参数有良好的描述。描述可以很简短,但需要准确。
2. 如何更改我正在使用的 LLM 的配置?
可以在创建新的 LLM 对象时传递一个配置对象作为第二个参数。
const apiKey = process.env.OPENAI_APIKEY;
const conf = AxOpenAIBestConfig();
const ai = new AxOpenAI({ apiKey, conf } as AxOpenAIArgs);
3. 我的提示太长了 / 我可以更改最大令牌数吗?
const conf = axOpenAIDefaultConfig(); // 或 OpenAIBestOptions()
conf.maxTokens = 2000;
4. 如何更改模型?(例如,我想使用 GPT4)
const conf = axOpenAIDefaultConfig(); // 或 OpenAIBestOptions()
conf.model = OpenAIModel.GPT4Turbo;
Monorepo 提示与技巧
重要的是要记住,我们应该只从根目录运行 npm install
。这可以防止创建嵌套的 package-lock.json
文件并避免非去重的 node_modules
。
在包中添加新依赖项应使用例如 npm install lodash --workspace=ax
(或者只需修改相应的 package.json
并从根目录运行 npm install
)。