亚马逊AWS官方博客

利用 Amazon Bedrock,3 步低代码构建 AI 股票分析助手

在金融服务行业的数字化转型浪潮中,构建一个灵活、可扩展、高度可用的现代 IT 架构是金融机构面临的重中之重。在这一过程中,生成式人工智能(Generative AI)正成为助力金融从业者提高工作效率、优化决策过程的重要工具。

在金融市场上,买方机构通常会努力发掘有关金融市场运作的信息,以期获得战胜市场的能力。他们通常需要发掘对投资组合风险有重大影响的风险因子,比如市场因子、行业因子、规模因子、价值因子等;并且收集与选定风险因子相关的数据,包括股票收益率、行业归属、市值、账面市值比等。这些因子经常需要从原始数据(新闻,财务报表,网页等)进行 ETL 来清洗生成。然后基于选定的风险因子和处理后的数据,构建风险模型。他们的研究人员利用内部构建的各种工具和算法查询数据,从中获得洞见。基金公司通常会从外部来源或数据提供商处获取金融市场数据,准备分析并创建数据湖。研究分析师在这些数据上运行批处理分析作业或实时查询,以获得投资信号,为投资者构建目标投资组合。考虑当前和目标投资组合以及投资信号后,生成适当的交易信号,再通过经纪人执行交易。

从前台到中后台,从客户获取到投资组合构建,我们看到越来越多客户正在利用生成式 AI 进行全流程价值链提升。生成式 AI 可在以下领域提高从业者生产效率:

  • 生成投资报告、市场观察报告、合规报告等
  • 智能研究助理,如通过综合市场数据和基金回报解释业绩表现,或通过审阅大量报告论文识别趋势
  • 文本摘要总结和后续研究
  • 构建可视化报表
  • 编写样板代码

传统上,查询数据库、转换和可视化数据需要数据工程师或数据科学家编写针对数据库和 API 的代码。他们需要提取和转换数据,以便可视化,用于投资组合构建、投资组合归因或历史模拟。但有了生成式 AI,非编码人员可使用自然语言与 AI 助手交互,AI 助手将自然语言提示转为代码并执行,生成所需输出。使用 Amazon Bedrock 的 Agents 功能,即使是非编码人员也可以轻松低代码实现复杂的功能。将大型语言模型(LLM)连接到自己的流程、系统和数据源,利用 LLM 的推理能力,自主创建编排计划、执行复杂任务,并通过调用正确的 API 完成用户请求。Amazon Bedrock 的 Agents 加速了生成式 AI 应用程序的交付,这些应用程序可以通过调用公司系统的 API 来管理和执行任务。Agents 扩展了 LLM,使其能够理解用户请求、将复杂任务分解为多个步骤、进行对话收集额外信息,并采取行动来完成请求。

在当今快节奏的金融市场中,投资者需要实时获取并分析大量信息,以做出明智的投资决策。传统的分析方式需要大量的代码工作,也对人员的技术代码能力有一定的要求。通过利用 Amazon Bedrock 的 Agents 功能,我们可以低代码构建 AI 驱动的股票分析助手,帮助投资者快速高效地分析股票。要构建这样一个AI股票分析助手,我们首先需要设置一个 Agent,让它能访问所需的数据源和 API,比如在我们的例子里面,包括股票新闻 API、分析师评级 API 和行业数据 API,当然你可以加上更多的工具和信息。我们需要确保 Agent 有权限从这些 API 安全获取相关数据。接下来,我们定义 Agent 的任务,也就是对特定股票进行全面分析并给出投资建议。我们只需用自然语言表述需求,比如”请分析亚马逊公司股票的当前状况,并提供投资建议”。一旦接收到请求,Agent 会首先理解并分解任务。它可能会先调用新闻 API 获取亚马逊的最新新闻,再调用分析师评级 API 获取外部分析师的评级,最后查询行业数据了解亚马逊所在行业的发展趋势。在汇总所有数据后,Agent 会运用大语言模型的推理能力,综合分析并形成对该股票的看法和投资建议。例如:“根据最新新闻,亚马逊将于下季度推出新的物流优化方案,有望进一步提高运营效率。分析师给予该股票“买入”的平均评级。电商行业整体保持两位数增长。考虑到这些因素,我们建议可适当买入亚马逊股票。”最终,Agent 会以自然语言向用户呈现分析结果和建议。用户还可以与 Agent 进行互动,询问更多细节或改变分析目标等,Agent 会相应调整分析路径。

