refactor: 步骤4智能体任务改为传递内容库ID
- 模板改为传递内容库数据ID列表而非内容摘要 - 智能体任务改为分析ID对应数据与产品的相关性和参数提取价值 - 智能体输出改为relevant_ids列表+分析说明 - 步骤3保存内容库时记录article_id - 步骤4根据智能体返回的ID从内容库获取实际内容 - 更新_fill_fields适配新的extracted_data格式
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
## 任务背景
|
||||
|
||||
### 内容库搜索结果
|
||||
以下是从内容库中搜索到的相关文章数据位置:
|
||||
### 内容库相关数据ID列表
|
||||
以下是从内容库中搜索到的相关文章数据ID:
|
||||
{{library_results}}
|
||||
|
||||
### 互联网搜索抓取内容
|
||||
以下是从互联网搜索并抓取的网页内容数据位置:
|
||||
### 互联网搜索已入库数据ID列表
|
||||
以下是从互联网搜索并已成功抓取入库的数据ID:
|
||||
{{internet_results}}
|
||||
|
||||
## 产品信息
|
||||
@@ -15,17 +15,39 @@
|
||||
|
||||
## 任务要求
|
||||
|
||||
请从上述搜索结果和抓取内容中,提取出与产品「{{product_name}}」直接相关的具体内容。
|
||||
请分析上述内容库ID对应的数据,判断哪些与产品「{{product_name}}」**直接相关**且**对提取产品参数有用**。
|
||||
|
||||
要求:
|
||||
1. 只提取与该产品直接相关的信息,排除其他无关产品的内容
|
||||
2. 提取的内容应包括但不限于:产品参数、规格、功能描述、发布信息、技术特点等
|
||||
3. 注明每条信息的来源(URL或文章标题)
|
||||
4. 如果某些信息在多个来源中都有提及,请综合整理
|
||||
5. 严格按照原始数据提取,不要编造或推测任何内容
|
||||
### 判断标准:
|
||||
1. **直接相关性**:内容必须明确提及该产品名称或其主要型号,排除仅提及相似产品或竞品的内容
|
||||
2. **参数提取价值**:内容应包含可用于填充产品字段的信息,如:
|
||||
- 产品规格参数(尺寸、重量、容量等)
|
||||
- 技术规格(性能指标、接口、兼容性等)
|
||||
- 功能特性
|
||||
- 发布信息(发布日期、价格等)
|
||||
- 其他结构化产品数据
|
||||
|
||||
请将提取结果以JSON格式输出,包含以下字段:
|
||||
- name: 产品名称
|
||||
- extracted_fields: 提取到的字段键值对
|
||||
- sources: 信息来源列表
|
||||
- confidence: 提取置信度(high/medium/low)
|
||||
### 输出要求:
|
||||
请以JSON格式输出分析结果,包含以下字段:
|
||||
```json
|
||||
{
|
||||
"relevant_ids": [1, 2, 3],
|
||||
"analysis": {
|
||||
"1": "简要说明为什么这条数据相关且有用",
|
||||
"2": "...",
|
||||
"3": "..."
|
||||
},
|
||||
"excluded_ids": [4, 5],
|
||||
"exclusion_reasons": {
|
||||
"4": "简要说明排除原因",
|
||||
"5": "..."
|
||||
},
|
||||
"confidence": "high/medium/low"
|
||||
}
|
||||
```
|
||||
|
||||
**注意:**
|
||||
- `relevant_ids`:与产品直接相关且对参数提取有用的数据ID列表
|
||||
- `analysis`:每个相关ID的简要分析说明
|
||||
- `excluded_ids`:被排除的ID列表(可选)
|
||||
- `exclusion_reasons`:排除原因说明(可选)
|
||||
- `confidence`:整体判断的置信度
|
||||
@@ -248,8 +248,8 @@ def preview_agent_template():
|
||||
filled = template.replace('{{product_name}}', product_name)
|
||||
filled = filled.replace('{{category}}', category)
|
||||
filled = filled.replace('{{subcategory}}', subcategory or '无')
|
||||
filled = filled.replace('{{library_results}}', '[内容库搜索结果将在此处列出,包含文章标题、URL、摘要等]')
|
||||
filled = filled.replace('{{internet_results}}', '[互联网抓取内容将在此处列出,包含URL、标题、正文片段等]')
|
||||
filled = filled.replace('{{library_results}}', 'ID 101: 示例文章标题A\nID 102: 示例文章标题B\nID 103: 示例文章标题C')
|
||||
filled = filled.replace('{{internet_results}}', 'ID 104: 示例互联网抓取标题X\nID 105: 示例互联网抓取标题Y')
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
|
||||
+112
-66
@@ -115,17 +115,18 @@ class ProcessMonitor:
|
||||
if fetch_result.get('success'):
|
||||
title = fetch_result.get('title', '')
|
||||
content = fetch_result.get('content', '')
|
||||
fetched.append({
|
||||
'url': url,
|
||||
'title': title,
|
||||
'content': content[:500]
|
||||
})
|
||||
article_id = None
|
||||
|
||||
# 保存到内容库
|
||||
try:
|
||||
existing = db.search_articles(url)
|
||||
if not any(a.get('url') == url for a in existing):
|
||||
db.add_article(
|
||||
if existing and len(existing) > 0:
|
||||
# 已存在,使用现有ID
|
||||
article_id = existing[0].get('id')
|
||||
logger.info(f"[{session_id}] 内容库已存在: {title[:30]}, ID={article_id}")
|
||||
else:
|
||||
# 新增,获取返回的ID
|
||||
article_id = db.add_article(
|
||||
product_names=[],
|
||||
category=category or '',
|
||||
keywords=[],
|
||||
@@ -135,9 +136,16 @@ class ProcessMonitor:
|
||||
url=url,
|
||||
search_title=title
|
||||
)
|
||||
logger.info(f"[{session_id}] 已保存到内容库: {title[:30]}")
|
||||
logger.info(f"[{session_id}] 已保存到内容库: {title[:30]}, ID={article_id}")
|
||||
except Exception as save_error:
|
||||
logger.warning(f"[{session_id}] 保存内容库失败: {save_error}")
|
||||
|
||||
fetched.append({
|
||||
'id': article_id,
|
||||
'url': url,
|
||||
'title': title,
|
||||
'content': content[:500]
|
||||
})
|
||||
else:
|
||||
# 记录失败URL
|
||||
failed_count += 1
|
||||
@@ -165,35 +173,54 @@ class ProcessMonitor:
|
||||
product_name, category, subcategory, all_data
|
||||
)
|
||||
|
||||
# 记录任务文本
|
||||
self._complete_step(session_id, 4, {
|
||||
'agent': 'hz4th_editor',
|
||||
'task_text': task_text,
|
||||
'status': 'calling_agent'
|
||||
})
|
||||
|
||||
# 调用智能体
|
||||
agent_result = self._call_agent(task_text)
|
||||
|
||||
if agent_result.get('success'):
|
||||
extracted = self._parse_agent_response(agent_result.get('output', ''))
|
||||
all_data['extracted_data'] = extracted
|
||||
parsed = self._parse_agent_response(agent_result.get('output', ''))
|
||||
|
||||
if extracted:
|
||||
if parsed and parsed.get('relevant_ids'):
|
||||
# 根据ID从内容库获取实际内容
|
||||
relevant_contents = []
|
||||
for aid in parsed['relevant_ids']:
|
||||
article = db.get_article_by_id(aid)
|
||||
if article:
|
||||
relevant_contents.append({
|
||||
'id': aid,
|
||||
'title': article.get('search_title', ''),
|
||||
'url': article.get('url', ''),
|
||||
'content': article.get('content', ''),
|
||||
'summary': article.get('summary', ''),
|
||||
'analysis': parsed.get('analysis', {}).get(str(aid), '')
|
||||
})
|
||||
|
||||
all_data['extracted_data'] = {
|
||||
'name': product_name,
|
||||
'relevant_ids': parsed['relevant_ids'],
|
||||
'relevant_contents': relevant_contents,
|
||||
'confidence': parsed.get('confidence', 'unknown'),
|
||||
'raw_output': agent_result.get('output', '')
|
||||
}
|
||||
|
||||
self._complete_step(session_id, 4, {
|
||||
'has_data': True,
|
||||
'agent': 'hz4th_editor',
|
||||
'task_text': task_text,
|
||||
'relevant_ids': parsed['relevant_ids'],
|
||||
'relevant_count': len(relevant_contents),
|
||||
'confidence': parsed.get('confidence', 'unknown'),
|
||||
'agent_output': agent_result.get('output', '')[:2000]
|
||||
})
|
||||
logger.info(f"[{session_id}] 步骤4完成: 智能体返回 {len(parsed['relevant_ids'])} 个相关ID")
|
||||
else:
|
||||
all_data['extracted_data'] = None
|
||||
self._complete_step(session_id, 4, {
|
||||
'has_data': False,
|
||||
'agent': 'hz4th_editor',
|
||||
'task_text': task_text,
|
||||
'agent_output': agent_result.get('output', '')[:2000]
|
||||
}, status='skipped')
|
||||
result['message'] = '智能体无法提取有效数据'
|
||||
result['message'] = '智能体未找到相关数据ID'
|
||||
else:
|
||||
self._fail_step(session_id, 4, f"智能体调用失败: {agent_result.get('error', '未知错误')}")
|
||||
result['message'] = f'智能体调用失败: {agent_result.get("error")}'
|
||||
@@ -347,30 +374,30 @@ class ProcessMonitor:
|
||||
else:
|
||||
# 默认模板
|
||||
template = (
|
||||
"请从以下数据中提取产品「{{product_name}}」的相关内容。\n"
|
||||
"请分析以下数据ID是否与产品「{{product_name}}」相关且对提取参数有用。\n"
|
||||
"类别: {{category}} / {{subcategory}}\n\n"
|
||||
"内容库结果:\n{{library_results}}\n\n"
|
||||
"互联网抓取内容:\n{{internet_results}}\n\n"
|
||||
"要求:只提取与该产品信息直接相关的内容,排除无关产品。以JSON格式输出。"
|
||||
"内容库结果ID: {{library_results}}\n\n"
|
||||
"互联网已入库ID: {{internet_results}}\n\n"
|
||||
"要求:输出相关且有用的ID列表,以JSON格式输出。"
|
||||
)
|
||||
|
||||
# 构建内容库搜索结果
|
||||
library_lines = []
|
||||
for i, article in enumerate(all_data.get('library_results', [])[:10], 1):
|
||||
title = article.get('search_title', article.get('title', '无标题'))
|
||||
url = article.get('url', article.get('source', '无URL'))
|
||||
summary = article.get('summary', '')[:200]
|
||||
library_lines.append(f" [{i}] 标题: {title}\n URL: {url}\n 摘要: {summary}")
|
||||
library_text = '\n'.join(library_lines) if library_lines else '(无内容库搜索结果)'
|
||||
# 构建内容库搜索结果ID列表
|
||||
library_ids = []
|
||||
for article in all_data.get('library_results', []):
|
||||
aid = article.get('id')
|
||||
if aid:
|
||||
title = article.get('search_title', article.get('title', ''))
|
||||
library_ids.append(f"ID {aid}: {title}")
|
||||
library_text = '\n'.join(library_ids) if library_ids else '(无内容库搜索结果)'
|
||||
|
||||
# 构建互联网抓取内容
|
||||
internet_lines = []
|
||||
for i, item in enumerate(all_data.get('fetched_contents', [])[:10], 1):
|
||||
title = item.get('title', '无标题')
|
||||
url = item.get('url', '无URL')
|
||||
content = item.get('content', '')[:300]
|
||||
internet_lines.append(f" [{i}] 标题: {title}\n URL: {url}\n 内容片段: {content}")
|
||||
internet_text = '\n'.join(internet_lines) if internet_lines else '(无互联网抓取内容)'
|
||||
# 构建互联网已入库数据ID列表
|
||||
internet_ids = []
|
||||
for item in all_data.get('fetched_contents', []):
|
||||
aid = item.get('id')
|
||||
if aid:
|
||||
title = item.get('title', '')
|
||||
internet_ids.append(f"ID {aid}: {title}")
|
||||
internet_text = '\n'.join(internet_ids) if internet_ids else '(无互联网已入库数据)'
|
||||
|
||||
# 填充模板
|
||||
task = template.replace('{{product_name}}', product_name or '未知')
|
||||
@@ -416,47 +443,55 @@ class ProcessMonitor:
|
||||
return {'success': False, 'error': str(e)}
|
||||
|
||||
def _parse_agent_response(self, output):
|
||||
"""解析智能体返回的结果"""
|
||||
"""解析智能体返回的结果,提取relevant_ids"""
|
||||
if not output:
|
||||
return None
|
||||
|
||||
# 尝试从输出中提取JSON
|
||||
import re
|
||||
|
||||
parsed_data = None
|
||||
|
||||
# 查找JSON块
|
||||
json_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', output, re.DOTALL)
|
||||
if json_match:
|
||||
try:
|
||||
data = json.loads(json_match.group(1))
|
||||
return {
|
||||
'name': data.get('name', ''),
|
||||
'extracted_fields': data.get('extracted_fields', {}),
|
||||
'sources': data.get('sources', []),
|
||||
'confidence': data.get('confidence', 'unknown'),
|
||||
'raw_output': output
|
||||
}
|
||||
parsed_data = json.loads(json_match.group(1))
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 尝试直接解析整个输出为JSON
|
||||
try:
|
||||
data = json.loads(output)
|
||||
if not parsed_data:
|
||||
try:
|
||||
parsed_data = json.loads(output)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
if parsed_data:
|
||||
relevant_ids = parsed_data.get('relevant_ids', [])
|
||||
# 确保都是整数
|
||||
relevant_ids = [int(x) for x in relevant_ids if str(x).isdigit()]
|
||||
|
||||
return {
|
||||
'name': data.get('name', ''),
|
||||
'extracted_fields': data.get('extracted_fields', {}),
|
||||
'sources': data.get('sources', []),
|
||||
'confidence': data.get('confidence', 'unknown'),
|
||||
'relevant_ids': relevant_ids,
|
||||
'analysis': parsed_data.get('analysis', {}),
|
||||
'excluded_ids': parsed_data.get('excluded_ids', []),
|
||||
'exclusion_reasons': parsed_data.get('exclusion_reasons', {}),
|
||||
'confidence': parsed_data.get('confidence', 'unknown'),
|
||||
'raw_output': output
|
||||
}
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 如果无法解析为JSON,将原始输出作为raw_content保存
|
||||
return {
|
||||
'name': '',
|
||||
'raw_content': output,
|
||||
'raw_output': output
|
||||
}
|
||||
# 无法解析为JSON,尝试从文本中提取ID
|
||||
id_matches = re.findall(r'(?:ID|id)[\s:]*(\d+)', output)
|
||||
if id_matches:
|
||||
return {
|
||||
'relevant_ids': [int(x) for x in id_matches],
|
||||
'analysis': {},
|
||||
'confidence': 'low',
|
||||
'raw_output': output
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
def _extract_data(self, product_name, all_data):
|
||||
"""提取产品数据(备用,已被智能体替代)"""
|
||||
@@ -488,23 +523,34 @@ class ProcessMonitor:
|
||||
import re
|
||||
|
||||
filled = {
|
||||
'name': extracted_data['name'],
|
||||
'name': extracted_data.get('name', ''),
|
||||
'visible': True,
|
||||
'is_pinned': False
|
||||
}
|
||||
|
||||
content = extracted_data.get('raw_content', '')
|
||||
# 从relevant_contents中拼接所有内容
|
||||
relevant_contents = extracted_data.get('relevant_contents', [])
|
||||
all_content = '\n---\n'.join([
|
||||
c.get('content', '') or c.get('summary', '')
|
||||
for c in relevant_contents
|
||||
if c.get('content') or c.get('summary')
|
||||
])
|
||||
|
||||
params_match = re.search(r'(\d+(?:\.\d+)?)\s*[Bb]', content)
|
||||
# 兼容旧格式
|
||||
if not all_content:
|
||||
all_content = extracted_data.get('raw_content', '')
|
||||
|
||||
params_match = re.search(r'(\d+(?:\.\d+)?)\s*[Bb]', all_content)
|
||||
if params_match:
|
||||
filled['parameters'] = f"{params_match.group(1)}B"
|
||||
|
||||
date_match = re.search(r'(\d{4}[-/]\d{1,2}[-/]\d{1,2})', content)
|
||||
date_match = re.search(r'(\d{4}[-/]\d{1,2}[-/]\d{1,2})', all_content)
|
||||
if date_match:
|
||||
filled['publish_date'] = date_match.group(1).replace('/', '-')
|
||||
|
||||
filled['_source'] = 'auto_manager'
|
||||
filled['_extracted_at'] = datetime.now().isoformat()
|
||||
filled['_relevant_ids'] = extracted_data.get('relevant_ids', [])
|
||||
|
||||
return filled
|
||||
|
||||
|
||||
Reference in New Issue
Block a user