生成式人工智能/大语言模型

基于大语言模型的Agent智能体在金融行业中的应用

大型语言模型(Large Language Models, LLM)已经取得了显著的进展,并展示了类人智能的潜力,它可以完成问题的拆解、理解与回答,代码生成,文本生成,以及数理计算等任务。近年来,LLM在包括金融行业的各领域中,得到了广泛的应用,并在相关业务场景中表现出良好的性能。如客户服务、营销文案生成、内部业务辅助等。

在LLM的应用方面,Agent是一个令人兴奋的领域。它是一种能够模拟人类智能行为,去执行特定任务或服务的智能系统。基于LLM的Agent,一方面可以利用LLM强大的理解能力与生成能力,另一方面可以结合Agent的规划和工具调用能力,来完成许多复杂的任务。

LLM与Agent简介

相较于早期的NLP模型,LLM由于使用了更大规模的训练数据集和模型参数,使LLM在对文本的处理能力,和对场景及下游任务的泛化能力上,都展现出了显著的提升。特别是在泛化能力上,LLM已经不再局限于文本理解、摘要等简单下游任务,它还可以执行代码生成,甚至用思维链方式来分析问题。那么,很自然的一个想法就是,能否让LLM来像人类一样,结合已有的知识和记忆,通过复杂的逻辑分析并合理运用工具,有规划的去解决现实世界中的问题。同时,在解决问题的过程中,LLM还要通过交互,对自己的行为进行评估和优化,以改善性能。

上述这种LLM的使用方式,实际就是基于LLM的Agent智能体。

Agent的构成

如上文所述,Agent的核心是LLM,它提供了处理和分析问题的能力。此外,LLM还是调用外部工具的入口,通过LLM可以生成外部工具的调用选择和调用参数,交由执行器(Executor)执行。除LLM之外,Agent通常还包括记忆(Memory),规划(Planning),工具使用(Tool use)等模块。