通过这个例子,我们可以看到 Amazon Bedrock Agents 如何帮助我们低代码、高效地构建 AI 应用,利用大语言模型的推理和自然语言交互能力,完成涉及多个数据源和 API 的复杂分析任务。这极大简化了 AI 应用的开发流程,使金融行业的分析师和投资者能够快速验证创新想法,提高工作效率。

接下来我们展示,如何利用 Amazon Bedrock 低代码搭建自己的简单 AI 股票分析助手。

第一步,创建。我们在 Amazon Bedrock 控制台首先创建一个名为”stock-analyst”的 Agent,并给它写上简单的描述。stock-analyst 需要访问多个数据源才能完成复杂的分析任务,因此后续我们需要为它连接股票新闻 API、分析师评级 API 和行业数据 API,并确保 stock-analyst 能安全可靠地获取所需信息。

第二步,配置。想要构建一款 AI 投资助手不仅需要连接数据源,更重要的是赋予它强大的分析能力和专业的投资智慧。在 Amazon Bedrock 中,我们可以轻松配置 Agent 的“大脑”和“性格”。对于 stock-analyst 这款 AI 股票分析助手,我们首选 Claude 3 Sonnet 作为其底层大语言模型。这是一款经过特殊训练的高性能模型,拥有广博的金融知识和出色的推理分析能力,非常适合复杂的投资场景。接下来我们需要为 stock-analyst 设置一套行为指令(Instructions),以确保它能够以专业、中肯的方式开展工作。除了头脑,我们还可以调整 stock-analyst 的性格特质,如自信程度、谨慎程度等,使其更加符合一名专业分析师的形象。这种设定形象的 prompt 方式有助于提升分析质量。我们编写如下指令:“你是一个专业的股票研究员,你会分析外部信息,给出专业且中肯的股票投资建议。如果你不确定,请直接回答不知道,不要随意给出不专业的回答。请保持冷静和审慎的态度,给出具体的事实和思考逻辑。” 这些指令将深深植入 stock-analyst 的基因,指导它在分析过程中如何表现、如何沟通、如何确保建议的专业性和权威性。我们希望 stock-analyst 能够像一名资深分析师那样运作,用理性和事实说话,而不是随意发表主观想法。

接下来,我们创建了一个名为”get_data”的操作组(Action Group),专门负责从外部渠道采集所需数据。这个操作组扮演着 stock-analyst 的“数据摄入层”角色,确保后续的分析建立在坚实的数据基础之上。为了快速验证概念展示,我们直接选择了 Yahoo Finance API 作为主要数据源。该 API 提供了丰富的上市公司数据,包括实时行情、新闻动态、分析师评级、财务指标等,可以全方位地反映一家公司的运营状况。当然,在实际的生产环境中,我们可以灵活地集成自有的内部数据源或其他第三方 API,以获取更加专业和定制化的数据服务。在 get_data 操作组中,我们将明确定义数据采集的流程,也就是具体的 API 函数。

我们希望通过股票代码,获取新闻动态、分析师评级、行业概况等原始数据投资分析,因此直接在 get_data 这个 action group 下创建 3 个函数,分别是:

  1. get_industry_analysis(ticker) 根据输入的股票代码,获取股票所在的行业,为后续行业分析奠定基础
  2. get_analyst_ratings(ticker) 获取最新的分析师对该股票的评级数据,包括买入、持有和卖出的具体比例,为投资决策提供外部专家视角
  3. get_sentiment_analysis(ticker) 获取该股票最新的新闻信息,并利用自然语言处理技术对新闻正文执行情绪分析,判断新闻所传达的情绪倾向,帮助投资者把握市场情绪变化

我们只需在控制台上一次性定义并创建这 3 个函数及其参数,即可完成”get_data”这个 Action Group 的全部配置,而无需进行任何复杂的手动设置。

第三步,实现。在完成函数定义后,Amazon Bedrock 会自动为我们创建相应的 AWS Lambda 函数,配置 CloudWatch 等监控服务、IAM 等权限设置,而我们只需要登陆到 get_data 的 Lambda 函数页面,使用 Python 编写 3 个函数的具体实现逻辑。我们可以直接跳转到创建成功后的 Lambda 函数内,实现自动生成的 dummy_lambda.py。

