Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eea7269eda | ||
|
|
5fdcf7a5b6 |
@@ -0,0 +1,71 @@
|
||||
## 任务背景
|
||||
|
||||
### 产品基本信息
|
||||
- **产品名称:** {{product_name}}
|
||||
- **产品类别:** {{category}}
|
||||
- **子类别:** {{subcategory}}
|
||||
|
||||
### 相关内容数据ID
|
||||
上一步已筛选出以下与产品直接相关且对参数提取有用的内容库数据ID:
|
||||
{{relevant_content_ids}}
|
||||
|
||||
## 任务要求
|
||||
|
||||
请完成以下工作:
|
||||
|
||||
### 1. 获取对应类别的字段配置
|
||||
首先,请访问 ParamHub API 文档获取对应类别的字段定义:
|
||||
- API文档地址:http://192.168.2.8:12007/hz4th_coder/param-hub-python/src/branch/master/API.md
|
||||
- 根据产品类别({{category}})确定应该使用哪个API:
|
||||
- AI模型 → `/api/models`
|
||||
- GPU → `/api/gpus`
|
||||
- CPU → `/api/cpus`
|
||||
- 其他动态分类 → `/api/items/{category_id}`
|
||||
|
||||
### 2. 从内容库获取数据内容
|
||||
根据上述数据ID,从内容库中获取每条数据的完整内容。
|
||||
|
||||
### 3. 整理产品参数
|
||||
根据获取到的内容,提取并整理产品的各项参数,严格按照API文档中定义的字段格式填充。
|
||||
|
||||
### 4. 提交到审核系统
|
||||
使用以下命令提交到ParamHub审核系统:
|
||||
|
||||
```bash
|
||||
# 先登录获取cookie
|
||||
curl -c /tmp/paramhub_cookie.txt -X POST "http://localhost:16041/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"password": "admin123"}'
|
||||
|
||||
# 然后提交产品数据(根据类别选择对应的API)
|
||||
curl -b /tmp/paramhub_cookie.txt -X POST "http://localhost:16041/api/{对应类别API}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "{{product_name}}",
|
||||
"visible": true,
|
||||
"is_pinned": false,
|
||||
// ... 其他字段根据API文档填充
|
||||
}'
|
||||
```
|
||||
|
||||
### 5. 输出要求
|
||||
请以JSON格式输出执行结果:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"review_id": "审核ID(如果提交成功)",
|
||||
"submitted_data": {
|
||||
"name": "产品名称",
|
||||
"field1": "值1",
|
||||
"field2": "值2"
|
||||
},
|
||||
"data_sources": [数据ID列表],
|
||||
"message": "执行说明"
|
||||
}
|
||||
```
|
||||
|
||||
**注意:**
|
||||
- 严格按照API文档的字段定义填充数据
|
||||
- 不要编造或推测任何参数,只使用内容库中实际存在的信息
|
||||
- 如果某些字段无法从内容中提取,可以留空或填写默认值
|
||||
- 提交成功后记录返回的review_id
|
||||
@@ -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,
|
||||
@@ -258,3 +258,83 @@ def preview_agent_template():
|
||||
except Exception as e:
|
||||
logger.error(f"预览模板失败: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
# ===== 步骤5填充字段模板 API =====
|
||||
|
||||
FILL_FIELDS_TEMPLATE_FILE = os.path.join(TEMPLATE_DIR, 'agent_fill_fields_template.txt')
|
||||
|
||||
|
||||
@bp.route('/fill-fields-template', methods=['GET'])
|
||||
def get_fill_fields_template():
|
||||
"""获取步骤5填充字段任务文本模板"""
|
||||
try:
|
||||
if os.path.exists(FILL_FIELDS_TEMPLATE_FILE):
|
||||
with open(FILL_FIELDS_TEMPLATE_FILE, 'r', encoding='utf-8') as f:
|
||||
template = f.read()
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'template': template
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': '模板文件不存在'
|
||||
}), 404
|
||||
except Exception as e:
|
||||
logger.error(f"获取填充字段模板失败: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@bp.route('/fill-fields-template', methods=['POST'])
|
||||
def save_fill_fields_template():
|
||||
"""保存步骤5填充字段任务文本模板"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
template = data.get('template', '')
|
||||
|
||||
if not template:
|
||||
return jsonify({'success': False, 'error': '模板内容不能为空'}), 400
|
||||
|
||||
os.makedirs(TEMPLATE_DIR, exist_ok=True)
|
||||
with open(FILL_FIELDS_TEMPLATE_FILE, 'w', encoding='utf-8') as f:
|
||||
f.write(template)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '模板已保存'
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"保存填充字段模板失败: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@bp.route('/fill-fields-template/preview', methods=['POST'])
|
||||
def preview_fill_fields_template():
|
||||
"""预览填充后的步骤5任务文本"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
product_name = data.get('product_name', '示例产品')
|
||||
category = data.get('category', '示例类别')
|
||||
subcategory = data.get('subcategory', '示例子类别')
|
||||
|
||||
# 读取模板
|
||||
if os.path.exists(FILL_FIELDS_TEMPLATE_FILE):
|
||||
with open(FILL_FIELDS_TEMPLATE_FILE, 'r', encoding='utf-8') as f:
|
||||
template = f.read()
|
||||
else:
|
||||
return jsonify({'success': False, 'error': '模板文件不存在'}), 404
|
||||
|
||||
# 填充示例数据
|
||||
filled = template.replace('{{product_name}}', product_name)
|
||||
filled = filled.replace('{{category}}', category)
|
||||
filled = filled.replace('{{subcategory}}', subcategory or '无')
|
||||
filled = filled.replace('{{relevant_content_ids}}', 'ID 101: 示例相关文章A\nID 102: 示例相关文章B\nID 103: 示例相关文章C')
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'preview': filled
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"预览填充字段模板失败: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
+267
-78
@@ -21,7 +21,7 @@ PROCESS_STEPS = [
|
||||
{'num': 2, 'name': '搜索互联网', 'description': '从互联网搜索最新数据'},
|
||||
{'num': 3, 'name': '抓取网页内容', 'description': '抓取搜索结果网页的详细内容'},
|
||||
{'num': 4, 'name': '提取产品数据(智能体)', 'description': '调用hz4th_editor智能体提取产品相关内容'},
|
||||
{'num': 5, 'name': '填充字段', 'description': '根据分类字段配置填充数据'},
|
||||
{'num': 5, 'name': '填充字段(智能体)', 'description': '调用hz4th_editor智能体整理产品数据并提交审核'},
|
||||
{'num': 6, 'name': '提交审核', 'description': '提交到ParamHub待审核区'},
|
||||
]
|
||||
|
||||
@@ -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,58 +173,130 @@ 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")}'
|
||||
except Exception as e:
|
||||
self._fail_step(session_id, 4, str(e))
|
||||
|
||||
# 步骤5: 填充字段
|
||||
# 步骤5: 填充字段并提交审核(调用智能体执行)
|
||||
if not self._check_pause(session_id) and all_data['extracted_data']:
|
||||
self._start_step(session_id, product_name, 5, '填充字段')
|
||||
self._start_step(session_id, product_name, 5, '填充字段并提交(智能体)')
|
||||
try:
|
||||
filled = self._fill_fields(all_data['extracted_data'], category, subcategory)
|
||||
all_data['filled_data'] = filled
|
||||
# 构建任务文本
|
||||
fill_task_text = self._build_fill_fields_task(
|
||||
product_name, category, subcategory, all_data['extracted_data']
|
||||
)
|
||||
|
||||
if filled:
|
||||
self._complete_step(session_id, 5, {'filled': True})
|
||||
# 调用智能体
|
||||
fill_agent_result = self._call_agent(fill_task_text)
|
||||
|
||||
if fill_agent_result.get('success'):
|
||||
fill_parsed = self._parse_fill_agent_response(fill_agent_result.get('output', ''))
|
||||
|
||||
if fill_parsed and fill_parsed.get('success'):
|
||||
review_id = fill_parsed.get('review_id')
|
||||
submitted_data = fill_parsed.get('submitted_data', {})
|
||||
|
||||
all_data['filled_data'] = submitted_data
|
||||
|
||||
self._complete_step(session_id, 5, {
|
||||
'filled': True,
|
||||
'agent': 'hz4th_editor',
|
||||
'task_text': fill_task_text,
|
||||
'review_id': review_id,
|
||||
'submitted_data': submitted_data,
|
||||
'agent_output': fill_agent_result.get('output', '')[:2000]
|
||||
})
|
||||
|
||||
# 如果智能体返回了review_id,直接标记步骤6完成
|
||||
if review_id:
|
||||
result['success'] = True
|
||||
result['review_id'] = review_id
|
||||
|
||||
db.update_session_status(session_id, 'completed',
|
||||
review_id=review_id,
|
||||
result=json.dumps(result, ensure_ascii=False))
|
||||
|
||||
db.add_process_history(
|
||||
product_name=product_name,
|
||||
category=category,
|
||||
subcategory=subcategory,
|
||||
status='submitted',
|
||||
review_id=review_id,
|
||||
details=all_data
|
||||
)
|
||||
logger.info(f"[{session_id}] 步骤5完成: 智能体提交成功, review_id={review_id}")
|
||||
else:
|
||||
self._complete_step(session_id, 5, {
|
||||
'filled': True,
|
||||
'agent': 'hz4th_editor',
|
||||
'task_text': fill_task_text,
|
||||
'submitted_data': submitted_data,
|
||||
'message': '数据已整理但未获取到review_id',
|
||||
'agent_output': fill_agent_result.get('output', '')[:2000]
|
||||
})
|
||||
result['message'] = '数据已整理但提交状态未知'
|
||||
else:
|
||||
self._fail_step(session_id, 5, f"智能体执行失败: {fill_parsed.get('message', '未知错误') if fill_parsed else '解析失败'}")
|
||||
result['message'] = f'智能体执行失败'
|
||||
else:
|
||||
self._fail_step(session_id, 5, '填充数据失败')
|
||||
self._fail_step(session_id, 5, f"智能体调用失败: {fill_agent_result.get('error', '未知错误')}")
|
||||
result['message'] = f'智能体调用失败: {fill_agent_result.get("error")}'
|
||||
except Exception as e:
|
||||
self._fail_step(session_id, 5, str(e))
|
||||
|
||||
# 步骤6: 提交审核
|
||||
if not self._check_pause(session_id) and all_data['filled_data']:
|
||||
self._start_step(session_id, product_name, 6, '提交审核')
|
||||
# 步骤6: 确认提交结果(如果步骤5未获取到review_id,尝试本地提交)
|
||||
if not self._check_pause(session_id) and all_data['filled_data'] and not result.get('review_id'):
|
||||
self._start_step(session_id, product_name, 6, '确认提交结果')
|
||||
try:
|
||||
category_type = self._get_category_type(category)
|
||||
success, review_id_or_error = paramhub_client.submit_for_review(
|
||||
@@ -242,7 +322,7 @@ class ProcessMonitor:
|
||||
review_id=review_id_or_error,
|
||||
details=all_data
|
||||
)
|
||||
logger.info(f"[{session_id}] 步骤6完成: 提交成功")
|
||||
logger.info(f"[{session_id}] 步骤6完成: 本地提交成功")
|
||||
else:
|
||||
self._fail_step(session_id, 6, review_id_or_error)
|
||||
db.update_session_status(session_id, 'failed')
|
||||
@@ -347,30 +427,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 +496,145 @@ 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 _build_fill_fields_task(self, product_name, category, subcategory, extracted_data):
|
||||
"""构建步骤5填充字段的智能体任务文本"""
|
||||
# 读取模板
|
||||
template_file = os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)),
|
||||
'config', 'agent_fill_fields_template.txt'
|
||||
)
|
||||
|
||||
if os.path.exists(template_file):
|
||||
with open(template_file, 'r', encoding='utf-8') as f:
|
||||
template = f.read()
|
||||
else:
|
||||
# 默认模板
|
||||
template = (
|
||||
"请根据内容库数据ID {{relevant_content_ids}} 整理产品「{{product_name}}」的参数并提交审核。\n"
|
||||
"类别: {{category}} / {{subcategory}}\n"
|
||||
"参考API文档: http://192.168.2.8:12007/hz4th_coder/param-hub-python/src/branch/master/API.md"
|
||||
)
|
||||
|
||||
# 构建相关内容ID列表
|
||||
relevant_ids = extracted_data.get('relevant_ids', [])
|
||||
relevant_contents = extracted_data.get('relevant_contents', [])
|
||||
|
||||
if relevant_contents:
|
||||
content_lines = []
|
||||
for item in relevant_contents:
|
||||
aid = item.get('id', '')
|
||||
title = item.get('title', '')
|
||||
content_lines.append(f"ID {aid}: {title}")
|
||||
relevant_text = '\n'.join(content_lines)
|
||||
elif relevant_ids:
|
||||
relevant_text = '\n'.join([f"ID {aid}" for aid in relevant_ids])
|
||||
else:
|
||||
relevant_text = '(无相关内容ID)'
|
||||
|
||||
# 填充模板
|
||||
task = template.replace('{{product_name}}', product_name or '未知')
|
||||
task = task.replace('{{category}}', category or '未分类')
|
||||
task = task.replace('{{subcategory}}', subcategory or '无')
|
||||
task = task.replace('{{relevant_content_ids}}', relevant_text)
|
||||
|
||||
return task
|
||||
|
||||
def _parse_fill_agent_response(self, output):
|
||||
"""解析步骤5智能体返回的结果"""
|
||||
if not output:
|
||||
return None
|
||||
|
||||
import re
|
||||
|
||||
parsed_data = None
|
||||
|
||||
# 查找JSON块
|
||||
json_match = re.search(r'```(?:json)?\s*(\{.*?\})\s*```', output, re.DOTALL)
|
||||
if json_match:
|
||||
try:
|
||||
parsed_data = json.loads(json_match.group(1))
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 尝试直接解析整个输出为JSON
|
||||
if not parsed_data:
|
||||
try:
|
||||
parsed_data = json.loads(output)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
if parsed_data:
|
||||
return {
|
||||
'success': parsed_data.get('success', False),
|
||||
'review_id': parsed_data.get('review_id'),
|
||||
'submitted_data': parsed_data.get('submitted_data', {}),
|
||||
'data_sources': parsed_data.get('data_sources', []),
|
||||
'message': parsed_data.get('message', ''),
|
||||
'raw_output': output
|
||||
}
|
||||
|
||||
# 尝试从文本中提取review_id
|
||||
review_match = re.search(r'review[_-]?id[\s:]*([\w-]+)', output, re.I)
|
||||
if review_match:
|
||||
return {
|
||||
'success': True,
|
||||
'review_id': review_match.group(1),
|
||||
'submitted_data': {},
|
||||
'message': '从输出中提取到review_id',
|
||||
'raw_output': output
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
def _extract_data(self, product_name, all_data):
|
||||
"""提取产品数据(备用,已被智能体替代)"""
|
||||
@@ -488,23 +666,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
|
||||
|
||||
|
||||
+80
-1
@@ -10,6 +10,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
loadActiveProcesses();
|
||||
loadHistory();
|
||||
loadAgentTemplate();
|
||||
loadFillFieldsTemplate();
|
||||
|
||||
// 启动自动刷新(每2秒)
|
||||
startAutoRefresh();
|
||||
@@ -508,4 +509,82 @@ async function doPreview() {
|
||||
} catch (error) {
|
||||
document.getElementById('template-preview-content').textContent = '预览失败: ' + error.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ===== 步骤5填充字段模板 =====
|
||||
|
||||
// 加载步骤5模板
|
||||
async function loadFillFieldsTemplate() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/process/fill-fields-template`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
document.getElementById('fill-fields-template-editor').value = data.template;
|
||||
} else {
|
||||
document.getElementById('fill-fields-template-editor').value = '// 模板加载失败: ' + (data.error || '未知错误');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载填充字段模板失败:', error);
|
||||
document.getElementById('fill-fields-template-editor').value = '// 加载模板失败: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存步骤5模板
|
||||
async function saveFillFieldsTemplate() {
|
||||
const template = document.getElementById('fill-fields-template-editor').value;
|
||||
|
||||
if (!template.trim()) {
|
||||
showToast('模板内容不能为空', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/process/fill-fields-template`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ template })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
showToast('步骤5模板已保存 ✓', 'success');
|
||||
} else {
|
||||
showToast('保存失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('保存失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 预览步骤5模板
|
||||
function previewFillFieldsTemplate() {
|
||||
document.getElementById('template-preview-modal').classList.add('active');
|
||||
doFillFieldsPreview();
|
||||
}
|
||||
|
||||
// 执行步骤5预览
|
||||
async function doFillFieldsPreview() {
|
||||
const product = document.getElementById('preview-product').value || '示例产品';
|
||||
const category = document.getElementById('preview-category').value || 'AI模型';
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/process/fill-fields-template/preview`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
product_name: product,
|
||||
category: category,
|
||||
subcategory: ''
|
||||
})
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
document.getElementById('template-preview-content').textContent = data.preview;
|
||||
} else {
|
||||
document.getElementById('template-preview-content').textContent = '预览失败: ' + data.error;
|
||||
}
|
||||
} catch (error) {
|
||||
document.getElementById('template-preview-content').textContent = '预览失败: ' + error.message;
|
||||
}
|
||||
}
|
||||
+30
-2
@@ -52,7 +52,7 @@
|
||||
<!-- 智能体任务模板区域 -->
|
||||
<div class="panel template-section">
|
||||
<div class="panel-header">
|
||||
<h2><i class="ri-robot-line"></i> 智能体任务文本模板(步骤4:提取产品数据)</h2>
|
||||
<h2><i class="ri-robot-line"></i> 步骤4:提取产品数据 - 智能体任务模板</h2>
|
||||
<div class="template-actions">
|
||||
<button onclick="previewTemplate()" class="btn btn-secondary btn-sm">
|
||||
<i class="ri-eye-line"></i> 预览
|
||||
@@ -74,7 +74,35 @@
|
||||
</p>
|
||||
<p><strong>调用命令:</strong><code>openclaw agent --agent hz4th_editor --message "[填充后的任务文本]"</code></p>
|
||||
</div>
|
||||
<textarea id="agent-template-editor" class="template-editor" rows="20" placeholder="加载模板中..."></textarea>
|
||||
<textarea id="agent-template-editor" class="template-editor" rows="15" placeholder="加载模板中..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 步骤5填充字段模板区域 -->
|
||||
<div class="panel template-section">
|
||||
<div class="panel-header">
|
||||
<h2><i class="ri-edit-box-line"></i> 步骤5:填充字段并提交 - 智能体任务模板</h2>
|
||||
<div class="template-actions">
|
||||
<button onclick="previewFillFieldsTemplate()" class="btn btn-secondary btn-sm">
|
||||
<i class="ri-eye-line"></i> 预览
|
||||
</button>
|
||||
<button onclick="saveFillFieldsTemplate()" class="btn btn-primary btn-sm">
|
||||
<i class="ri-save-line"></i> 保存模板
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="template-info">
|
||||
<p><strong>说明:</strong>此模板用于步骤5「填充字段并提交审核」中调用智能体 <code>hz4th_editor</code> 的任务文本。</p>
|
||||
<p><strong>可用变量:</strong>
|
||||
<code>{{product_name}}</code> 产品名称、
|
||||
<code>{{category}}</code> 类别、
|
||||
<code>{{subcategory}}</code> 子类别、
|
||||
<code>{{relevant_content_ids}}</code> 上一步筛选的相关内容数据ID
|
||||
</p>
|
||||
<p><strong>任务目标:</strong>智能体根据API文档获取字段定义,整理产品参数,并通过API提交到ParamHub审核系统。</p>
|
||||
</div>
|
||||
<textarea id="fill-fields-template-editor" class="template-editor" rows="15" placeholder="加载模板中..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user