前言
在上一篇文章中,我们通过一个单体 Agent 实现了 Polymarket 与新闻数据的初步整合。然而,在面对波诡云谲的实战环境时,单体架构暴露出逻辑深度不足、工具调用混乱以及无法自我纠错等硬伤。
为了解决这些痛点,本文将对原有的单智能体系统进行“手术级”重构,引入 LangGraph 实现多角色协同,让你的投研工具从一个“全栈练习生”进化为一支“专业特种部队”。
一、 单体 Agent 的“天花板”
在尝试构建 Web3 投研助手时,我们通常会给一个 Agent 塞进所有工具(搜索、行情、链上监测)。但在实战中,单体 Agent 常遇到三大痛点:
- 注意力涣散:Prompt 过长导致模型忽略了关键的风险提示。
- 逻辑闭环难:模型容易“自嗨”,拿到错误数据后直接开始推演,没有纠错机制。
- 工具冲突:当工具超过 5 个时,模型选择工具的准确率大幅下降。
二、 多智能体的降维打击:分工与制衡
多智能体架构的核心在于 “角色拆解” 。通过将任务分给不同的 Agent,我们模拟了一个专业投研机构的运作流水线:
1. 角色纯粹化(Specialization)
- 研究员 (Researcher) :只负责“找”。它精通各种搜索语法,不带主观偏见地搬运事实。
- 分析师 (Analyst) :只负责“想”。它不直接查数据,而是对研究员提供的情报进行逻辑建模、胜率计算和风险评估。
2. 动态博弈与纠错(Feedback Loop)
这是多智能体最强的地方:分析师可以“打回重做” 。如果研究员提供的情报不足以支撑结论,分析师会提出具体的补查要求,迫使系统进入循环直到逻辑闭环。
三、 实战:基于 LangGraph 的投研工作流
1.工具
import * as dotenv from "dotenv";
dotenv.config();
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { TavilySearch } from "@langchain/tavily";
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";
const agent = new HttpsProxyAgent("http://127.0.0.1:3067");
const axiosConfig = { timeout: 15000, httpsAgent: agent, proxy: false };
const searchInstance = new TavilySearch({ maxResults: 5 }); // 增加搜索结果以捕捉更多套利新闻
// 1. 搜索工具:强化了对套利/异动新闻的搜索描述
export const financialSearchTool = tool(
async (input) => {
const query = typeof input === 'string' ? input : (input.query || JSON.stringify(input));
console.log(`\n[🔍 正在执行深度搜索]: ${query}`);
try {
const res = await searchInstance.invoke(query);
return JSON.stringify(res);
} catch (e: any) { return `搜索暂时不可用`; }
},
{
name: "financial_market_search",
description: "搜索最新新闻背景、套利机会或市场异动。请输入关键词对象,例如:{\"query\": \"Polymarket arbitrage opportunities\"}",
schema: z.object({ query: z.string() }),
}
);
// 2. 深度优化的行情工具 (Arbitrage & Trend Ready)
export const marketDataTool = tool(
async (input) => {
const userInput = typeof input === 'string' ? input : (input.marketName || JSON.stringify(input));
// 1. 行业语义映射表 (包含最新套利关键词)
const mapping: Record<string, string[]> = {
"原油": ["oil", "crude", "energy", "brent", "wti", "gasoline"],
"油价": ["oil", "crude", "energy"],
"中东": ["israel", "gaza", "iran", "lebanon", "middle east", "hezbollah", "conflict"],
"战争": ["war", "military", "strike", "attack", "invasion"],
"选举": ["election", "trump", "vance", "harris", "walz"],
"停火": ["ceasefire", "truce", "peace"],
"核": ["nuclear", "facility", "isfahan"],
"海峡": ["hormuz", "strait", "shipping"],
"宏观": ["fed", "rate cut", "inflation", "recession", "gdp"],
"套利": ["arbitrage", "mispricing", "spread", "basis", "hedging"],
"时间差": ["deadline", "expiry", "until", "before", "sooner"],
"美联储": ["powell", "fomc", "interest rate", "basis points"],
"加密货币": ["bitcoin", "etf", "ethereum", "solana", "ath"]
};
// 获取基础关注词
let baseTerms = [userInput.toLowerCase()];
for (const [zh, ens] of Object.entries(mapping)) {
if (userInput.includes(zh)) {
baseTerms = ens;
break;
}
}
// --- 优化点:自动生成组合搜索词(捕获最新最热) ---
const hotSuffixes = ["ceasefire", "rate cut", "ath", "trump", "deadline"];
const focusTerms = [
...baseTerms,
...baseTerms.flatMap(term => hotSuffixes.map(suffix => `${term} ${suffix}`))
];
console.log(`\n[📊 正在扫描套利机会]: 领域 -> ${userInput} | 衍生词数 -> ${focusTerms.length}`);
try {
// 获取全平台最火的 50 个市场
const url = `https://gamma-api.polymarket.com/markets?active=true&closed=false&limit=50`;
const res = await axios.get(url, axiosConfig);
if (!res.data || res.data.length === 0) return "Polymarket 暂无活跃市场。";
// 2. 精准过滤逻辑
const relevantMarkets = res.data.filter((m: any) => {
const title = m.question.toLowerCase();
// 必须包含正向词,剔除体育等干扰噪音
const hasFocus = focusTerms.some(term => title.includes(term));
const isNoise = ["nhl", "nba", "cup", "game", "soccer", "football"].some(noise => title.includes(noise));
return hasFocus && !isNoise;
});
if (relevantMarkets.length > 0) {
// 按相关度或题目排序,方便 Agent 对比相似市场寻找套利空间
const marketList = relevantMarkets.map((m: any) => ({
question: m.question,
price: m.lastTradePrice,
endDate: m.endDate
}));
return JSON.stringify({
status: "success",
count: marketList.length,
data: marketList,
hint: "请分析以上市场之间是否存在隐含概率冲突或定价偏差。"
});
}
return `[提示]:热门榜单中暂无直接对标 "${userInput}" 的套利交易对。`;
} catch (e: any) { return `行情接口异常: ${e.message}`; }
},
{
name: "get_realtime_market_data",
description: "获取 Polymarket 实时赔率与套利机会。支持组合搜索。",
schema: z.object({ marketName: z.string() }),
}
);
export const tools = [financialSearchTool, marketDataTool];
2.主程
以下是使用 LangChain 最新 LangGraph 框架实现的协作代码片段:
import { ChatOpenAI } from "@langchain/openai";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { StateGraph, MessagesAnnotation, END } from "@langchain/langgraph";
import { financialSearchTool } from "./tools"; // 复用你之前的工具
// 1. 定义两个不同的 LLM 实例(可以给分析师更高的 Temperature 来激发灵感)
const llm = new ChatOpenAI({
// 核心修复:显式指定 API Key 和 Base URL
apiKey: process.env.DEEPSEEK_API_KEY,
modelName: "deepseek-chat",
configuration: {
baseURL: process.env.DEEPSEEK_API_BASE_URL,
},
temperature: 0,
});
// 2. 定义角色逻辑
// 研究员节点:强迫它必须使用工具
async function researcherNode(state: typeof MessagesAnnotation.State) {
const systemMessage = {
role: "system",
content: "你是一名 Web3 研究员。你的任务是利用搜索工具获取关于特定事件的最新事实。获取事实后,直接将其传递给分析师,不要进行深度评论。",
};
const response = await llm.bindTools([financialSearchTool]).invoke([systemMessage, ...state.messages]);
return { messages: [response] };
}
// 分析师节点:负责逻辑推演
async function analystNode(state: typeof MessagesAnnotation.State) {
const systemMessage = {
role: "system",
content: "你是一名顶级高级分析师。你需要审查研究员提供的事实。如果事实太模糊,请要求研究员重新搜索;如果事实充足,请给出最终的套利分析报告。你的回复必须以 '【最终报告】' 开头。",
};
const response = await llm.invoke([systemMessage, ...state.messages]);
return { messages: [response] };
}
// 3. 定义路由逻辑:判断是该继续搜,还是该结束了
function shouldContinue(state: typeof MessagesAnnotation.State) {
const lastMessage = state.messages[state.messages.length - 1];
// 如果分析师说了“最终报告”,就结束
if (typeof lastMessage.content === "string" && lastMessage.content.includes("【最终报告】")) {
return END;
}
// 如果有工具调用,去执行工具
if (lastMessage.additional_kwargs.tool_calls) {
return "tools";
}
// 否则,让分析师看研究员的结果
return "analyst";
}
// 4. 构建工作流图 (Graph)
const workflow = new StateGraph(MessagesAnnotation)
.addNode("researcher", researcherNode)
.addNode("analyst", analystNode)
.addNode("tools", new ToolNode([financialSearchTool])) // 专门执行工具的节点
// 连线逻辑
.addEdge("__start__", "researcher") // 从研究员开始
.addEdge("tools", "researcher") // 工具执行完后回到研究员
.addConditionalEdges("researcher", shouldContinue) // 研究员做完后判断:调工具还是给分析师
.addConditionalEdges("analyst", shouldContinue); // 分析师做完后判断:结束还是打回重搜
// 5. 编译并运行
const app = workflow.compile();
async function runMultiAgent() {
const inputs = { messages: [{ role: "user", content: "分析以伊冲突对 Polymarket 原油预测价格的影响" }] };
const result = await app.invoke(inputs);
console.log("\n--- 协作过程结束 ---");
console.log(result.messages[result.messages.length - 1].content);
}
runMultiAgent();
为什么这套代码能跑赢单体模型?
- 状态可控:你可以清晰看到请求是在“搜索”阶段还是“审计”阶段。
- 递归深度:通过
recursionLimit限制,你可以防止 Agent 陷入死循环,同时保证了深度。 - 模型异构:你可以让研究员用便宜快速的 GPT-4o-mini,而让分析师用逻辑更强的 DeepSeek-V3 或 Claude 3.5。
结语
单体 AI 是工具,多智能体才是“数字员工”。在信息密度极高的 Web3 领域,学会如何编排一群 Agent 协作,将是开发者真正的竞争壁垒。