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专用为什么要分组?
- 职责明确: 每个Analyst只能调用与其职责相关的工具
- 提示词优化: LLM看到的工具列表更短、更相关
- 权限控制: 避免Analyst越权访问不该访问的数据
- 性能优化: 减少LLM的选择负担
代码实现
在 trading_graph.py 中:
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天)
返回示例:
{
"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: 当前日期
支持的指标 (部分列表):
# 趋势指标
- '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):
{
"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: 结束日期
数据来源:
搜索范围 = [
'wallstreetbets', # 散户聚集地
'stocks', # 股票讨论
'investing', # 投资策略
'SecurityAnalysis',# 证券分析
'Finance', # 金融综合
'Economics', # 经济学讨论
]返回示例:
{
"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
返回示例:
{
"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: 交易次数
返回示例:
{
"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 - 内部人交易
功能: 详细的内部人买卖记录
返回示例:
{
"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 - 关键财务指标
功能: 获取核心财务比率和指标
返回示例:
{
"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片段:
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的第一轮输出:
{
"tool_calls": [
{
"name": "get_stock_data",
"arguments": {"symbol": "AAPL", "curr_date": "2024-11-19", "lookback_days": 15}
}
]
}Conditional Logic判断:
def should_continue_market(state: AgentState):
last_message = state["messages"][-1]
if last_message.tool_calls:
return "tools_market" # 有工具调用,去执行工具
return "Msg Clear Market" # 无工具调用,输出报告工具节点执行:
# LangGraph自动调用工具并返回结果
tool_result = get_stock_data("AAPL", "2024-11-19", 15)
# 将结果添加到messages
state["messages"].append(ToolMessage(content=tool_result))LLM的第二轮输出:
{
"tool_calls": [
{
"name": "get_indicators",
"arguments": {"symbol": "AAPL", "indicator": "rsi", "curr_date": "2024-11-19"}
}
]
}...循环继续,直到LLM不再调用工具
LLM的最终输出:
## 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. 工具描述要清晰
@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. 返回格式要一致
✅ 好的返回:
{"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. 错误处理要友好
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_market | get_stock_data, get_indicators | 2 |
| tools_social | get_news | 1 |
| tools_news | get_news, get_global_news, get_insider_sentiment, get_insider_transactions | 4 |
| tools_fundamentals | get_fundamentals, get_balance_sheet, get_cashflow, get_income_statement | 4 |
总计: 11个工具函数 (去重后,因为get_news在两个节点中)
Q4: LLM如何判断调用哪个工具?
答案: 通过以下机制:
- System Prompt: 告诉LLM可用工具列表
You have access to: get_stock_data, get_indicators- Tool Schema: 每个工具的详细描述
{
"name": "get_indicators",
"description": "Calculate technical indicators...",
"parameters": {...}
}- LLM推理: 根据任务目标选择合适工具
任务: "分析RSI是否超买"
→ LLM推理: "需要RSI数据"
→ 选择工具: get_indicators
→ 参数: indicator="rsi"- Function Calling: LLM输出JSON格式的工具调用
{
"tool_calls": [{
"name": "get_indicators",
"arguments": {"symbol": "AAPL", "indicator": "rsi", "curr_date": "2024-11-19"}
}]
}- 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)