规划(Planning

子目标和分解:代理将大型任务分解为较小的、可管理的子目标,从而能够有效地处理复杂任务。

反思和改进:agent可以对过去的行为进行自我批评和反思,从错误中吸取教训,并为未来的步骤进行改进,从而提高最终结果的质量。

记忆(Memory

短期记忆:所有的上下文学习(参见提示工程)都是利用模型的短期记忆来学习的。

长期记忆:这为代理提供了在较长时间内保留和回忆(无限)信息的能力,通常是通过利用外部向量存储和快速检索。

工具使用(Tool use

代理学习调用外部API来获取模型权重中缺少的额外信息(通常在预训练后很难更改),包括当前信息、代码执行能力、对专有信息源的访问等等。

综上,Agent的基本构成如下图所示。

图片来源:https://lilianweng.github.io/posts/2023-06-23-agent/

下面我们将通过银行类和资本市场类的三个相关案例,阐述LLM Agent智能体在金融行业中的应用。

基于LLM 的Agent在撮合交易中的应用

银行的资金交易业务是金融市场中的重要组成部分,主要涉及资金的流动和短期融资。常见的资金交易方式包括拆借和回购。这些业务不仅帮助银行管理流动性,还为市场参与者提供了融资和投资的机会。撮合交易Agent是一个给银行业务部门使用的公对公的聊天机器人,它可以和其他金融机构交流、讲价提供的金融业务。撮合交易Agent会调用策略查询工具来查询金融产品的初始报价,调用询价工具来和客户讲价、查询金融产品可提供的价格,还有确认交易工具和取消交易工具,一共涉及4个工具的调用。

该Agent应用使用Qwen-Agent框架、Qwen2-72B-Instruct模型、ReAct算法和自定义的策略查询、询价、确认交易、取消交易工具构建。下面,我们将通过示例,展示如何通过Agent的规划和工具调用能力处理撮合交易这样相对复杂的场景。

  1. 使用TensorRT-LLM提供的OpenAI API接口,在本地部署Qwen2-72B-Instruct模型的serving服务,TensorRT-LLM和其部署过程会在“TensorRT-LLM对大语言模型的加速”部分详细介绍。
  2. 以下对话模拟了另一家金融机构的业务人员来咨询金融产品的场景,依次询问价格、讲价和确认交易。可以看到,撮合交易Agent按照预期正确地依次调用了策略查询工具、询价工具,并且正确地生成了工具调用参数,与客户沟通,最后调用确认交易工具,自动化地完成了整个交易流程。

基于LLM 的Agent在量化投资中的应用

量化投资是金融行业中的又一关键场景。量化投资是一种投资策略,它基于某些特定的风险因子来选择投资组合,以期获得超越市场平均水平的回报。在量化领域,投研人员会根据量价数据(交易量、交易价)或其他数据(财报、研报等,主要是文本类数据)进行因子挖掘。这里所说的“因子”是指那些能够解释资产收益差异的变量或特征。因子挖掘是一个系统性的过程,它涉及到从大量数据中识别出能够预测未来收益的因子。

因子挖掘涉及因子挖掘与开发、单因子回测、单因子优化等过程。如果考虑数据处理,还应包括数据收集、数据预处理的过程。在最终的业务中,通常是多个因子进行组合,所以还会有多因子组合、多因子回测、多因子组合优化等过程。整个过程需要金融工程行业的量化研究人员,不断地开发、迭代和优化。其中,有些过程虽然可以借助算法或AI做到局部自动化,但整个流程依然相对割裂,需要人为控制和干预。

因子挖掘Agent是一个使用三个LLM组成的循环来自动挖掘因子的应用,第一个Factor-LLM负责构建因子表达式;第二个Code-LLM负责将因子表达式转换为可执行的代码并计算结果;第三个Eval-LLM,负责对因子计算结果进行回测检验并生成优化建议,将建议返回给Factor-LLM继续优化(来源华泰金工报告《GPT因子工厂:多智能体与因子挖掘》)。该Agent应用使用 Langchain框架、Llama3-70B-Instruct模型、自定义的3个LLM循环计算的执行逻辑、接入一个辅助代码生成的工具构建。下面,我们将通过示例,展示如何借助基于LLM的Agent智能体,来自动化地完成单因子开发回测全流程,其中包括:挖掘与开发、单因子回测、单因子优化。

1.第一个Factor-LLM负责构建因子表达式,它将对数据字段和算子进行组合和复合,输出因子名称、因子计算方法和因子含义。

2. 第二个Code-LLM接入了一个辅助代码生成的工具,以保证代码生成的稳定性。将上一步生成的因子表达式转换为可执行的代码,并计算结果。在此过程中如果发生错误,Code-LLM能够迭代地对代码进行修改。

3. 第三个 Eval-LLM负责对上一步的因子计算结果进行回测检验,并生成优化建议,将建议返回给Factor-LLM继续优化(来源华泰金工报告《GPT因子工厂:多智能体与因子挖掘》)。如此循环直至达到指定优化次数。

上述展示步骤是量价因子的构建流程,通过在第一步Factor-LLM中改变数据字段和算子的设计来保证因子表达式合乎逻辑,我们可以把大语言模型因子挖掘扩展至基本面与高频因子挖掘场景。目前,高频因子挖掘产出效果最佳,因子周度 IC 均值为 0.020,周度 RankIC 均值为 0.031,利用产出的高频因子构建中证1000指增策略(回测区间 20161230- 20240920),年化超额收益率为31.32%,信息比率为4.20。(来源华泰金工报告《GPT因子工厂2.0:基本面与高频因子挖掘》)

基于 LLM 的 Agent 在信用评估中的应用

银行在市场经济中发挥着至关重要的作用,决定了谁可以获得融资以及融资的条件,并能影响投资决策的成败。为了使市场和社会正常运作,个人和公司需要获得信贷。信用评估算法是银行用来判断是否应批准贷款的方法,它通过估算违约的概率来进行决策。通常,此类工作需要有经验的金融分析师来完成。但个体的经验积累,以及时间和成本等原因,是制约效率提升的关键因素。

如前所述,LLM的能力已经超越了文本处理的范畴,并可以用于处理数值类数据。该Agent应用使用 Qwen-Agent框架、Qwen2-72B-Instruct模型、ReAct规划算法、接入一个代码执行器工具构建。下面,我们将通过示例,展示如何运用基于LLM的Agent规划和工具调用,在一份模拟的客户信息样本上,通过处理和分析,预测客户在未来两年内经历财务困境的概率,帮助借款人做出最佳的财务决策。

1.首先我们上传模拟的客户历史数据样本,包含客户在过去90天是否逾期、年龄、收入、贷款数量、信用额度、过去两年逾期行为等数据,给Agent输入prompt:读取数据集cs-training.csv,并在对话框中展示前面5行。可以看到LLM生成了读csv数据的代码,并调用代码执行器工具,在本地环境使用Jupyter内核执行了代码。代码执行结果返回输入LLM,LLM对数据集各列的含义进行了理解并输出。

2. 接下来,我们继续给Agent输入prompt:请一步步处理数据集、训练模型判断一个客户在接下来的2年内是否会严重拖欠,不要遗漏字段和步骤,并在对话中展示相关可视化图表。可以看到LLM对这个要求进行了拆解,首先会做数据探索和预处理,然后选择一个机器学习模型进行训练,并且此过程中生成可视化图表。最后成功训练了一个能够预测指定客户在未来两年内是否会严重拖欠的模型,并且对于执行过程中发生的错误,能够迭代处理解决。

TensorRT-LLM 对大语言模型的加速

在金融行业中,时效性至关重要。在上述的三个示例中,LLM是这些应用的核心组件。相关的数据分析,代码的生成与调用,问题的拆解与工具选择,实际上都是LLM的推理过程。因此,提高LLM的推理速度,是提高上述示例的关键路径。

TensorRT-LLM是专门用于加速和优化LLM推理性能的加速库。它提供了最先进的优化方案,包括自定义attention内核,inflight batching机制,paged KV caching, 量化(FP8, INT4 AWQ, INT8 SmoothQuant)等优化。通过这些优化,使LLM可以在NVIDIA GPU上高效地执行推理。

本文中的三个示例使用的是Qwen和Llama大模型,下面将展示在TensorRT-LLM中对Qwen LLM的优化步骤。

TensorRT-LLM 安装

1.克隆TensorRT-LLM代码仓库

# TensorRT-LLM uses git-lfs, which needs to be installed in advance.

apt-get update && apt-get -y install python3.10 python3-pip openmpi-bin libopenmpi-dev git git-lfs

git lfs install

git clone https://github.com/NVIDIA/TensorRT-LLM.git

cd TensorRT-LLM

git submodule update --init --recursive

git lfs pull

2. 安装TensorRT-LLM

具体可参考TensorRT-LLM安装指导中提供的两种从源码编译的安装方式;或参考此链接,使用pip install来安装

构建Qwen的推理引擎

1.进入TensorRT-LLM/examples/qwen/目录,并安装相关依赖。

pip install -r requirements.txt

git lfs install

2. 从HuggingFace下载Qwen-72B-Instruct的模型权重

git clone https://huggingface.co/Qwen/Qwen2-72B-Instruct ./tmp/Qwen/72B

3. 构建Qwen的推理引擎

# Build Qwen-72B-Instruct using 8-way tensor parallelism.

python convert_checkpoint.py --model_dir ./tmp/Qwen/72B/ \

                            --output_dir ./tllm_checkpoint_8gpu_tp8 \

                            --dtype float16 \

                            --tp_size 8

 

trtllm-build --checkpoint_dir ./tllm_checkpoint_8gpu_tp8 \

            --output_dir ./tmp/qwen/72B/trt_engines/fp16/8-gpu/ \

            --gemm_plugin float16

4. 对构建好的推理引擎进行验证

# With fp16 inference

python3 ../run.py --input_text "你好,请问你叫什么?" \

                  --max_output_len=50 \

                  --tokenizer_dir ./tmp/Qwen/72B/ \

                  --engine_dir=./tmp/Qwen/72B/trt_engines/fp16/8-gpu/

对推理引擎进行性能测试

1.安装benchmark工具

python3 ./scripts/build_wheel.py –benchmarks

2. 构建测试数据集

可参考此文档,构建基于实际数据集正态分布,或均匀分布的测试数据集。例如构建1000条固定输入长度为2048、固定输出长度为32的测试数据集:

python3 ./benchmarks/cpp/prepare_dataset.py \
    --output token-norm-dist.json \
    --tokenizer ./tmp/Qwen/72B/ \
    token-norm-dist \
    --num-requests 1000 \
    --input-mean 2048 --input-stdev 0 \
    --output-mean 32 --output-stdev 0

3. 运行性能测试

mpirun -n 8 ./benchmarks/gptManagerBenchmark \
    --engine_dir ./tmp/Qwen/72B/trt_engines/fp16/8-gpu/ \
    --request_rate 10 \
    --dataset ./token-norm-dist.json \
    --max_num_samples 500

经过上述三个步骤,我们可以得到以下性能测试结果:

[BENCHMARK] num_samples 500
[BENCHMARK] num_error_samples 0

[BENCHMARK] num_samples 500
[BENCHMARK] total_latency(ms) 350899.59
[BENCHMARK] seq_throughput(seq/sec) 1.42
[BENCHMARK] token_throughput(token/sec) 45.60
[BENCHMARK] avg_acceptance_rate(tokens/decoding steps) 1.00

[BENCHMARK] avg_sequence_latency(ms) 209908.12
[BENCHMARK] max_sequence_latency(ms) 308942.41
[BENCHMARK] min_sequence_latency(ms) 66060.34
[BENCHMARK] p99_sequence_latency(ms) 308529.88
[BENCHMARK] p90_sequence_latency(ms) 305296.28
[BENCHMARK] p50_sequence_latency(ms) 215189.81

部署TensorRT-LLMOpenAI API格式Serving

在Agent应用和常见的LLM应用框架中,大家通常偏向于使用OpenAI API格式的接口,在TensorRT-LLM中我们可以参考此步骤,通过一行命令便可以部署一个OpenAI API格式的serving服务。以下展示了我们部署一个 tensor parallelism大小为8的Qwen2-72B-Instruct模型:

python /app/tensorrt_llm/examples/apps/openai_server.py /workspace/Qwen2-72B-Instruct --tp_size 8

注意:如果是多GPU的部署,例如上述Qwen2-72B-Instruct模型使用8张GPU部署,首次需要发送一个调用请求,才会触发engine的加载。如果是单GPU部署,则会自动加载engine,无需进行首次请求触发。

使用NIM部署

NIM(NVIDIA Inference Microservice),是NVIDIA AI Enterprise的一部分,NVIDIA NIM™ 是 NVIDIA AI Enterprise 的一部分,为跨云、数据中心和工作站的自托管 GPU 加速推理微服务提供容器,用于预训练和自定义的AI 模型。使用简单命令部署后,NIM 微服务会提供行业标准 API,以便轻松地集成到 AI 应用程序、开发框架和工作流程中。NIM 微服务基于 NVIDIA 和社区的预优化推理引擎 (包括 NVIDIA® TensorRT™ 和 TensorRT-LLM) 构建,可自动优化运行时检测到的基础模型和 GPU 系统的每个组合的响应延迟和吞吐量。NIM 容器还提供标准的可观察性监控指标,并支持在 GPU 上的 Kubernetes 的动态扩展。这样,就可以简化模型优化的部分,让用户更专注于业务流程的开发当中。

目前,NIM提供两种使用方式,一种是通过API来调用云端运行的NIMs,另外一种是在本地部署NIM容器。云端运行方式,可以通过登录ai.nvidia.com来快速体验。下面将展示通过云端API的方式,来快速构建上文中提到的用于量化投资的Agent智能体。

  1. 在Langchain中,我们可以通过一个封装的接口ChatNVIDIA()来调用NIM云端API或本地API。https://integrate.api.nvidia.com/v1是云端API的URL,我们通过更换模型名,例如 meta/llama3-70b-instruct、 nvidia/nemotron-4-340b-instruct、 qwen/qwen2-7b-instruct,可以快捷地测试不同模型在当前Agent场景下的表现。
from langchain_nvidia_ai_endpoints import ChatNVIDIA

base_url= "https://integrate.api.nvidia.com/v1"
llm = ChatNVIDIA(base_url=base_url, model="meta/llama3-70b-instruct", temperature=1, api_key="nvapi-...")

messages = [
{
'role': 'system',
'content': 'You are a senior quantitative researcher at a top global quantitative hedge fund, highly skilled in discovering and constructing price-volume factors. '
'our daily task is to build price-volume stock selection factors that contain high and stable alpha from price-volume data.',
'role': 'user',
'content': '...',
}
]
factor_message = llm.invoke(messages).content
  • ChatNVIDIA()是一个高级封装的接口,除了标准的OpenAI API格式的推理接口,还提供了工具绑定(.bind_tools)、结构化输出(.with_structured_output )等高级接口。

2.1 工具绑定(.bind_tools)

from langchain_nvidia_ai_endpoints import ChatNVIDIA
from langchain_core.tools import tool
from typing import Annotated

base_url= "https://integrate.api.nvidia.com/v1"

@tool
def function1(param1: Annotated[str, "Parameter description"]):
"""
Function description.
"""
    ...
    return ...

llm = ChatNVIDIA(base_url=base_url, model="meta/llama3-70b-instruct", temperature=0.3, api_key="nvapi-...").bind_tools([function1)

Messages = [...]
response = self.llm.invoke(messages).content

2.2 结构化输出(.with_structured_output )

from langchain_nvidia_ai_endpoints import ChatNVIDIA
from pydantic import BaseModel, Field
from langchain.prompts.chat import ChatPromptTemplate

base_url= "https://integrate.api.nvidia.com/v1"

class Factor(BaseModel):
    name: str = Field(..., description="Factor name")
    method: str = Field(..., description="Factor calculation formula")
    meanings: str = Field(..., description="Factor meaning")

extract_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a world-class algorithm for extracting structured format information."),
    ("human", "Extract information from the following input using the given format: {input}"),
    ("human", "Hint: Ensure to answer in the correct format."),
])

llm = ChatNVIDIA(base_url= base_url, model="nv-mistralai/mistral-nemo-12b-instruct", temperature=0, api_key= "nvapi-...")
extract_chain = extract_prompt | extract_llm.with_structured_output(schema=Factor) 
factor = extract_chain.invoke({"input": factor_message})

注意:不是所有的NIM模型都支持结构化输出接口,我们可以使用以下命令进行查询,选择适合的模型。

[model for model in ChatNVIDIA.get_available_models() if model.supports_structured_output]
```
输出结果: [Model(id='nv-mistralai/mistral-nemo-12b-instruct', model_type='chat', client='ChatNVIDIA', endpoint=None, aliases=None, supports_tools=True, supports_structured_output=True, base_model=None), Model(id='meta/llama-3.1-70b-instruct', model_type='chat', client='ChatNVIDIA', endpoint=None, aliases=None, supports_tools=True, supports_structured_output=True, base_model=None), Model(id='writer/palmyra-fin-70b-32k', model_type='chat', client='ChatNVIDIA', endpoint=None, aliases=None, supports_tools=False, supports_structured_output=True, base_model=None), Model(id='meta/llama-3.1-405b-instruct', model_type='chat', client='ChatNVIDIA', endpoint=None, aliases=None, supports_tools=True, supports_structured_output=True, base_model=None), Model(id='meta/llama-3.1-8b-instruct', model_type='chat', client='ChatNVIDIA', endpoint=None, aliases=None, supports_tools=True, supports_structured_output=True, base_model=None), Model(id='mistralai/codestral-22b-instruct-v0.1', model_type='chat', client='ChatNVIDIA', endpoint=None, aliases=['ai-codestral-22b-instruct-v01'], supports_tools=False, supports_structured_output=True, base_model=None)]
```

除了云端访问方式外,NIM还支持本地化部署。通过NIM,可以将优化后的LLM打包成容器镜像,然后通过API对外提供服务。在NVAIE中,包含一些已经对模型优化过并打包好的NIM容器,其中包括LLaMA3-70B,Mixtral-8x7B等模型,支持的模型列表可在NIM docs中查看。下面将展示如何借助NIM容器本地化部署的方式,来快速构建上文中提到的用于撮合交易的Agent智能体。

  1. 只要以下一条命令,就可以自动化完成拉取镜像、下载模型参数、选择适合的后端和配置部署模型推理服务的全流程。
export NGC_API_KEY="YOUR-API-KEY"
export LOCAL_NIM_CACHE= ~/.cache/nim
mkdir -p "$LOCAL_NIM_CACHE"
docker run -it --rm \
    --gpus '"device=0,1,2,3"' \
    --shm-size=16GB \
    -e NGC_API_KEY \
    -v "$LOCAL_NIM_CACHE:/opt/nim/.cache" \
    -u $(id -u) \
    -p 8000:8000 \
    --ipc=host \
    nvcr.io/nim/meta/llama3-70b-instruct:1.0.0

2. 调用本地化NIM接口的方式和调用云端接口的方式相同,只需要将URL从云端地址(https://integrate.api.nvidia.com/v1)改为本地机器IP地址和对应端口号(http://xx.xx.xx.xx:8000/v1)。同样支持标准的OpenAI API格式的推理接口,和工具绑定(.bind_tools)、结构化输出(.with_structured_output )等高级接口。撮合交易Agent中对本地NIM接口的调用如以下 init_agent_service()方法所示。

from qwen_agent.agents import ReActChat
from qwen_agent.tools.base import BaseTool, register_tool
import json

@register_tool('inquiry')
class Inquiry(BaseTool): 
    description = '首次报价后如果客户讲价,查询金融产品可提供的价格。'
    parameters = [...]
    def call(self, params: str, **kwargs) -> str:
        ...
        return json.dumps({...}, ensure_ascii=False)

@register_tool('strategy_query')
class StrategyQuery(BaseTool):
    ...

@register_tool('transaction_confirm')
class TransactionConfirm(BaseTool):
    ...

@register_tool('transaction_cancel')
class TransactionCancel(BaseTool): 
    ...

def init_agent_service():
    llm_cfg = {
        'model': 'meta/llama3-70b-instruct',
        'model_server': 'http://0.0.0.0:8000/v1', 
        'api_key': "...",
    }
    tools = ['inquiry', 'strategy_query', 'transaction_confirm', 'transaction_cancel']
    bot = ReActChat(
            llm=llm_cfg, 
            name='match transaction agent', 
            description='This agent can help to match transaction.',
            function_list=tools)
    return bot

总结

本文介绍了LLM Agent智能体的基本原理,并通过三个银行类和资本市场类的相关案例,阐述了LLM Agent智能体在金融行业中的应用,以及基于NVIDIA TensorRT-LLM和NIM的应用构建方案。Agent智能体中的核心组件LLM在该方案下可以获得极致加速,欢迎大家体验和使用NVIDIA TensorRT-LLMNIM

标签