整个代码实现非常简单,只需要关注在业务逻辑层面上的实现。当然,你可能会在代码中利用到部分第三方库/外部依赖包,可以利用 Lambda Layer 来解决。

import json
from datetime import datetime, timedelta
import yfinance as yf
import requests
from bs4 import BeautifulSoup

def get_article_text(url):
    try:
        response = requests.get(url)
        soup = BeautifulSoup(response.content, 'html.parser')
        article_text = ' '.join([p.get_text() for p in soup.find_all('p')])
        return article_text
    except:
        return "Error retrieving article text."

def get_industry_analysis(ticker):
    stock = yf.Ticker(ticker)
    industry = stock.info['industry']
    sector = stock.info['sector']
    
    return f"industry {industry} sector {sector}"

def get_analyst_ratings(ticker):
    stock = yf.Ticker(ticker)
    
    recommendations = stock.recommendations
    print("recommendations:", recommendations)
    
    if recommendations is None or recommendations.empty:
        return "No analyst ratings available."

    latest_rating = recommendations.iloc[-1]

    strongBuy = latest_rating.get('strongBuy', 'N/A')
    buy = latest_rating.get('buy', 'N/A')
    hold = latest_rating.get('hold', 'N/A')
    sell = latest_rating.get('sell', 'N/A')
    strongSell = latest_rating.get('strongSell', 'N/A')

    rating_summary = f"Latest analyst rating for {ticker}:\nstrongBuy: {strongBuy}\nbuy: {buy}\nhold: {hold}\nsell: {sell}\nstrongSell: {strongSell}."
    return rating_summary

def get_sentiment_analysis(ticker):
    end_date = datetime.now().date()
    start_date = end_date - timedelta(days=1*365)

    stock = yf.Ticker(ticker)
    news = stock.news
    
    news_text = ""
    for article in news:
        article_text = get_article_text(article['link'])
        timestamp = datetime.fromtimestamp(article['providerPublishTime']).strftime("%Y-%m-%d")
        news_text += f"\n\n---\n\nDate: {timestamp}\nTitle: {article['title']}\nText: {article_text}"
        
    return news_text

def lambda_handler(event, context):
    agent = event['agent']
    actionGroup = event['actionGroup']
    function = event['function']
    parameters = event.get('parameters', [])
    responseBody = {
        "TEXT": {
            "body": "Error, no function was called"
        }
    }

    if function == 'get_industry_analysis':
        ticker = None
        for param in parameters:
            if param["name"] == "ticker":
                ticker = param["value"]

        if not ticker:
            raise Exception("Missing mandatory parameter: ticker")

        industry_analysis = get_industry_analysis(ticker)
        responseBody = {
            'TEXT': {
                "body": industry_analysis
            }
        }
    elif function == 'get_analyst_ratings':
        ticker = None
        for param in parameters:
            if param["name"] == "ticker":
                ticker = param["value"]

        if not ticker:
            raise Exception("Missing mandatory parameter: ticker")

        analyst_ratings = get_analyst_ratings(ticker)
        responseBody = {
            'TEXT': {
                "body": analyst_ratings
            }
        }
    elif function == 'get_sentiment_analysis':
        ticker = None
        for param in parameters:
            if param["name"] == "ticker":
                ticker = param["value"]

        if not ticker:
            raise Exception("Missing mandatory parameter: ticker")

        sentiment_analysis = get_sentiment_analysis(ticker)
        responseBody = {
            'TEXT': {
                "body": sentiment_analysis
            }
        }
    else:
        responseBody = {
            'TEXT': {
                "body": f"Error, function '{function}' not recognized"
            }
        }

    action_response = {
        'actionGroup': actionGroup,
        'function': function,
        'functionResponse': {
            'responseBody': responseBody
        }
    }

    function_response = {'response': action_response, 'messageVersion': event['messageVersion']}
    print("Response: {}".format(function_response))

    return function_response

