Skip to content

10.5 工具系统 - Agent的武器库

🎯 本节目标

工具(Tools)是Agent与真实世界交互的桥梁。一个Agent再聪明,如果没有工具,也无法获取实时数据、执行具体操作。本节将深入解析TradingAgent的工具系统:如何组织、如何调用、如何与LLM协作。

🏗️ 工具系统架构

整体设计哲学

TradingAgent的工具系统遵循**"分组隔离、按需调用"**的设计原则:

4个工具节点 (ToolNode)
├─ tools_market       → Market Analyst专用
├─ tools_social       → Social Media Analyst专用
├─ tools_news         → News Analyst专用
└─ tools_fundamentals → Fundamentals Analyst专用

为什么要分组?

  1. 职责明确: 每个Analyst只能调用与其职责相关的工具
  2. 提示词优化: LLM看到的工具列表更短、更相关
  3. 权限控制: 避免Analyst越权访问不该访问的数据
  4. 性能优化: 减少LLM的选择负担

代码实现

trading_graph.py 中:

python
def _create_tool_nodes(self) -> Dict[str, ToolNode]:
    """Create tool nodes for different data sources"""
    return {
        "market": ToolNode([
            get_stock_data,      # 股票价格数据
            get_indicators,      # 技术指标
        ]),
        "social": ToolNode([
            get_news,            # 社交媒体新闻
        ]),
        "news": ToolNode([
            get_news,            # 新闻文章
            get_global_news,     # 全球新闻
            get_insider_sentiment,    # 内部人情绪
            get_insider_transactions, # 内部人交易
        ]),
        "fundamentals": ToolNode([
            get_fundamentals,    # 基本财务指标
            get_balance_sheet,   # 资产负债表
            get_cashflow,        # 现金流量表
            get_income_statement,# 利润表
        ]),
    }

🛠️ 工具清单详解

类别1: 市场数据工具 (Market Tools)

1. get_stock_data - 股票价格数据

功能: 获取历史股票价格和成交量

参数:

  • symbol: 股票代码 (如 "AAPL")
  • curr_date: 当前日期
  • lookback_days: 回溯天数 (默认15天)

返回示例:

json
{
  "2024-11-01": {"open": 220.75, "high": 222.50, "low": 219.80, "close": 221.45, "volume": 52341200},
  "2024-11-04": {"open": 221.50, "high": 223.10, "low": 220.90, "close": 222.80, "volume": 48765300},
  ...
}

典型用途:

Market Analyst问题: "AAPL最近的价格走势如何?"
LLM决策: 调用get_stock_data(AAPL, 2024-11-19, 15)
获得数据后: "从15天数据看,AAPL从220.75涨至228.02,涨幅3.3%..."

2. get_indicators - 技术指标

功能: 计算各类技术指标

参数:

  • symbol: 股票代码
  • indicator: 指标名称 (支持60+种)
  • curr_date: 当前日期

支持的指标 (部分列表):

python
# 趋势指标
- 'macd': MACD (Moving Average Convergence Divergence)
- 'adx': ADX (Average Directional Index)
- 'supertrend': 超级趋势指标

# 动量指标
- 'rsi': RSI (Relative Strength Index)
- 'cci': CCI (Commodity Channel Index)
- 'stoch': 随机指标

# 波动率指标
- 'boll': 布林带
- 'atr': ATR (Average True Range)

# 成交量指标
- 'vr': 成交量比率
- 'obv': OBV (On-Balance Volume)

返回示例 (RSI):

json
{
  "2024-11-04": 45.2,
  "2024-11-05": 66.67,
  "2024-11-06": 72.3,
  ...
  "2024-11-18": 68.5
}

典型用途:

Analyst: "需要查看RSI是否超买"
→ 调用get_indicators('AAPL', 'rsi', '2024-11-19')
→ 发现RSI=72.3,超过70阈值
→ 结论: "RSI显示超买状态,可能面临回调风险"

类别2: 社交媒体工具 (Social Tools)

3. get_news - 社交媒体新闻

