Co-creation-projects/czxgg0630-ProductAnalysisAgent/ProductAnalysis_PlanSolveAgent.ipynb
基于Hello Agents框架的智能化竞品分析系统
- 自动收集竞品信息
- 多维度对比分析
- 生成专业报告
# 安装依赖
!pip install -q hello-agents[all]
# 导入必要的库
from hello_agents import SimpleAgent, HelloAgentsLLM
from hello_agents.tools import Tool, ToolParameter
from hello_agents.tools.builtin.search_tool import SearchTool
from typing import Dict, Any, List
import os
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:8800' # 你的代理地址
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
# 版本: v2.0 - 2026-04-09
# 更新说明: DataProcessorTool 和 ReportGeneratorTool 现在真正处理数据,不再返回固定字符串
class CompetitiveInfoSearchTool(Tool):
"""竞品信息搜索工具 - 使用真实搜索API"""
def __init__(self):
super().__init__(
name="competitive_info_search",
description="搜索指定竞品的产品信息、功能特性、定价策略等"
)
# 初始化内置搜索工具,使用 Tavily 后端
self.search = SearchTool(backend="tavily")
def get_parameters(self) -> List[ToolParameter]:
"""获取工具参数定义"""
return [
ToolParameter(
name="product_name",
type="string",
description="要搜索的竞品名称",
required=True
)
]
def run(self, parameters: Dict[str, Any]) -> str:
"""执行工具,使用真实搜索"""
product_name = parameters.get("product_name", "")
print(f"🔍 正在搜索 {product_name} 的竞品信息...")
# 使用真实搜索API
try:
search_query = f"{product_name} 产品功能介绍 定价 优缺点 2024"
result = self.search.run({
"query": search_query,
"max_results": 5
})
# 格式化搜索结果
return f"""
【{product_name} 搜索结果】
{result}
"""
except Exception as e:
print(f"⚠️ 搜索失败: {e},使用备用数据")
# 如果搜索失败,返回提示信息
return f"""
【{product_name} 信息】
- 搜索遇到问题,请检查网络或API配置
- 产品名称: {product_name}
- 建议手动补充信息
"""
class DataProcessorTool(Tool):
"""
数据处理工具 v2.0 - 真正解析搜索返回的文本,提取结构化信息
更新日志:
- v2.0 (2026-04-09): 实现真正的数据解析,使用正则提取关键信息
- v1.0: 返回固定字符串(PoC阶段)
"""
def __init__(self):
super().__init__(
name="data_processor",
description="将原始竞品数据清洗并构建对比矩阵,提取结构化信息"
)
def get_parameters(self) -> List[ToolParameter]:
"""获取工具参数定义"""
return [
ToolParameter(
name="raw_data",
type="string",
description="原始收集的竞品数据(搜索返回的文本)",
required=True
)
]
def run(self, parameters: Dict[str, Any]) -> str:
"""
执行数据处理 - 真正解析搜索文本,提取结构化信息
提取字段:
- 产品名称
- 产品定位
- 核心功能 (列表)
- 定价策略
- 主要优势
- 主要劣势
"""
import json
import re
raw_data = parameters.get("raw_data", "")
print("📊 正在解析并结构化数据...")
# 初始化结构化数据结构
structured_data = {
"产品名称": "",
"产品定位": "",
"核心功能": [],
"定价策略": "",
"主要优势": "",
"主要劣势": "",
"原始摘要": raw_data[:500] + "..." if len(raw_data) > 500 else raw_data
}
# 提取产品名称(从标题中提取)
name_match = re.search(r'【(.+?) 搜索', raw_data)
if name_match:
structured_data["产品名称"] = name_match.group(1)
# 提取产品定位(查找包含"定位"、"是"等关键词的句子)
position_patterns = [
r'(?:是|作为|定位为)[一个款]*(\S{2,20}?)(?:工具|软件|平台|应用)',
r'(?:用于|专为)(.+?)(?:设计|打造|提供)',
]
for pattern in position_patterns:
match = re.search(pattern, raw_data)
if match:
structured_data["产品定位"] = match.group(1).strip()
break
# 提取核心功能(查找列表、逗号分隔的功能描述)
function_patterns = [
r'功能[::](.+?)(?:\n|定价|价格|$)',
r'支持(.+?)(?:等功能|等特性)',
r'(?:包括|包含)(.+?)(?:等功能)',
]
for pattern in function_patterns:
match = re.search(pattern, raw_data)
if match:
functions_text = match.group(1)
# 分割功能列表
functions = [f.strip() for f in re.split(r'[,,、\/]', functions_text) if len(f.strip()) > 1 and len(f.strip()) < 20]
structured_data["核心功能"] = functions[:6] # 最多取6个
break
# 提取定价策略
price_patterns = [
r'(?:定价|价格|费用)[::](.+?)(?:\n|元|\$|USD)',
r'(免费|付费|订阅|一次性购买)',
r'(\d+\.?\d*\s*(?:元|\$|USD|美元)/(?:月|年|用户))',
]
for pattern in price_patterns:
match = re.search(pattern, raw_data, re.IGNORECASE)
if match:
structured_data["定价策略"] = match.group(1).strip()
break
# 提取优势和劣势(查找包含"优势"、"优点"、"缺点"、"不足"等的句子)
advantage_patterns = [
r'(?:优势|优点|特色)[::](.+?)(?:\n|劣势|缺点|不足|$)',
]
for pattern in advantage_patterns:
match = re.search(pattern, raw_data)
if match:
structured_data["主要优势"] = match.group(1).strip()[:100]
break
disadvantage_patterns = [
r'(?:劣势|缺点|不足|局限)[::](.+?)(?:\n|优势|总结|$)',
]
for pattern in disadvantage_patterns:
match = re.search(pattern, raw_data)
if match:
structured_data["主要劣势"] = match.group(1).strip()[:100]
break
# 如果没有提取到,从文本中智能推断
if not structured_data["主要优势"] and "优势" in raw_data:
# 查找"优势"后面的一句话
match = re.search(r'优势[是为:]+(.+?)[。\.\n]', raw_data)
if match:
structured_data["主要优势"] = match.group(1).strip()[:100]
if not structured_data["主要劣势"] and ("劣势" in raw_data or "缺点" in raw_data):
match = re.search(r'(?:劣势|缺点)[是为:]+(.+?)[。\.\n]', raw_data)
if match:
structured_data["主要劣势"] = match.group(1).strip()[:100]
print(f"✅ 结构化完成: {structured_data['产品名称'] or '未知产品'}")
print(f" - 提取功能: {len(structured_data['核心功能'])} 项")
# 返回 JSON 格式的结构化数据
return json.dumps(structured_data, ensure_ascii=False, indent=2)
class ReportGeneratorTool(Tool):
"""
报告生成工具 v2.0 - 基于结构化数据生成报告
更新日志:
- v2.0 (2026-04-09): 基于真实结构化数据生成报告,支持多产品对比
- v1.0: 返回固定字符串(PoC阶段)
"""
def __init__(self):
super().__init__(
name="report_generator",
description="基于结构化竞品数据生成专业的Markdown格式分析报告"
)
def get_parameters(self) -> List[ToolParameter]:
"""获取工具参数定义"""
return [
ToolParameter(
name="analysis_data",
type="string",
description="结构化后的竞品数据(JSON格式)",
required=True
)
]
def run(self, parameters: Dict[str, Any]) -> str:
"""
基于结构化数据生成专业的竞品分析报告
支持:
- 单个产品详情报告
- 多个产品对比报告
"""
import json
analysis_data = parameters.get("analysis_data", "")
print("📝 正在基于结构化数据生成报告...")
# 解析输入数据(支持多个产品的JSON数组或单个产品)
try:
# 尝试解析为JSON
if isinstance(analysis_data, str):
data = json.loads(analysis_data)
else:
data = analysis_data
# 如果是单个产品,转换为列表
if isinstance(data, dict):
products = [data]
elif isinstance(data, list):
products = data
else:
products = []
except json.JSONDecodeError:
# 如果不是JSON,当作原始文本处理
print("⚠️ 输入数据不是JSON格式,生成简化报告")
return self._generate_simple_report(analysis_data)
if not products:
return "# 竞品分析报告\n\n暂无有效数据。"
# 生成完整的对比报告
report_lines = []
report_lines.append("# 竞品分析报告")
report_lines.append("")
report_lines.append("## 一、执行摘要")
report_lines.append("")
report_lines.append(f"本次分析共涉及 **{len(products)}** 款产品,")
product_names = [p.get('产品名称', '未知') for p in products if p.get('产品名称')]
if product_names:
report_lines.append(f"包括:{', '.join(product_names)}。")
report_lines.append("")
report_lines.append("---")
report_lines.append("")
# 二、产品详情
report_lines.append("## 二、产品详情")
report_lines.append("")
for i, product in enumerate(products, 1):
name = product.get('产品名称', f'产品{i}')
report_lines.append(f"### {i}. {name}")
report_lines.append("")
if product.get('产品定位'):
report_lines.append(f"**产品定位**: {product['产品定位']}")
report_lines.append("")
if product.get('核心功能'):
report_lines.append("**核心功能**:")
for func in product['核心功能']:
report_lines.append(f"- {func}")
report_lines.append("")
if product.get('定价策略'):
report_lines.append(f"**定价策略**: {product['定价策略']}")
report_lines.append("")
if product.get('主要优势'):
report_lines.append(f"**主要优势**: {product['主要优势']}")
report_lines.append("")
if product.get('主要劣势'):
report_lines.append(f"**主要劣势**: {product['主要劣势']}")
report_lines.append("")
# 三、对比矩阵(仅当有多个产品时)
if len(products) > 1:
report_lines.append("---")
report_lines.append("")
report_lines.append("## 三、对比矩阵")
report_lines.append("")
report_lines.append("| 维度 | " + " | ".join([p.get('产品名称', f'产品{i}') for i, p in enumerate(products, 1)]) + " |")
report_lines.append("|------|" + "|".join(["------"] * len(products)) + "|")
# 产品定位对比
positions = [p.get('产品定位', '-')[:20] for p in products]
report_lines.append("| 定位 | " + " | ".join(positions) + " |")
# 定价对比
prices = [p.get('定价策略', '-')[:15] for p in products]
report_lines.append("| 定价 | " + " | ".join(prices) + " |")
# 功能数量对比
func_counts = [str(len(p.get('核心功能', []))) + " 项" for p in products]
report_lines.append("| 功能数 | " + " | ".join(func_counts) + " |")
report_lines.append("")
# 四、总结与建议
report_lines.append("---")
report_lines.append("")
report_lines.append("## 四、总结与建议")
report_lines.append("")
report_lines.append("基于以上分析,建议:")
report_lines.append("")
# 生成简单建议
if len(products) == 1:
report_lines.append(f"- {products[0].get('产品名称', '该产品')}适合需要{products[0].get('产品定位', '相关功能')}的用户")
else:
# 找出功能最多的
max_func_product = max(products, key=lambda x: len(x.get('核心功能', [])))
report_lines.append(f"- **功能最丰富**: {max_func_product.get('产品名称')},提供 {len(max_func_product.get('核心功能', []))} 项核心功能")
# 找出免费/开源的
free_products = [p for p in products if '免费' in p.get('定价策略', '') or '开源' in p.get('定价策略', '')]
if free_products:
report_lines.append(f"- **预算有限首选**: {', '.join([p.get('产品名称') for p in free_products])}")
report_lines.append("")
report_lines.append("---")
report_lines.append("")
report_lines.append("*报告生成时间: 基于真实结构化数据*")
return "\n".join(report_lines)
def _generate_simple_report(self, raw_text: str) -> str:
"""当无法解析JSON时,生成简化报告"""
return f"""# 竞品分析报告
## 执行摘要
基于收集的原始数据生成报告。
## 原始数据摘要
{raw_text[:800]}...
---
*注: 数据解析遇到问题,以上为原始数据摘要。*
"""
print("✅ 三个核心工具已定义完成(v2.0 - 真正数据处理)")
print(" 1. CompetitiveInfoSearchTool - 竞品信息搜索(真实API)")
print(" 2. DataProcessorTool - 数据处理(真正解析结构化)✨ v2.0")
print(" 3. ReportGeneratorTool - 报告生成(基于结构化数据)✨ v2.0")
# 创建LLM
llm = HelloAgentsLLM()
# 定义系统提示词 - Plan-and-Solve 范式
SYSTEM_PROMPT = """你是一位专业的竞品分析专家,擅长通过系统化的方法对多个竞品进行深度分析。
【Plan-and-Solve 工作范式】
你的任务执行流程:
1. 首先分析用户需求,提取需要对比的竞品名称
2. 制定详细的分析计划,将任务分解为可执行的步骤
3. 按计划逐步执行,收集每个竞品的信息
4. 对收集的数据进行处理和结构化
5. 生成完整的竞品对比分析报告
【分析维度要求】
- 产品定位与目标用户
- 核心功能对比
- 定价策略分析
- 优势与劣势(SWOT)
- 战略建议
【输出要求】
- 报告必须结构化、专业
- 对比必须基于实际数据
- 建议必须具体可行"""
# 定义 Plan-and-Solve 自定义提示词模板
CUSTOM_PROMPTS = {
"planner": """
你是一位顶级的AI规划专家。你的任务是将竞品分析需求分解成一个由多个简单步骤组成的行动计划。
请确保计划中的每个步骤都是一个独立的、可执行的子任务,并且严格按照逻辑顺序排列。
对于竞品分析任务,典型的计划步骤包括:
1. 提取并确认需要分析的竞品名称
2. 搜索第一个竞品的基本信息
3. 搜索第二个竞品的基本信息
4. (如有更多竞品继续搜索)
5. 整理并对比各竞品的核心特性
6. 生成SWOT分析
7. 输出完整的竞品分析报告
问题: {question}
请严格按照以下格式输出你的计划:
```python
["步骤1", "步骤2", "步骤3", ...]
""", "executor": """ 你是一位顶级的AI执行专家。你的任务是严格按照给定的计划,一步步地完成竞品分析。
你将收到原始问题、完整的计划、以及到目前为止已经完成的步骤和结果。 请你专注于解决"当前步骤",并输出该步骤的执行结果。
{question}
{plan}
{history}
{current_step}
请执行当前步骤并输出结果: """ }
from hello_agents.agents.plan_solve_agent import PlanAndSolveAgent
agent = PlanAndSolveAgent( name="Plan-and-Solve 竞品分析专家", llm=llm, system_prompt=SYSTEM_PROMPT, custom_prompts=CUSTOM_PROMPTS )
print("✅ PlanAndSolveAgent 已初始化") print("✅ 采用 Plan-and-Solve 范式:先规划分析步骤,再逐步执行") print("✅ 自定义提示词模板已配置")
# 第5部分: 功能演示
```python
# 示例1: 基础竞品分析
import time
from datetime import datetime
print("="*70)
print("📊 示例1: Plan-and-Solve 竞品深度分析")
print("="*70)
target_products = ["盒马", "叮咚买菜", "盒马会员商店"]
print(f"\n🎯 分析目标: {', '.join(target_products)}")
print(f"⏰ 开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("-"*70)
# 执行分析
start_time = time.time()
result = agent.run(
f"请对以下竞品进行深度对比分析: {', '.join(target_products)}。"
)
elapsed_time = time.time() - start_time
# 美观的输出排版
print("\n" + "="*70)
print("📋 分析完成")
print("="*70)
print(f"⏱️ 总耗时: {elapsed_time:.2f} 秒")
print(f"📝 报告长度: {len(result)} 字符")
print("-"*70)
# 显示报告摘要
print("\n📄 报告预览(前1000字符):")
print("-"*70)
print(result[:1000] + "..." if len(result) > 1000 else result)
print("-"*70)
# 保存结果到文件
output_filename = f"outputs/demo_result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md"
with open(output_filename, 'w', encoding='utf-8') as f:
f.write(f"# 竞品分析报告\n\n")
f.write(f"**分析时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
f.write(f"**分析产品**: {', '.join(target_products)}\n\n")
f.write(f"**分析耗时**: {elapsed_time:.2f} 秒\n\n")
f.write("---\n\n")
f.write(result)
print(f"\n💾 报告已保存至: {output_filename}")
print("="*70)
这两种 Agent 分别代表了目前主流的两种任务编排范式:
| 评估维度 | SimpleAgent (单轨/ReAct 范式) | Plan-and-Solve Agent (分层规划范式) |
|---|---|---|
| 逻辑复杂度承载 | ★★★ 中低。依赖大模型自身的注意力机制去同时兼顾"理解上下文"、"选择工具"和"决定下一步"。分析 3 个竞品可能还行,分析 10 个极易产生幻觉或遗漏。 | ★★★★★ 极高。将"思考"与"行动"物理隔离。Planner 专职生成 DAG(有向无环图)或线性步骤,Executor 只负责低头干活,大幅降低认知负荷。 |
| 上下文消耗 (Token) | ★★ 较高。所有的中间结果、报错信息、循环思考(Thought/Action/Observation)都在同一个上下文窗口里堆叠,容易触发 Context Window 限制。 | ★★★★★ 极优。可以通过 Context Engineering 做到"阅后即焚"或"摘要传递",每次 Executor 执行时只需要当前步骤和历史摘要,Token 消耗可控。 |
| 可控性与干预度 | ★★ 黑盒状态。一旦运行,很难在中间阻断并修改它的思考路径。 | ★★★★★ 白盒状态。业务侧可以在 Planner 输出计划后,加入人工确认环节(Human-in-the-loop),修改计划后再交由 Executor 执行。 |
从运行日志来看,两个 Agent 在稳定性上暴露出了实际开发中最常遇到的痛点:
1. SimpleAgent 的超时崩溃 (Network/Timeout Issues)
问题现象:在 ProductAnalysis_SimpleAgent.ipynb 中,程序可能因网络问题抛出异常,堆栈信息指向底层的网络读取 (ssl.py: read) 和 httpx。
诊断:这不是 Agent 逻辑本身的 Bug,而是同步阻塞导致的假死。在使用单轨 Agent 时,无论是 LLM 的 API 响应过慢,还是 Tavily 搜索接口被墙/限流,整个主线程都会挂起。
改进建议:在生产环境中,任何外部 API 调用(LLM 或 Search)都必须配置强硬的 timeout 策略,并结合重试机制(如 tenacity 库),防止单点网络波动拖垮整个系统。
2. 工具层的防御性编程 (Defensive Programming)
现状:CompetitiveInfoSearchTool 中写了 try...except Exception as e 并返回了备用文本。这是一个非常优秀的工程习惯!
价值:它保证了即使搜索失败,Agent 也能拿到一个明确的"失败反馈",而不是直接崩溃,这让大模型有机会决定是否要重试或跳过。
目前已经成功接入了真实的搜索引擎 (Tavily),打通了与外部世界的连接,但整个工作流(Pipeline)在数据处理层面目前仍处于 PoC (概念验证) 阶段。
1. 搜索工具 (SearchTool)
亮点:能够动态拼接 {product_name} 产品功能介绍 定价 优缺点 2024,这比单纯输入产品名能获得更高质量的搜索片段(Snippets)。
2. 数据处理与报告工具的"假动作" (Dummy Tools)
现状:DataProcessorTool 和 ReportGeneratorTool 目前的 run 方法只是直接 return 了一段写死的字符串(例如 return "# 竞品分析报告\n\n## 执行摘要\n分析完成...")。Agent 确实调用了这些工具,但实际上并没有对 Tavily 抓取回来的真实数据做任何结构化清洗,最后的长篇报告依然是 LLM 绕过工具直接"脑补"出来的。
改进建议:这两个工具内部也需要实例化 LLM 客户端。DataProcessorTool 应该将原始文本喂给 LLM,并通过 JSON Schema 强制其输出结构化的字典(包含定位、功能、价格等);ReportGeneratorTool 则应该接收这些干净的 JSON 数据,套用 Markdown 模板进行渲染。
| Agent 类型 | 评级 | 评价 |
|---|---|---|
| SimpleAgent | ★★☆☆☆ (2/5) | 适合作为基础调试脚手架,但不具备复杂业务的工程韧性。 |
| Plan-and-Solve Agent | ★★★★☆ (4/5) | 架构思路极佳,具备向企业级 AI 产品演进的潜力,逻辑拆解清晰。 |
在实际测试中,我们得到以下性能数据:
测试时间: 2026-04-09
说明: 性能数据受网络状况、API 响应速度、分析复杂度等因素影响。
本次项目基于 Hello Agents 框架成功实现了 Plan-and-Solve 范式 的竞品分析 Agent,主要成果包括:
1. 核心工具链搭建(v2.0 - 真正数据处理流程)
2. PlanAndSolveAgent 实现
PlanAndSolveAgent 类构建分层规划范式的竞品分析 Agent3. 数据流闭环(Data Pipeline)
用户输入 → 制定计划 → 搜索工具(原始文本) → 数据处理工具(JSON结构化) → 报告生成工具(Markdown报告) → 输出结果
4. 双范式对比分析
5. 工程实践
outputs/ 目录DataProcessorTool v2.0 改进:
ReportGeneratorTool v2.0 改进:
通过本项目,验证了 PlanAndSolveAgent 相比 SimpleAgent 的显著优势:
| 优势维度 | 具体表现 |
|---|---|
| 过程透明度 | 每步执行过程清晰可见,便于调试和审计 |
| 可控性 | 可在 Planner 生成计划后加入人工确认(Human-in-the-loop) |
| 复杂任务处理 | 将复杂分析拆解为可管理的步骤,降低认知负荷 |
| 上下文管理 | 通过"阅后即焚"和摘要传递,有效控制 Token 消耗 |
| 数据流闭环 | 工具链真正运转,从原始数据到结构化报告全流程自动化 |
| 挑战 | 解决方案 | 状态 |
|---|---|---|
| 工具导入错误 | 从 BaseTool 改为 Tool,并实现 get_parameters() 方法 | ✅ 已解决 |
| 参数传递为空 | 优化 Planner 和执行器的提示词模板,明确参数格式 | ✅ 已解决 |
| 网络 SSL 错误 | 增加异常处理和备用返回,配置代理 | ✅ 已解决 |
| 工具"假动作" | 升级 v2.0:DataProcessorTool 真正解析文本,ReportGeneratorTool 基于结构化数据生成报告 | ✅ 已解决 |
| 数据解析准确性 | 使用多种正则模式匹配,支持容错和降级 | ✅ 已解决 |
1. Plan-and-Solve 设计要点
2. 工具链设计原则
3. 正则表达式在数据提取中的应用
短期改进(1-2 周)
中期改进(1 个月)
长期改进(3 个月)
| 场景 | 推荐范式 | 理由 |
|---|---|---|
| 快速原型验证 | SimpleAgent | 代码简洁,响应快速 |
| 复杂竞品分析(5+ 产品) | PlanAndSolveAgent | 步骤清晰,不易遗漏 |
| 需要人工干预 | PlanAndSolveAgent | 白盒执行,可暂停修改 |
| 生产环境部署 | PlanAndSolveAgent | 更好的可观测性和容错性 |
| 教学演示 | 两者都适用 | SimpleAgent 简单,PlanAndSolve 展示完整流程 |
| 真正数据处理流程 | PlanAndSolveAgent | 工具链完整闭环,数据可追溯 |
PlanAndSolveAgent + 工具链 v2.0 在本次项目中的表现:
版本里程碑:
推荐后续行动:
项目完成时间: 2026-04-09
版本: v2.0 - 真正数据处理流程
作者: czxgg0630
GitHub: https://github.com/czxgg0630