为了提高代码开发的效率,我们除了自己实现上述代码,也可以利用 Amazon Bedrock 的开发助手来生成代码框架。通过简单描述函数定义并选择格式规范,开发助手就能为我们生成初步的代码结构。之后,我们只需在此基础上填充具体的功能实现,就能快速完成 Lambda 函数的开发。直接将”Generated Python Lambda Code based on Function Definitions”下方生成的代码复制到 Lambda 函数中,并在此基础上,简单实现获取行业分析、分析师评级和情感分析等功能,就可以完成整个 Agent 的构建,并且 Agent 会通过调用 Lambda 函数为后续的投资分析决策提供所需的原始数据支持。

在完成 Lambda 函数的补充和功能实现后,我们就可以在页面上进行简单的 Agent 功能测试了。通过 Trace 功能,我们能够看到生成式 AI 在执行任务时的思考过程和调用的函数流程,这对于了解 AI 的决策路径和优化 Prompt 的质量都至关重要。

通过 Trace 功能我们可以了解到 Amazon Bedrock Agents 是如何通过 Prompt 工作的。为了优化结果,Amazon Bedrock Agents 还提供了”Edit”模块,让用户能够直接优化和覆盖默认的 Prompts。通过进入该模块,你可以一览不同任务阶段所使用的 Prompts 模板。只需点击”Override Default Prompt”,即可根据实际需求修改和自定义这些 Prompts。这种可视化的方式使得定制 Prompts 的过程变得前所未有的简单直观。你无需深涉代码,就能充分发挥创意,为 Amazon Bedrock Agents 注入个性化的指令。无论是优化任务执行的逻辑,还是调整语言风格,亦或是嵌入特定领域的知识,定制 Prompts 都将为你打开一扇全新的大门,释放AI助理的无限潜能。具体可以详细参考官方文档

当然这只是一个简单的 Demo。在真实场景下,客户可以进一步扩展这个 AI 股票分析助手,整合更多数据源、添加风险模型等,打造一个功能更加强大的投资辅助工具。不断优化的过程中,我们可以通过创建 Agent 的别名来设置 Agent 的部署和集成到您的应用程序中,并且可以通过版本来管理不同的 Agent 快照。

以买方的投资研究、投资组合构建等阶段来说,生成式 AI 可以带来非常多的新思路。比如,通过数据可视化、文本摘要和对话式的界面,充分利用来自结构化、半结构化和非结构化的数据,可以帮助基金经理进行构思过程,比如 Bridgewater 的案例。除了买方,金融市场上还有卖方券商、财富管理这些参与者。每当公司发新的财报,或是宏观经济形势、政策变化、行业动态等因素发生变化,卖方分析师就会需要就目标公司发表研究报告来更新研报,财富管理经理会向客户解释金融市场的变化,并根据客户的风险偏好和投资目标,提供相应的投资建议。

利用探索对话式的工具,投资者可以自主搜索投资和详细信息,以获取最新的见解和可视化信息,并根据当前事件和市场情绪生成动态风险。分析师还可以使用文本数据源创建投资主题,人工智能代理充当搜索大型文本数据存储库的角色,包括新闻、文件、收益记录和经纪研究以及内部研究和会议记录。人工智能代理可以总结同一个或相关行业中不同公司的投资主题,并寻找可能隐藏在财务报表脚注中的重要信息片段。在这种情况下,分析师的任务从寻找、阅读和总结相关文档部分转变为依赖人工智能助手进行搜索和总结,并与代理交互以构建论点并检查准确性和完整性。生成式 AI 还可以加速主题投资策略。通过与情感分析相结合,充当推荐引擎或完全自动化系统性策略,从文本数据源识别一个流行主题,并评估与这些实体相关的情绪和可投资实体。例如通过“生成式 AI”这一主题相关的积极情绪构建新兴主题,推测与之相关的公司。而 Amazon Bedrock 可以帮助您低代码、安全、高效的实现您的构想。


*前述特定亚马逊云科技生成式人工智能相关的服务仅在亚马逊云科技海外区域可用,亚马逊云科技中国仅为帮助您了解行业前沿技术和发展海外业务选择推介该服务。

参考文档

Amazon Bedrock:https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html

Amazon Bedrock Agents:https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html

本篇作者

钱宇秋 Tina

亚马逊云科技解决方案架构师。中国科学技术大学计算机本科,香港大学计算机博士。目前专注于金融行业,FRM 持证人,CFA 二级。提供金融行业企业级解决方案,以及 Data、AI 相关的技术咨询。