功能: 获取Reddit等社交平台的讨论

参数:

  • query: 搜索关键词 (如 "Apple Inc")
  • start_date: 开始日期
  • end_date: 结束日期

数据来源:

python
搜索范围 = [
    'wallstreetbets',  # 散户聚集地
    'stocks',          # 股票讨论
    'investing',       # 投资策略
    'SecurityAnalysis',# 证券分析
    'Finance',         # 金融综合
    'Economics',       # 经济学讨论
]

返回示例:

json
{
  "post_count": 15,
  "avg_sentiment": 0.65,  # -1到1,正值表示积极
  "hot_topics": ["iPhone 15", "AI expansion", "China market"],
  "sample_posts": [
    {
      "title": "AAPL breaking resistance!",
      "score": 245,
      "comments": 89,
      "sentiment": 0.8
    }
  ]
}

类别3: 新闻分析工具 (News Tools)

4. get_global_news - 全球新闻

功能: 获取宏观经济和公司新闻

数据源:

  • Bloomberg
  • Yahoo Finance
  • EODHD API
  • FinnHub
  • Reuters

返回示例:

json
{
  "news": [
    {
      "title": "Apple Unveils AI-Powered Smart Home Device",
      "source": "Bloomberg",
      "date": "2024-11-15",
      "sentiment": "positive",
      "summary": "Apple enters smart home market with innovative AI features..."
    },
    {
      "title": "Fed Signals Potential Pause in Rate Cuts",
      "source": "Reuters",
      "date": "2024-11-14",
      "sentiment": "neutral",
      "impact": "macroeconomic"
    }
  ]
}

5. get_insider_sentiment - 内部人情绪

功能: 分析公司内部人员的情绪和行为

指标:

  • MSPR (Monthly Share Purchase Ratio): 月度净买入比率
  • CHANGE: 持股变化
  • TRANSACTIONS: 交易次数

返回示例:

json
{
  "2024-09": {"mspr": -2.5, "change": -50000, "transactions": 12},  # 内部人净卖出
  "2024-10": {"mspr": 0.2, "change": 5000, "transactions": 3},      # 小幅买入
  "2024-11": {"mspr": -1.8, "change": -30000, "transactions": 8}    # 再次卖出
}

解读:

  • MSPR < -1: 强烈看空信号
  • MSPR > 1: 强烈看多信号
  • MSPR接近0: 中性

6. get_insider_transactions - 内部人交易

功能: 详细的内部人买卖记录

返回示例:

json
{
  "transactions": [
    {
      "name": "Arthur D. Levinson",
      "position": "Chairman",
      "date": "2024-11-15",
      "type": "SALE",
      "shares": 150000,
      "price": 228.66,
      "value": 34299000
    }
  ]
}

类别4: 基本面工具 (Fundamentals Tools)

7. get_fundamentals - 关键财务指标

功能: 获取核心财务比率和指标

返回示例:

json
{
  "valuation": {
    "pe_ratio": 37.79,           # 市盈率
    "pb_ratio": 62.20,           # 市净率
    "ps_ratio": 9.82,            # 市销率
    "pcf_ratio": 30.05           # 市现率
  },
  "profitability": {
    "gross_margin": 46.21,       # 毛利率
    "operating_margin": 31.51,   # 营业利润率
    "net_margin": 23.97,         # 净利率
    "roe": 164.59,               # 净资产收益率
    "roa": 25.68                 # 总资产收益率
  },
  "growth": {
    "revenue_growth_3y": 2.25,   # 3年营收增长
    "eps_growth_5y": 15.41,      # 5年EPS增长
    "revenue_growth_qoq": 6.07   # 季度营收增长
  },
  "liquidity": {
    "current_ratio": 0.87,       # 流动比率
    "quick_ratio": 0.83          # 速动比率
  },
  "leverage": {
    "debt_to_equity": 1.87,      # 负债股权比
    "interest_coverage": 28.5    # 利息保障倍数
  }
}

8. get_balance_sheet - 资产负债表

