From acb2262bb8da661cdf85d9fef46924bce96413c6 Mon Sep 17 00:00:00 2001 From: "jinli.yl" Date: Tue, 15 Jul 2025 19:48:52 +0800 Subject: [PATCH] up readme --- cookbook/financial_agent/__init__.py | 1 - cookbook/financial_agent/example.py | 81 ---- cookbook/financial_agent/financial_agent.py | 345 ------------------ cookbook/financial_agent/knowledge_graph.py | 238 ------------ cookbook/financial_agent/schema.py | 46 --- cookbook/financial_agent/test/README.md | 171 --------- cookbook/financial_agent/test/__init__.py | 0 cookbook/financial_agent/test/prompt.py | 77 ---- .../test/test_financial_agent.py | 294 --------------- cookbook/financial_agent/test/test_simple.py | 191 ---------- 10 files changed, 1444 deletions(-) delete mode 100644 cookbook/financial_agent/__init__.py delete mode 100644 cookbook/financial_agent/example.py delete mode 100644 cookbook/financial_agent/financial_agent.py delete mode 100644 cookbook/financial_agent/knowledge_graph.py delete mode 100644 cookbook/financial_agent/schema.py delete mode 100644 cookbook/financial_agent/test/README.md delete mode 100644 cookbook/financial_agent/test/__init__.py delete mode 100644 cookbook/financial_agent/test/prompt.py delete mode 100644 cookbook/financial_agent/test/test_financial_agent.py delete mode 100644 cookbook/financial_agent/test/test_simple.py diff --git a/cookbook/financial_agent/__init__.py b/cookbook/financial_agent/__init__.py deleted file mode 100644 index 3dc1f76b..00000000 --- a/cookbook/financial_agent/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.1.0" diff --git a/cookbook/financial_agent/example.py b/cookbook/financial_agent/example.py deleted file mode 100644 index d9f7ff4a..00000000 --- a/cookbook/financial_agent/example.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python3 -""" -金融分析师Agent使用示例 -""" - -import sys -from experiencemaker.utils.util_function import load_env_keys - -load_env_keys("../../.env") -# 添加项目根目录到Python路径 -# project_root = Path(__file__).parent.parent -# sys.path.insert(0, str(project_root)) -sys.path.append(".") -from financial_agent import FinancialAgent - - - - -def main(): - """主函数""" - # 加载环境变量 - load_env_keys() - - # 初始实体列表 - init_entity_list = [ - "美元债务", - "美债利率", - "美元指数", - "工业金属银、铜、铝", - "黄金", - "稳定币", - "石油", - "能源", - "军工", - "海运", - ] - - # 创建金融分析师Agent - agent = FinancialAgent( - model_name="qwen-max-2025-01-25", - verbose=True - ) - - # 执行知识图谱构建 - output_file = "金融知识图谱.jsonl" - stats = agent.execute( - init_entity_list=init_entity_list, - dump_file_path=output_file, - max_iter=5, # 减少迭代次数用于演示 - search_strategy="mixed" - ) - - print("\n" + "=" * 50) - print("执行结果统计:") - print(f"总实体数: {stats['total_entities']}") - print(f"总关系数: {stats['total_relations']}") - print(f"新增关系数: {stats['new_relations']}") - print(f"迭代次数: {stats['iterations']}") - print("=" * 50) - - # 查询示例 - print("\n查询示例:") - query_results = agent.query_knowledge_graph("美元") - print(f"包含'美元'的关系数量: {len(query_results)}") - - # 获取特定实体的关系 - print("\n美元指数的关系:") - usd_relations = agent.get_entity_relations("美元指数") - for i, relation in enumerate(usd_relations[:3]): # 只显示前3个 - print(f"{i + 1}. {relation}") - - # 生成可视化图表 - print("\n生成可视化图表...") - agent.visualize("financial_knowledge_graph.html") - - print(f"\n知识图谱已保存到: {output_file}") - print("可视化图表已保存到: financial_knowledge_graph.html") - - -if __name__ == "__main__": - main() diff --git a/cookbook/financial_agent/financial_agent.py b/cookbook/financial_agent/financial_agent.py deleted file mode 100644 index e76e2173..00000000 --- a/cookbook/financial_agent/financial_agent.py +++ /dev/null @@ -1,345 +0,0 @@ -""" -金融分析师Agent主类 -""" - -import datetime -import json -import re -from typing import List, Dict - -from loguru import logger - -from experiencemaker.enumeration.role import Role -from experiencemaker.model import OpenAICompatibleBaseLLM -from experiencemaker.schema.trajectory import Message -from experiencemaker.tool import CodeTool, DashscopeSearchTool -from knowledge_graph import KnowledgeGraphBuilder -from schema import FinancialRelation - - -class FinancialAgent: - """金融分析师Agent""" - - def __init__(self, model_name: str = "qwen-max-2025-01-25", verbose: bool = True): - """初始化金融分析师Agent""" - self.llm = OpenAICompatibleBaseLLM(model_name=model_name) - self.search_tool = DashscopeSearchTool() - self.code_tool = CodeTool() - self.tools = [self.search_tool, self.code_tool] - self.verbose = verbose - - # 知识图谱构建器 - self.knowledge_graph = KnowledgeGraphBuilder() - - # 系统提示词 - self.system_prompt = """你是一个专业的金融分析师,专门负责分析金融市场中各种实体之间的逻辑关系。 - -你的任务: -1. 通过搜索发现多个实体之间的逻辑关系 -2. 不断补充新的实体到实体列表中 -3. 将多跳的实体关系拆分成多个单跳的实体关系 - -实体关系格式: -{ - "input_entities": ["实体1", "实体2"], - "output_entities": ["实体3", "实体4"], - "relation": "正向、负向、中性", - "reasoning": "实体1和实体2是通过什么样的逻辑影响到实体3和实体4", - "source": "来源", - "confidence": "置信度(0-1)", - "timestamp": "时间戳", -} - -请确保: -- 关系描述准确、具体 -- 置信度合理评估 -- 来源信息完整 -- 时间戳格式:YYYY-MM-DD HH:MM:SS -- 多跳关系要拆分成多个单跳关系 -""" - - def _search_entity_relations(self, entity_list: List[str]) -> List[FinancialRelation]: - """搜索实体间的关系""" - if self.verbose: - logger.info(f"Searching relations for entities: {entity_list[:5]}...") - - # 构建搜索查询 - entities_str = "、".join(entity_list[:5]) # 限制实体数量 - query = f"请分析以下金融实体之间的关系:{entities_str}。请详细说明它们之间的逻辑关系,包括正向、负向或中性的影响关系。" - - # 使用搜索工具 - try: - search_result = self.search_tool.execute(query=query) - if self.verbose: - logger.info(f"Search completed, result length: {len(str(search_result))}") - except Exception as e: - logger.error(f"Search failed: {e}") - return [] - - # 使用LLM分析搜索结果并提取关系 - analysis_prompt = f""" -基于以下搜索结果,请分析金融实体之间的关系: - -搜索结果: -{search_result[:10000]} - -当前实体列表: -{entity_list[:100]} - -请提取出实体间的关系,格式如下(JSON格式): -{{ - "relations": [ - {{ - "input_entities": ["实体1", "实体2"], - "output_entities": ["实体3", "实体4"], - "relation": "正向/负向/中性", - "reasoning": "详细的分析逻辑", - "source": "信息来源", - "confidence": 0.8 - }} - ], - "new_entities": ["新实体1", "新实体2"] -}} - -请确保: -1. 关系描述准确具体 -2. 置信度在0-1之间 -3. 如果发现新的相关实体,请添加到new_entities中 -4. 只返回JSON格式,不要其他内容 -5. 多跳关系要拆分成多个单跳关系 -""" - - messages = [ - Message(role=Role.SYSTEM, content=self.system_prompt), - Message(role=Role.USER, content=analysis_prompt) - ] - - try: - response = self.llm.chat(messages, self.tools) - - # 尝试解析JSON响应 - content = str(response.content) - json_match = re.search(r'\{.*\}', content, re.DOTALL) - if json_match: - data = json.loads(json_match.group()) - - relations = [] - for rel_data in data.get("relations", []): - try: - relation = FinancialRelation( - input_entities=rel_data["input_entities"], - output_entities=rel_data["output_entities"], - relation=rel_data["relation"], - reasoning=rel_data["reasoning"], - source=rel_data["source"], - confidence=rel_data["confidence"], - timestamp=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - ) - relations.append(relation) - except Exception as e: - logger.exception(f"Failed to create relation: {e}, data: {rel_data}") - - # 添加新实体 - for new_entity in data.get("new_entities", []): - self.knowledge_graph.entities.add(new_entity) - - if self.verbose: - logger.info( - f"Extracted {len(relations)} relations and {len(data.get('new_entities', []))} new entities") - - return relations - except Exception as e: - logger.error(f"Error parsing LLM response: {e}") - if self.verbose: - logger.error(f"Response content: {response.content if 'response' in locals() else 'No response'}") - - return [] - - def _search_specific_entity_pairs(self, entity_pairs: List[List[str]]) -> List[FinancialRelation]: - """搜索特定实体对之间的关系""" - all_relations = [] - - for pair in entity_pairs: - if len(pair) < 2: - continue - - query = f"请分析{pair[0]}和{pair[1]}之间的金融关系,包括它们如何相互影响,以及对其他金融实体的影响。" - - try: - search_result = self.search_tool.execute(query=query) - - analysis_prompt = f""" -基于搜索结果,分析{pair[0]}和{pair[1]}之间的关系: - -搜索结果: -{search_result[:800] if isinstance(search_result, str) else str(search_result)[:800]} - -请提取关系,格式如下(JSON格式): -{{ - "relations": [ - {{ - "input_entities": ["{pair[0]}", "{pair[1]}"], - "output_entities": ["影响实体1", "影响实体2"], - "relation": "正向/负向/中性", - "reasoning": "详细分析", - "source": "来源", - "confidence": 0.8 - }} - ] -}} - -只返回JSON格式。 -""" - - messages = [ - Message(role=Role.SYSTEM, content=self.system_prompt), - Message(role=Role.USER, content=analysis_prompt) - ] - - response = self.llm.chat(messages, self.tools) - - content = str(response.content) - json_match = re.search(r'\{.*\}', content, re.DOTALL) - if json_match: - data = json.loads(json_match.group()) - - for rel_data in data.get("relations", []): - try: - relation = FinancialRelation( - input_entities=rel_data["input_entities"], - output_entities=rel_data["output_entities"], - relation=rel_data["relation"], - reasoning=rel_data["reasoning"], - source=rel_data["source"], - confidence=rel_data["confidence"], - timestamp=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - ) - all_relations.append(relation) - except Exception as e: - logger.warning(f"Failed to create relation: {e}") - - except Exception as e: - logger.error(f"Error searching pair {pair}: {e}") - - return all_relations - - def _generate_entity_pairs(self, entity_list: List[str], max_pairs: int = 10) -> List[List[str]]: - """生成实体对用于搜索""" - pairs = [] - entities = list(entity_list) - - # 生成所有可能的二元组合 - for i in range(len(entities)): - for j in range(i + 1, len(entities)): - pairs.append([entities[i], entities[j]]) - if len(pairs) >= max_pairs: - break - if len(pairs) >= max_pairs: - break - - return pairs - - def execute(self, init_entity_list: List[str], dump_file_path: str, max_iter: int = 10, - search_strategy: str = "mixed") -> Dict: - """ - 执行金融知识图谱构建 - - Args: - init_entity_list: 初始实体列表 - dump_file_path: 输出文件路径 - max_iter: 最大迭代次数 - search_strategy: 搜索策略 ("general", "pairs", "mixed") - - Returns: - 执行结果统计 - """ - logger.info(f"Starting financial knowledge graph construction with {len(init_entity_list)} initial entities") - - # 1. 初始化实体列表 - self.knowledge_graph.entities = set(init_entity_list) - - # 2. 加载历史数据 - self.knowledge_graph.load_from_jsonl(dump_file_path) - - # 3. 迭代搜索 - iteration = 0 - total_new_relations = 0 - - while iteration < max_iter: - iteration += 1 - logger.info(f"Iteration {iteration}/{max_iter}") - - new_relations_count = 0 - - if search_strategy in ["general", "mixed"]: - # 通用搜索 - entity_list = list(self.knowledge_graph.entities) - relations = self._search_entity_relations(entity_list) - - for relation in relations: - if self.knowledge_graph.add_relation(relation): - new_relations_count += 1 - - if search_strategy in ["pairs", "mixed"] and iteration % 2 == 0: - # 实体对搜索 - entity_list = list(self.knowledge_graph.entities) - entity_pairs = self._generate_entity_pairs(entity_list, max_pairs=5) - relations = self._search_specific_entity_pairs(entity_pairs) - - for relation in relations: - if self.knowledge_graph.add_relation(relation): - new_relations_count += 1 - - total_new_relations += new_relations_count - logger.info(f"Added {new_relations_count} new relations in iteration {iteration}") - - # 检查是否还有新的关系可以添加 - if new_relations_count == 0: - logger.info("No new relations found, stopping iteration") - break - - # 保存中间结果 - self.knowledge_graph.export_to_jsonl(dump_file_path) - - # 4. 保存最终结果 - self.knowledge_graph.export_to_jsonl(dump_file_path) - - # 5. 生成统计信息 - stats = self.knowledge_graph.get_entity_statistics() - stats["new_relations"] = total_new_relations - stats["iterations"] = iteration - - logger.info(f"Financial knowledge graph construction completed!") - logger.info(f"Total entities: {stats['total_entities']}") - logger.info(f"Total relations: {stats['total_relations']}") - logger.info(f"New relations added: {total_new_relations}") - - return stats - - def query_knowledge_graph(self, query: str) -> List[FinancialRelation]: - """查询知识图谱""" - # 简单的关键词匹配查询 - query_lower = query.lower() - results = [] - - for relation in self.knowledge_graph.relations: - # 检查输入实体、输出实体、推理过程是否包含查询关键词 - all_text = " ".join([ - " ".join(relation.input_entities), - " ".join(relation.output_entities), - relation.reasoning, - relation.source - ]).lower() - - if query_lower in all_text: - results.append(relation) - - return results - - def get_entity_relations(self, entity: str) -> List[FinancialRelation]: - """获取特定实体的所有关系""" - return self.knowledge_graph.find_related_entities(entity) - - def visualize(self, output_path: str = "financial_knowledge_graph.html"): - """生成可视化图表""" - self.knowledge_graph.visualize_graph(output_path) diff --git a/cookbook/financial_agent/knowledge_graph.py b/cookbook/financial_agent/knowledge_graph.py deleted file mode 100644 index b38ec916..00000000 --- a/cookbook/financial_agent/knowledge_graph.py +++ /dev/null @@ -1,238 +0,0 @@ -""" -金融知识图谱构建器 -""" - -import hashlib -import json -import os -from typing import List, Set, Dict - -from schema import FinancialRelation - - -class KnowledgeGraphBuilder: - """金融知识图谱构建器""" - - def __init__(self): - """初始化知识图谱构建器""" - self.entities: Set[str] = set() - self.relations: List[FinancialRelation] = [] - self.relation_hashes: Set[str] = set() # 用于去重 - self.entity_aliases: Dict[str, Set[str]] = {} # 实体别名映射 - - def _generate_relation_hash(self, relation: FinancialRelation) -> str: - """生成关系的哈希值用于去重""" - content = f"{sorted(relation.input_entities)}_{sorted(relation.output_entities)}_{relation.relation}_{relation.reasoning}" - return hashlib.md5(content.encode()).hexdigest() - - def add_relation(self, relation: FinancialRelation) -> bool: - """添加关系,如果重复则返回False""" - relation_hash = self._generate_relation_hash(relation) - if relation_hash in self.relation_hashes: - return False - - self.relation_hashes.add(relation_hash) - self.relations.append(relation) - - # 添加新实体到实体列表 - for entity in relation.input_entities + relation.output_entities: - self.entities.add(entity) - - return True - - def add_entity_alias(self, main_entity: str, aliases: List[str]): - """添加实体别名""" - if main_entity not in self.entity_aliases: - self.entity_aliases[main_entity] = set() - - for alias in aliases: - self.entity_aliases[main_entity].add(alias) - self.entities.add(alias) - - def get_entity_aliases(self, entity: str) -> Set[str]: - """获取实体的所有别名""" - for main_entity, aliases in self.entity_aliases.items(): - if entity in aliases or entity == main_entity: - return aliases | {main_entity} - return {entity} - - def find_related_entities(self, entity: str, max_depth: int = 2) -> List[FinancialRelation]: - """查找与指定实体相关的所有关系""" - related_relations = [] - visited_entities = set() - entities_to_check = {entity} - - for depth in range(max_depth): - current_entities = entities_to_check.copy() - entities_to_check.clear() - - for relation in self.relations: - # 检查关系是否涉及当前实体 - relation_entities = set(relation.input_entities + relation.output_entities) - if relation_entities & current_entities: - related_relations.append(relation) - # 添加新的实体到下一轮检查 - entities_to_check.update(relation_entities - visited_entities) - - visited_entities.update(current_entities) - - if not entities_to_check: - break - - return related_relations - - def get_entity_statistics(self) -> Dict: - """获取实体统计信息""" - return { - "total_entities": len(self.entities), - "total_relations": len(self.relations), - "unique_relations": len(self.relation_hashes), - "entity_aliases": len(self.entity_aliases) - } - - def export_to_jsonl(self, file_path: str): - """导出到JSONL文件""" - # os.makedirs(os.path.dirname(file_path), exist_ok=True) - with open(file_path, 'w', encoding='utf-8') as f: - for relation in self.relations: - f.write(json.dumps(relation.to_dict(), ensure_ascii=False) + '\n') - - def load_from_jsonl(self, file_path: str): - """从JSONL文件加载""" - if not os.path.exists(file_path): - return - - with open(file_path, 'r', encoding='utf-8') as f: - for line in f: - line = line.strip() - if line: - data = json.loads(line) - relation = FinancialRelation.from_dict(data) - self.add_relation(relation) - - def export_to_networkx_format(self) -> Dict: - """导出为NetworkX可用的格式""" - nodes = [] - edges = [] - - # 添加节点 - for entity in self.entities: - nodes.append({ - "id": entity, - "label": entity, - "type": "entity" - }) - - # 添加边 - for i, relation in enumerate(self.relations): - for input_entity in relation.input_entities: - for output_entity in relation.output_entities: - edges.append({ - "source": input_entity, - "target": output_entity, - "relation": relation.relation, - "reasoning": relation.reasoning, - "confidence": relation.confidence, - "source_info": relation.source, - "timestamp": relation.timestamp - }) - - return { - "nodes": nodes, - "edges": edges, - "metadata": self.get_entity_statistics() - } - - def visualize_graph(self, output_path: str = "financial_knowledge_graph.html"): - """生成可视化图表(HTML格式)""" - try: - import plotly.graph_objects as go - import networkx as nx - - # 创建NetworkX图 - G = nx.DiGraph() - - # 添加节点 - for entity in self.entities: - G.add_node(entity) - - # 添加边 - for relation in self.relations: - for input_entity in relation.input_entities: - for output_entity in relation.output_entities: - G.add_edge( - input_entity, - output_entity, - relation=relation.relation, - confidence=relation.confidence - ) - - # 使用spring布局 - pos = nx.spring_layout(G, k=1, iterations=50) - - # 创建边轨迹 - edge_x = [] - edge_y = [] - edge_text = [] - - for edge in G.edges(data=True): - x0, y0 = pos[edge[0]] - x1, y1 = pos[edge[1]] - edge_x.extend([x0, x1, None]) - edge_y.extend([y0, y1, None]) - edge_text.append(f"{edge[0]} → {edge[1]} ({edge[2]['relation']})") - - edge_trace = go.Scatter( - x=edge_x, y=edge_y, - line=dict(width=0.5, color='#888'), - hoverinfo='text', - text=edge_text, - mode='lines') - - # 创建节点轨迹 - node_x = [] - node_y = [] - node_text = [] - - for node in G.nodes(): - x, y = pos[node] - node_x.append(x) - node_y.append(y) - node_text.append(node) - - node_trace = go.Scatter( - x=node_x, y=node_y, - mode='markers+text', - hoverinfo='text', - text=node_text, - textposition="top center", - marker=dict( - showscale=True, - colorscale='YlGnBu', - size=10, - color=[], - line_width=2)) - - # 设置节点颜色 - node_adjacency_list = [] - for node in G.nodes(): - node_adjacency_list.append(len(list(G.neighbors(node)))) - node_trace.marker.color = node_adjacency_list - - # 创建图形 - fig = go.Figure(data=[edge_trace, node_trace], - layout=go.Layout( - title='金融知识图谱', - showlegend=False, - hovermode='closest', - margin=dict(b=20, l=5, r=5, t=40), - xaxis=dict(showgrid=False, zeroline=False, showticklabels=False), - yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)) - ) - - fig.write_html(output_path) - print(f"知识图谱已保存到: {output_path}") - - except ImportError: - print("需要安装 plotly 和 networkx 来生成可视化图表") - print("运行: pip install plotly networkx") diff --git a/cookbook/financial_agent/schema.py b/cookbook/financial_agent/schema.py deleted file mode 100644 index b5234551..00000000 --- a/cookbook/financial_agent/schema.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -金融实体关系的schema定义 -""" - -from dataclasses import dataclass -from typing import List - - -@dataclass -class FinancialRelation: - """金融实体关系的数据结构""" - input_entities: List[str] - output_entities: List[str] - relation: str # "正向", "负向", "中性" - reasoning: str - source: str - confidence: float - timestamp: str - - def __post_init__(self): - """初始化后的验证""" - if not isinstance(self.confidence, (int, float)) or not 0 <= self.confidence <= 1: - raise ValueError("confidence must be a float between 0 and 1") - - if self.relation not in ["正向", "负向", "中性"]: - raise ValueError("relation must be one of: 正向, 负向, 中性") - - def to_dict(self): - """转换为字典格式""" - return { - "input_entities": self.input_entities, - "output_entities": self.output_entities, - "relation": self.relation, - "reasoning": self.reasoning, - "source": self.source, - "confidence": self.confidence, - "timestamp": self.timestamp - } - - @classmethod - def from_dict(cls, data: dict): - """从字典创建实例""" - return cls(**data) - - def __str__(self): - return f"{self.input_entities} -> {self.output_entities} ({self.relation}, 置信度: {self.confidence})" diff --git a/cookbook/financial_agent/test/README.md b/cookbook/financial_agent/test/README.md deleted file mode 100644 index e689bbca..00000000 --- a/cookbook/financial_agent/test/README.md +++ /dev/null @@ -1,171 +0,0 @@ -# 金融分析师Agent - -一个智能的金融分析师Agent,能够通过搜索和分析构建金融知识图谱,发现金融实体之间的逻辑关系。 - -## 功能特性 - -- 🔍 **智能搜索**: 使用大模型和搜索工具发现金融实体关系 -- 🧠 **知识图谱构建**: 自动构建和更新金融知识图谱 -- 🔗 **关系分析**: 分析正向、负向、中性关系 -- 📊 **可视化**: 生成交互式知识图谱可视化 -- 🔄 **迭代优化**: 支持多轮迭代,不断丰富知识图谱 -- 📝 **数据导出**: 支持JSONL格式导出 - -## 安装 - -1. 克隆项目并安装依赖: -```bash -pip install -r financial_agent/requirements.txt -``` - -2. 设置环境变量: -```bash -export OPENAI_API_KEY="your_openai_api_key" -export OPENAI_BASE_URL="your_openai_base_url" -export DASHSCOPE_API_KEY="your_dashscope_api_key" -``` - -## 快速开始 - -### 基本使用 - -```python -from financial_agent import FinancialAgent - -# 创建Agent -agent = FinancialAgent(model_name="qwen-max-2025-01-25") - -# 初始实体列表 -init_entities = ["美元债务", "美债利率", "美元指数", "黄金", "石油"] - -# 执行知识图谱构建 -stats = agent.execute( - init_entity_list=init_entities, - dump_file_path="金融知识图谱.jsonl", - max_iter=10, - search_strategy="mixed" -) - -print(f"构建完成!总实体数: {stats['total_entities']}, 总关系数: {stats['total_relations']}") -``` - -### 运行示例 - -```bash -python financial_agent/example.py -``` - -## 核心组件 - -### FinancialAgent - -主要的金融分析师Agent类,负责: -- 搜索和分析金融实体关系 -- 构建和更新知识图谱 -- 提供查询接口 - -### FinancialRelation - -金融实体关系的数据结构: -```python -{ - "input_entities": ["实体1", "实体2"], - "output_entities": ["实体3", "实体4"], - "relation": "正向、负向、中性", - "reasoning": "关系推理过程", - "source": "信息来源", - "confidence": "置信度(0-1)", - "timestamp": "时间戳" -} -``` - -### KnowledgeGraphBuilder - -知识图谱构建器,提供: -- 关系去重和验证 -- 实体别名管理 -- 图谱查询和统计 -- 可视化生成 - -## 配置选项 - -### 搜索策略 - -- `"general"`: 通用搜索,分析多个实体间的关系 -- `"pairs"`: 实体对搜索,专注于特定实体对 -- `"mixed"`: 混合策略,结合两种方法 - -### 模型配置 - -支持多种大模型: -- `qwen-max-2025-01-25` -- `qwen3-32b` -- 其他OpenAI兼容模型 - -## 输出格式 - -### JSONL文件格式 - -每行一个JSON对象,包含完整的金融关系信息: - -```jsonl -{"input_entities": ["美债利率", "美元债务"], "output_entities": ["美元指数"], "relation": "负向", "reasoning": "美债利率上升和美元债务增加会导致美元走弱", "source": "金融分析报告", "confidence": 0.85, "timestamp": "2024-01-15 10:30:00"} -{"input_entities": ["石油价格", "美元指数"], "output_entities": ["通胀预期"], "relation": "正向", "reasoning": "石油价格上涨和美元走弱会推高通胀预期", "source": "经济分析", "confidence": 0.78, "timestamp": "2024-01-15 10:31:00"} -``` - -### 可视化输出 - -生成交互式HTML图表,支持: -- 节点拖拽 -- 关系查看 -- 缩放和平移 -- 悬停信息显示 - -## API参考 - -### FinancialAgent.execute() - -执行知识图谱构建: - -```python -def execute(self, init_entity_list: List[str], dump_file_path: str, - max_iter: int = 10, search_strategy: str = "mixed") -> Dict -``` - -### FinancialAgent.query_knowledge_graph() - -查询知识图谱: - -```python -def query_knowledge_graph(self, query: str) -> List[FinancialRelation] -``` - -### FinancialAgent.get_entity_relations() - -获取特定实体的关系: - -```python -def get_entity_relations(self, entity: str) -> List[FinancialRelation] -``` - -## 使用场景 - -1. **金融研究**: 自动发现金融市场中的实体关系 -2. **投资分析**: 分析投资标的之间的相互影响 -3. **风险管理**: 识别风险传导路径 -4. **政策分析**: 分析政策对市场的影响机制 - -## 注意事项 - -1. **API限制**: 注意搜索API的调用频率限制 -2. **数据质量**: 建议对生成的关系进行人工验证 -3. **成本控制**: 大模型调用会产生费用,注意控制迭代次数 -4. **环境配置**: 确保正确配置API密钥和基础URL - -## 贡献 - -欢迎提交Issue和Pull Request来改进项目! - -## 许可证 - -MIT License \ No newline at end of file diff --git a/cookbook/financial_agent/test/__init__.py b/cookbook/financial_agent/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cookbook/financial_agent/test/prompt.py b/cookbook/financial_agent/test/prompt.py deleted file mode 100644 index c0e34e75..00000000 --- a/cookbook/financial_agent/test/prompt.py +++ /dev/null @@ -1,77 +0,0 @@ - -entity_list = [ - "美元债务", - "美债利率", - "美元指数", - "工业金属银、铜、铝", - "黄金", - "稳定币", - "石油", - "能源", - "军工", - "海运", -] - - - -""" -现在初始状态你有以下的实体列表: - -美元债务 -美债利率 -美元指数 -工业金属银、铜、铝 -黄金 -稳定币 -石油 -能源 -军工 -海运 - - -你有一个大模型调用的Client -from experiencemaker.model import OpenAICompatibleBaseLLM - -你有两个工具: -1. 代码执行工具 -from experiencemaker.tool import CodeTool -2. web search工具 -from experiencemaker.tool import DashscopeSearchTool - -请你根据以上内容,设计一个金融分析师的Agent,并给出Agent的代码。 -Agent的任务: -1. 通过不断的搜索,发现多个实体之间的逻辑关系,例如“美债利率高”、“美元债务高”会让美元变弱,从而导致“美元指数”变弱。 -2. 同时通过不断的搜索,不断的补充新的实体到**实体列表**。 -3. 多跳的实体关系,可以拆成多个单跳的实体关系。 - -实体关系的schema如下: -{ - "input_entities": ["实体1", "实体2"], - "output_entities": ["实体3", "实体4"], - "relation": "正向、负向、中性", - "reasoning": "实体1和实体2是通过什么样的逻辑影响到实体3和实体4", - "source": "来源", - "confidence": "置信度", - "timestamp": "时间戳", -} - -最后这些schema的list会变成一个jsonl文件,文件名是: -金融知识图谱.jsonl - -```python -class FinAgent(object): - - def execute(self, init_entity_list: list[str], dump_file_path: str, max_iter: int = 100): - # init_entity_list 是初始的实体列表 - # dump_file_path 是最终的jsonl文件路径 - # max_iter 是最大迭代次数 - - # 1. 初始化实体列表 - # 2. 读取dump_file_path中历史的金融知识图谱(如果有),加载到内容 - # 2. 迭代搜索,不断更新已有的知识图谱,不断补充新的知识图谱,不断增加新的实体(和已有的语义去重) - # 3. 迭代到max_iter次,或者没有新的知识图谱可以补充,则停止迭代, 保存到dump_file_path中 - pass -``` - -帮忙把整个project写一下 -""" \ No newline at end of file diff --git a/cookbook/financial_agent/test/test_financial_agent.py b/cookbook/financial_agent/test/test_financial_agent.py deleted file mode 100644 index f5dd997e..00000000 --- a/cookbook/financial_agent/test/test_financial_agent.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/env python3 -""" -金融分析师Agent测试脚本 -""" - -import json -import os -import datetime -from typing import List, Dict, Set -from dataclasses import dataclass -import hashlib -import re - -from experiencemaker.model import OpenAICompatibleBaseLLM -from experiencemaker.tool import CodeTool, DashscopeSearchTool -from experiencemaker.schema.trajectory import Message -from experiencemaker.enumeration.role import Role - - -@dataclass -class FinancialRelation: - """金融实体关系的数据结构""" - input_entities: List[str] - output_entities: List[str] - relation: str # "正向", "负向", "中性" - reasoning: str - source: str - confidence: float - timestamp: str - - -class FinancialAgent: - """金融分析师Agent""" - - def __init__(self, model_name: str = "qwen-max-2025-01-25"): - """初始化金融分析师Agent""" - self.llm = OpenAICompatibleBaseLLM(model_name=model_name) - self.search_tool = DashscopeSearchTool() - self.code_tool = CodeTool() - self.tools = [self.search_tool, self.code_tool] - - # 存储实体和关系 - self.entities: Set[str] = set() - self.relations: List[FinancialRelation] = [] - self.relation_hashes: Set[str] = set() # 用于去重 - - # 系统提示词 - self.system_prompt = """你是一个专业的金融分析师,专门负责分析金融市场中各种实体之间的逻辑关系。 - -你的任务: -1. 通过搜索发现多个实体之间的逻辑关系 -2. 不断补充新的实体到实体列表中 -3. 将多跳的实体关系拆分成多个单跳的实体关系 - -实体关系格式: -{ - "input_entities": ["实体1", "实体2"], - "output_entities": ["实体3", "实体4"], - "relation": "正向、负向、中性", - "reasoning": "实体1和实体2是通过什么样的逻辑影响到实体3和实体4", - "source": "来源", - "confidence": "置信度(0-1)", - "timestamp": "时间戳", -} - -请确保: -- 关系描述准确、具体 -- 置信度合理评估 -- 来源信息完整 -- 时间戳格式:YYYY-MM-DD HH:MM:SS -""" - - def _generate_relation_hash(self, relation: FinancialRelation) -> str: - """生成关系的哈希值用于去重""" - content = f"{sorted(relation.input_entities)}_{sorted(relation.output_entities)}_{relation.relation}_{relation.reasoning}" - return hashlib.md5(content.encode()).hexdigest() - - def _add_relation(self, relation: FinancialRelation) -> bool: - """添加关系,如果重复则返回False""" - relation_hash = self._generate_relation_hash(relation) - if relation_hash in self.relation_hashes: - return False - - self.relation_hashes.add(relation_hash) - self.relations.append(relation) - - # 添加新实体到实体列表 - for entity in relation.input_entities + relation.output_entities: - self.entities.add(entity) - - return True - - def _load_existing_relations(self, file_path: str): - """加载已存在的关系数据""" - if not os.path.exists(file_path): - return - - try: - with open(file_path, 'r', encoding='utf-8') as f: - for line in f: - line = line.strip() - if line: - data = json.loads(line) - relation = FinancialRelation(**data) - self._add_relation(relation) - print(f"Loaded {len(self.relations)} existing relations from {file_path}") - except Exception as e: - print(f"Error loading existing relations: {e}") - - def _save_relations(self, file_path: str): - """保存关系到文件""" - os.makedirs(os.path.dirname(file_path), exist_ok=True) - with open(file_path, 'w', encoding='utf-8') as f: - for relation in self.relations: - f.write(json.dumps(relation.__dict__, ensure_ascii=False) + '\n') - print(f"Saved {len(self.relations)} relations to {file_path}") - - def _search_entity_relations(self, entity_list: List[str]) -> List[FinancialRelation]: - """搜索实体间的关系""" - # 构建搜索查询 - entities_str = "、".join(entity_list[:5]) # 限制实体数量 - query = f"请分析以下金融实体之间的关系:{entities_str}。请详细说明它们之间的逻辑关系,包括正向、负向或中性的影响关系。" - - # 使用搜索工具 - search_result = self.search_tool.execute(query=query) - - # 使用LLM分析搜索结果并提取关系 - analysis_prompt = f""" -基于以下搜索结果,请分析金融实体之间的关系: - -搜索结果: -{search_result[:1000] if isinstance(search_result, str) else str(search_result)[:1000]} # 限制长度 - -当前实体列表: -{entity_list[:10]} # 限制显示数量 - -请提取出实体间的关系,格式如下(JSON格式): -{{ - "relations": [ - {{ - "input_entities": ["实体1", "实体2"], - "output_entities": ["实体3", "实体4"], - "relation": "正向/负向/中性", - "reasoning": "详细的分析逻辑", - "source": "信息来源", - "confidence": 0.8 - }} - ], - "new_entities": ["新实体1", "新实体2"] -}} - -请确保: -1. 关系描述准确具体 -2. 置信度在0-1之间 -3. 如果发现新的相关实体,请添加到new_entities中 -4. 只返回JSON格式,不要其他内容 -""" - - messages = [ - Message(role=Role.SYSTEM, content=self.system_prompt), - Message(role=Role.USER, content=analysis_prompt) - ] - - response = self.llm._chat(messages, self.tools) - - try: - # 尝试解析JSON响应 - content = str(response.content) - json_match = re.search(r'\{.*\}', content, re.DOTALL) - if json_match: - data = json.loads(json_match.group()) - - relations = [] - for rel_data in data.get("relations", []): - relation = FinancialRelation( - input_entities=rel_data["input_entities"], - output_entities=rel_data["output_entities"], - relation=rel_data["relation"], - reasoning=rel_data["reasoning"], - source=rel_data["source"], - confidence=rel_data["confidence"], - timestamp=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - ) - relations.append(relation) - - # 添加新实体 - for new_entity in data.get("new_entities", []): - self.entities.add(new_entity) - - return relations - except Exception as e: - print(f"Error parsing LLM response: {e}") - print(f"Response content: {response.content}") - - return [] - - def execute(self, init_entity_list: List[str], dump_file_path: str, max_iter: int = 5): - """ - 执行金融知识图谱构建 - - Args: - init_entity_list: 初始实体列表 - dump_file_path: 输出文件路径 - max_iter: 最大迭代次数 - """ - print(f"Starting financial knowledge graph construction with {len(init_entity_list)} initial entities") - - # 1. 初始化实体列表 - self.entities = set(init_entity_list) - - # 2. 加载历史数据 - self._load_existing_relations(dump_file_path) - - # 3. 迭代搜索 - iteration = 0 - new_relations_count = 0 - - while iteration < max_iter: - iteration += 1 - print(f"Iteration {iteration}/{max_iter}") - - # 搜索实体间关系 - entity_list = list(self.entities) - relations = self._search_entity_relations(entity_list) - - # 添加新关系 - added_count = 0 - for relation in relations: - if self._add_relation(relation): - added_count += 1 - - new_relations_count += added_count - print(f"Added {added_count} new relations in iteration {iteration}") - - # 检查是否还有新的关系可以添加 - if added_count == 0: - print("No new relations found, stopping iteration") - break - - # 保存中间结果 - if iteration % 2 == 0: - self._save_relations(dump_file_path) - - # 4. 保存最终结果 - self._save_relations(dump_file_path) - - print(f"Financial knowledge graph construction completed!") - print(f"Total entities: {len(self.entities)}") - print(f"Total relations: {len(self.relations)}") - print(f"New relations added: {new_relations_count}") - - return { - "entities": list(self.entities), - "relations": [rel.__dict__ for rel in self.relations], - "total_entities": len(self.entities), - "total_relations": len(self.relations), - "new_relations": new_relations_count - } - - -def main(): - """主函数""" - from experiencemaker.utils.util_function import load_env_keys - - # 加载环境变量 - load_env_keys() - - # 初始实体列表 - init_entities = [ - "美元债务", - "美债利率", - "美元指数", - "黄金", - "石油", - ] - - # 创建Agent并执行 - agent = FinancialAgent() - result = agent.execute( - init_entity_list=init_entities, - dump_file_path="test/金融知识图谱.jsonl", - max_iter=3 # 减少迭代次数用于测试 - ) - - print("=" * 50) - print("构建完成!") - print(f"总实体数: {result['total_entities']}") - print(f"总关系数: {result['total_relations']}") - print(f"新增关系数: {result['new_relations']}") - print("=" * 50) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/cookbook/financial_agent/test/test_simple.py b/cookbook/financial_agent/test/test_simple.py deleted file mode 100644 index 87a9d759..00000000 --- a/cookbook/financial_agent/test/test_simple.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python3 -""" -简化的金融Agent测试脚本 -""" - -import os -import sys -import json -from pathlib import Path - -# 添加项目根目录到Python路径 -project_root = Path(__file__).parent.parent -sys.path.insert(0, str(project_root)) - -# 模拟环境变量(如果没有设置的话) -if not os.getenv("OPENAI_API_KEY"): - os.environ["OPENAI_API_KEY"] = "test_key" -if not os.getenv("OPENAI_BASE_URL"): - os.environ["OPENAI_BASE_URL"] = "http://localhost:8000/v1" -if not os.getenv("DASHSCOPE_API_KEY"): - os.environ["DASHSCOPE_API_KEY"] = "test_key" - -from financial_agent.schema import FinancialRelation -from financial_agent.knowledge_graph import KnowledgeGraphBuilder - - -def test_schema(): - """测试FinancialRelation schema""" - print("测试FinancialRelation schema...") - - # 创建测试关系 - relation = FinancialRelation( - input_entities=["美债利率", "美元债务"], - output_entities=["美元指数"], - relation="负向", - reasoning="美债利率上升和美元债务增加会导致美元走弱", - source="金融分析报告", - confidence=0.85, - timestamp="2024-01-15 10:30:00" - ) - - print(f"创建的关系: {relation}") - print(f"转换为字典: {relation.to_dict()}") - - # 测试验证 - try: - invalid_relation = FinancialRelation( - input_entities=["实体1"], - output_entities=["实体2"], - relation="无效关系", # 应该报错 - reasoning="测试", - source="测试", - confidence=0.5, - timestamp="2024-01-15 10:30:00" - ) - except ValueError as e: - print(f"验证错误(预期): {e}") - - print("Schema测试通过!\n") - - -def test_knowledge_graph(): - """测试KnowledgeGraphBuilder""" - print("测试KnowledgeGraphBuilder...") - - # 创建知识图谱 - kg = KnowledgeGraphBuilder() - - # 添加关系 - relation1 = FinancialRelation( - input_entities=["美债利率", "美元债务"], - output_entities=["美元指数"], - relation="负向", - reasoning="美债利率上升和美元债务增加会导致美元走弱", - source="金融分析报告", - confidence=0.85, - timestamp="2024-01-15 10:30:00" - ) - - relation2 = FinancialRelation( - input_entities=["石油价格", "美元指数"], - output_entities=["通胀预期"], - relation="正向", - reasoning="石油价格上涨和美元走弱会推高通胀预期", - source="经济分析", - confidence=0.78, - timestamp="2024-01-15 10:31:00" - ) - - # 添加关系 - kg.add_relation(relation1) - kg.add_relation(relation2) - - # 添加实体别名 - kg.add_entity_alias("美元指数", ["USD Index", "DXY"]) - - # 测试统计 - stats = kg.get_entity_statistics() - print(f"知识图谱统计: {stats}") - - # 测试查询 - usd_relations = kg.find_related_entities("美元指数") - print(f"美元指数相关关系数量: {len(usd_relations)}") - - # 测试导出 - test_file = "test_knowledge_graph.jsonl" - kg.export_to_jsonl(test_file) - print(f"知识图谱已导出到: {test_file}") - - # 测试加载 - new_kg = KnowledgeGraphBuilder() - new_kg.load_from_jsonl(test_file) - print(f"加载后的实体数量: {len(new_kg.entities)}") - - # 清理测试文件 - if os.path.exists(test_file): - os.remove(test_file) - - print("KnowledgeGraphBuilder测试通过!\n") - - -def test_mock_agent(): - """测试模拟的Agent(不调用真实API)""" - print("测试模拟Agent...") - - # 创建模拟的搜索结果 - mock_search_result = """ - 根据最新金融分析,美债利率上升和美元债务增加会对美元指数产生负面影响。 - 当美债利率上升时,投资者会要求更高的收益率,这可能导致美元走弱。 - 同时,美元债务的增加也会增加市场对美元贬值的担忧。 - """ - - # 模拟LLM响应 - mock_llm_response = { - "relations": [ - { - "input_entities": ["美债利率", "美元债务"], - "output_entities": ["美元指数"], - "relation": "负向", - "reasoning": "美债利率上升和美元债务增加会导致美元走弱", - "source": "金融分析报告", - "confidence": 0.85 - } - ], - "new_entities": ["美联储政策", "市场情绪"] - } - - print(f"模拟搜索结果: {mock_search_result[:100]}...") - print(f"模拟LLM响应: {json.dumps(mock_llm_response, ensure_ascii=False, indent=2)}") - - # 测试关系创建 - try: - relation = FinancialRelation( - input_entities=mock_llm_response["relations"][0]["input_entities"], - output_entities=mock_llm_response["relations"][0]["output_entities"], - relation=mock_llm_response["relations"][0]["relation"], - reasoning=mock_llm_response["relations"][0]["reasoning"], - source=mock_llm_response["relations"][0]["source"], - confidence=mock_llm_response["relations"][0]["confidence"], - timestamp="2024-01-15 10:30:00" - ) - print(f"成功创建关系: {relation}") - except Exception as e: - print(f"创建关系失败: {e}") - - print("模拟Agent测试通过!\n") - - -def main(): - """主测试函数""" - print("=" * 50) - print("金融Agent测试开始") - print("=" * 50) - - try: - test_schema() - test_knowledge_graph() - test_mock_agent() - - print("=" * 50) - print("所有测试通过!") - print("=" * 50) - - except Exception as e: - print(f"测试失败: {e}") - import traceback - traceback.print_exc() - - -if __name__ == "__main__": - main() \ No newline at end of file