核心数据:

  • 总资产、总负债、股东权益
  • 流动资产、固定资产
  • 短期负债、长期负债

9. get_cashflow - 现金流量表

核心数据:

  • 经营活动现金流
  • 投资活动现金流
  • 融资活动现金流
  • 自由现金流

10. get_income_statement - 利润表

核心数据:

  • 营业收入、营业成本
  • 毛利润、营业利润、净利润
  • 每股收益(EPS)

🔄 ReAct模式:LLM如何调用工具?

ReAct循环详解

ReAct = Reason (推理) + Act (行动)

循环开始

├─ Reason (推理): "我需要什么数据?"
│   LLM内心独白: "要分析AAPL的技术面,我需要价格数据和RSI指标"
│   ↓
├─ Act (行动): 调用工具
│   工具调用1: get_stock_data("AAPL", "2024-11-19", 15)
│   等待工具返回...
│   ↓
├─ Observe (观察): 接收工具返回的数据
│   获得15天的价格数据
│   ↓
├─ Reason (再次推理): "还需要什么?"
│   LLM: "有了价格,现在需要RSI指标来判断超买超卖"
│   ↓
├─ Act (再次行动):
│   工具调用2: get_indicators("AAPL", "rsi", "2024-11-19")
│   等待工具返回...
│   ↓
├─ Observe (再次观察):
│   获得RSI=72.3
│   ↓
├─ Reason (最终推理): "数据够了,可以输出报告了"
│   LLM: "价格上涨+RSI超买 = 短期回调风险"
│   ↓
└─ Finish: 不再调用工具,输出最终报告

实际代码追踪

Market Analyst的Prompt片段:

python
You are a Market Analyst with access to the following tools:
- get_stock_data: Get historical stock prices
- get_indicators: Calculate technical indicators (RSI, MACD, etc.)

Your task: Analyze the market trends for {company} on {trade_date}.

You can call multiple tools in sequence to gather all needed information.
When you have enough data, provide your final report without calling more tools.

LLM的第一轮输出:

json
{
  "tool_calls": [
    {
      "name": "get_stock_data",
      "arguments": {"symbol": "AAPL", "curr_date": "2024-11-19", "lookback_days": 15}
    }
  ]
}

Conditional Logic判断:

python
def should_continue_market(state: AgentState):
    last_message = state["messages"][-1]
    if last_message.tool_calls:
        return "tools_market"  # 有工具调用,去执行工具
    return "Msg Clear Market"  # 无工具调用,输出报告

工具节点执行:

python
# LangGraph自动调用工具并返回结果
tool_result = get_stock_data("AAPL", "2024-11-19", 15)
# 将结果添加到messages
state["messages"].append(ToolMessage(content=tool_result))

LLM的第二轮输出:

json
{
  "tool_calls": [
    {
      "name": "get_indicators",
      "arguments": {"symbol": "AAPL", "indicator": "rsi", "curr_date": "2024-11-19"}
    }
  ]
}

...循环继续,直到LLM不再调用工具

LLM的最终输出:

markdown
## Market Analysis Report for AAPL (2024-11-19)

### Price Action
- 15-day trend: Upward momentum from $220.75 to $228.02 (+3.3%)
- Recent volatility: Moderate, with daily ranges of 2-3%

### Technical Indicators
- **RSI**: 72.3 - **OVERBOUGHT** condition
- **MACD**: Bullish crossover, but momentum slowing
- **Bollinger Bands**: Price touching upper band, suggesting potential reversal

### Conclusion
Strong upward trend but RSI indicates overbought conditions.
Recommend caution for new entries. Watch for pullback to 225 support.

🎯 工具调用的最佳实践

1. 工具描述要清晰

python
@tool
def get_indicators(symbol: str, indicator: str, curr_date: str) -> str:
    """Calculate technical indicators for a stock.

    Args:
        symbol: Stock ticker symbol (e.g., 'AAPL')
        indicator: Indicator name. Available options:
            - 'rsi': Relative Strength Index (0-100, >70=overbought, <30=oversold)
            - 'macd': MACD line and signal line values
            - 'boll': Bollinger Bands (upper, middle, lower)
        curr_date: Current trading date in YYYY-MM-DD format

    Returns:
        JSON string with indicator values for the past 15 days
    """
    ...

为什么重要?

  • LLM通过描述理解工具功能
  • 清晰的参数说明减少调用错误
  • 示例值帮助LLM正确格式化参数

2. 返回格式要一致

好的返回:

json
{"2024-11-01": 45.2, "2024-11-02": 48.5, ...}

不好的返回:

RSI on Nov 1 is 45.2, Nov 2 is 48.5...

原因: JSON格式LLM更容易解析和理解

3. 错误处理要友好

python
def get_stock_data(symbol: str, curr_date: str, lookback_days: int = 15) -> str:
    try:
        data = fetch_from_api(symbol, curr_date, lookback_days)
        return json.dumps(data)
    except APIError as e:
        return json.dumps({
            "error": True,
            "message": f"Failed to fetch data for {symbol}: {str(e)}",
            "suggestion": "Try a different date or check if the symbol is correct"
        })

好处: LLM可以理解错误并采取替代策略

🤔 核心问题解答

Q3: 有多少个工具节点?每个可以调用哪些工具?

答案: 4个工具节点

工具节点可调用的工具工具数量
tools_marketget_stock_data, get_indicators2
tools_socialget_news1
tools_newsget_news, get_global_news, get_insider_sentiment, get_insider_transactions4
tools_fundamentalsget_fundamentals, get_balance_sheet, get_cashflow, get_income_statement4

总计: 11个工具函数 (去重后,因为get_news在两个节点中)

Q4: LLM如何判断调用哪个工具?

答案: 通过以下机制:

  1. System Prompt: 告诉LLM可用工具列表
You have access to: get_stock_data, get_indicators
  1. Tool Schema: 每个工具的详细描述
json
{
  "name": "get_indicators",
  "description": "Calculate technical indicators...",
  "parameters": {...}
}
  1. LLM推理: 根据任务目标选择合适工具
任务: "分析RSI是否超买"
→ LLM推理: "需要RSI数据"
→ 选择工具: get_indicators
→ 参数: indicator="rsi"
  1. Function Calling: LLM输出JSON格式的工具调用
json
{
  "tool_calls": [{
    "name": "get_indicators",
    "arguments": {"symbol": "AAPL", "indicator": "rsi", "curr_date": "2024-11-19"}
  }]
}
  1. LangGraph执行: 自动调用工具并返回结果

Q5: 为什么要有消息清理节点?

答案: 防止Context过长

问题: 工具调用会产生大量消息

Message 1: Analyst "I need RSI data"
Message 2: ToolCall get_indicators(...)
Message 3: ToolMessage {"2024-11-01": 45.2, ...}
Message 4: Analyst "I need MACD data"
Message 5: ToolCall get_indicators(...)
Message 6: ToolMessage {"2024-11-01": 120.5, ...}
...
Message 20: Analyst final report

解决: 清理中间过程,只保留最终报告

清理后:
Message 1: Market Analyst final report

好处:

  • 节省Token成本
  • 提高后续Agent的理解效率
  • 避免超出LLM的Context限制

📝 本节小结

通过本节,你应该掌握:

✅ TradingAgent的4个工具节点分组策略 ✅ 11个工具函数的功能和用途 ✅ ReAct模式的完整循环流程 ✅ LLM如何选择和调用工具 ✅ 工具调用的最佳实践 ✅ 消息清理的必要性

关键洞察:

  • 工具是Agent的"手和眼",决定了Agent的能力边界
  • ReAct模式让LLM可以像人类一样"边思考边行动"
  • 合理的工具分组可以显著提升系统性能

上一节: [10.3 State状态管理](./10.3 State.md)

下一节: [10.5 Analyst 研究员辩论](./10.5 Analyst.md)

返回目录: [10.0 本章介绍](./10.0 Introduction.md)

基于 MIT 许可证发布。内容版权归作者所有。