v2.0.0 大模型驱动版:移除智能体,直接调用大模型接口

- 核心改造:不再使用 openclaw 智能体执行处理步骤,改为直接调用大模型接口
- 新增 services/llm_client.py:OpenAI 兼容接口客户端,支持多模型配置管理
- 步骤4/5 由大模型直接完成(提取产品数据、填充字段)
- 步骤6 改为直接调用 ParamHub API 提交审核
- 新增 llm_configs 数据库表,默认配置 unsloth/Qwen3.6-27B-Q4_K_M (262144上下文)
- 新增 /api/llm 配置管理 API:增删改查、切换激活、测试连接
- 前端首页新增「大模型配置」面板,可随时新增/切换模型
- 处理步骤名称更新为「大模型」版
This commit is contained in:
2026-08-13 17:15:53 +08:00
parent 6abd3389f3
commit 23991c3552
12 changed files with 1187 additions and 151 deletions
+97 -125
View File
@@ -5,24 +5,24 @@ import os
import time
import uuid
import json
import subprocess
import threading
import logging
from datetime import datetime
from models.database import db
from services.search_service import search_service
from services.paramhub_client import paramhub_client
from services.llm_client import llm_client
logger = logging.getLogger('process_monitor')
# 处理步骤定义
# 处理步骤定义(大模型版)
PROCESS_STEPS = [
{'num': 1, 'name': '搜索内容库', 'description': '从内容库搜索相关文章'},
{'num': 2, 'name': '搜索互联网', 'description': '从互联网搜索最新数据'},
{'num': 3, 'name': '抓取网页内容', 'description': '抓取搜索结果网页的详细内容'},
{'num': 4, 'name': '提取产品数据(智能体)', 'description': '调用hz4th_editor智能体提取产品相关内容'},
{'num': 5, 'name': '填充字段(智能体)', 'description': '调用智能体生成产品数据并检查格式'},
{'num': 6, 'name': '提交审核(智能体)', 'description': '调用智能体将产品数据提交到ParamHub审核系统'},
{'num': 4, 'name': '提取产品数据(大模型)', 'description': '调用大模型筛选并提取产品相关内容'},
{'num': 5, 'name': '填充字段(大模型)', 'description': '调用大模型生成产品数据并检查格式'},
{'num': 6, 'name': '提交审核', 'description': '提交产品数据到ParamHub审核系统'},
]
class ProcessMonitor:
@@ -203,17 +203,17 @@ class ProcessMonitor:
db.update_task_status(bg_task_id, 'failed', error_message=str(e))
self._fail_step(session_id, 3, str(e))
# 步骤4: 提取产品数据(调用智能体执行
# 步骤4: 提取产品数据(调用大模型筛选相关内容
if not self._check_pause(session_id):
self._start_step(session_id, product_name, 4, '提取产品数据(智能体)')
self._start_step(session_id, product_name, 4, '提取产品数据(大模型)')
try:
# 构建任务文本
task_text = self._build_agent_task(
product_name, category, subcategory, all_data
)
# 调用智能体
agent_result = self._call_agent(task_text)
# 直接调用大模型
agent_result = self._call_llm(task_text)
if agent_result.get('success'):
parsed = self._parse_agent_response(agent_result.get('output', ''))
@@ -243,40 +243,42 @@ class ProcessMonitor:
self._complete_step(session_id, 4, {
'has_data': True,
'agent': 'hz4th_editor',
'agent': '大模型',
'model': self._get_active_model_name(),
'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")
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',
'agent': '大模型',
'model': self._get_active_model_name(),
'task_text': task_text,
'agent_output': agent_result.get('output', '')[:2000]
}, status='skipped')
result['message'] = '智能体未找到相关数据ID'
result['message'] = '大模型未找到相关数据ID'
else:
self._fail_step(session_id, 4, f"智能体调用失败: {agent_result.get('error', '未知错误')}")
result['message'] = f'智能体调用失败: {agent_result.get("error")}'
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:
# 构建任务文本
fill_task_text = self._build_fill_fields_task(
product_name, category, subcategory, all_data['extracted_data']
)
# 调用智能体
fill_agent_result = self._call_agent(fill_task_text)
# 直接调用大模型
fill_agent_result = self._call_llm(fill_task_text)
if fill_agent_result.get('success'):
fill_parsed = self._parse_fill_agent_response(fill_agent_result.get('output', ''))
@@ -293,7 +295,8 @@ class ProcessMonitor:
self._complete_step(session_id, 5, {
'filled': True,
'agent': 'hz4th_editor',
'agent': '大模型',
'model': self._get_active_model_name(),
'task_text': fill_task_text,
'product_data': product_data,
'format_check': format_check,
@@ -307,67 +310,54 @@ class ProcessMonitor:
result['message'] = '数据格式验证失败'
else:
error_msg = fill_parsed.get('message', '未知错误') if fill_parsed else '解析失败'
self._fail_step(session_id, 5, f"智能体执行失败: {error_msg}")
result['message'] = f'智能体执行失败: {error_msg}'
self._fail_step(session_id, 5, f"大模型执行失败: {error_msg}")
result['message'] = f'大模型执行失败: {error_msg}'
else:
self._fail_step(session_id, 5, f"智能体调用失败: {fill_agent_result.get('error', '未知错误')}")
result['message'] = f'智能体调用失败: {fill_agent_result.get("error")}'
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: 提交审核(调用智能体执行
# 步骤6: 提交审核(直接调用ParamHub API,不再依赖智能体)
if not self._check_pause(session_id) and all_data['filled_data']:
self._start_step(session_id, product_name, 6, '提交审核(智能体)')
self._start_step(session_id, product_name, 6, '提交审核')
try:
# 构建任务文本
submit_task_text = self._build_submit_task(
product_name, category, subcategory, all_data['filled_data']
category_type = self._get_category_type(category)
subcategory_id = subcategory
success, review_id_or_error = paramhub_client.submit_for_review(
category_type,
all_data['filled_data'],
subcategory_id
)
# 调用智能体
submit_agent_result = self._call_agent(submit_task_text)
if submit_agent_result.get('success'):
submit_parsed = self._parse_submit_agent_response(submit_agent_result.get('output', ''))
if success:
review_id = review_id_or_error
self._complete_step(session_id, 6, {
'submitted': True,
'agent': 'ParamHub API',
'review_id': review_id,
'product_data': all_data['filled_data']
})
if submit_parsed and submit_parsed.get('success'):
review_id = submit_parsed.get('review_id')
if review_id:
self._complete_step(session_id, 6, {
'submitted': True,
'agent': 'hz4th_editor',
'task_text': submit_task_text,
'review_id': review_id,
'agent_output': submit_agent_result.get('output', '')[:2000]
})
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}] 步骤6完成: 智能体提交成功, review_id={review_id}")
else:
self._fail_step(session_id, 6, '智能体未返回review_id')
result['message'] = '智能体提交成功但未获取到review_id'
else:
error_msg = submit_parsed.get('message', '未知错误') if submit_parsed else '解析失败'
self._fail_step(session_id, 6, f"智能体提交失败: {error_msg}")
result['message'] = f'智能体提交失败: {error_msg}'
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}] 步骤6完成: 提交成功, review_id={review_id}")
else:
self._fail_step(session_id, 6, f"智能体调用失败: {submit_agent_result.get('error', '未知错误')}")
result['message'] = f'智能体调用失败: {submit_agent_result.get("error")}'
self._fail_step(session_id, 6, f"提交失败: {review_id_or_error}")
result['message'] = f'提交失败: {review_id_or_error}'
except Exception as e:
self._fail_step(session_id, 6, str(e))
@@ -522,67 +512,49 @@ class ProcessMonitor:
return task
def _call_agent(self, task_text):
"""调用智能体执行任务"""
import signal
def _get_active_model_name(self):
"""获取当前激活的模型名称(用于日志/步骤展示)"""
try:
cmd = [
'openclaw', 'agent',
'--agent', 'hz4th_editor',
'--message', task_text,
'--json' # 输出JSON格式以便解析
cfg = llm_client.get_active_config()
return cfg.get('model_name', '未知模型')
except Exception:
return '未知模型'
def _call_llm(self, task_text):
"""直接调用大模型执行任务(替代原来的 openclaw 智能体)"""
try:
logger.info(f"调用大模型执行任务,任务文本 [{len(task_text)} 字符]")
# 构建消息
messages = [
{
'role': 'system',
'content': (
'你是一个专业的产品数据提取与整理助手。'
'严格按照用户要求输出结果,遵循任务文本中的输出格式要求。'
'对于要求JSON输出的任务,必须只输出合法JSON,不要添加多余解释。'
)
},
{'role': 'user', 'content': task_text}
]
logger.info(f"调用智能体命令: openclaw agent --agent hz4th_editor --message '[任务文本 {len(task_text)} 字符]' --json")
# 使用Popen以便更好地控制超时和进程杀死
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
preexec_fn=os.setsid # 创建新进程组,方便杀死所有子进程
# 直接调用大模型
ok, result = llm_client.chat(
messages,
temperature=0.2,
max_tokens=8192,
timeout=600
)
try:
stdout, stderr = proc.communicate(timeout=180) # 3分钟超时
raw_output = stdout.decode('utf-8', errors='replace').strip()
if proc.returncode == 0:
# 解析JSON输出
try:
data = json.loads(raw_output)
# 提取实际回复文本: result.payloads[0].text
payloads = data.get('result', {}).get('payloads', [])
if payloads and isinstance(payloads[0], dict):
output = payloads[0].get('text', '')
else:
output = raw_output
logger.info(f"智能体返回: {output[:500]}...")
return {'success': True, 'output': output}
except json.JSONDecodeError as e:
logger.warning(f"JSON解析失败,使用原始输出: {e}")
return {'success': True, 'output': raw_output}
else:
error = stderr.decode('utf-8', errors='replace').strip() or raw_output
logger.error(f"智能体调用失败(returncode={proc.returncode}): {error}")
return {'success': False, 'error': error}
except subprocess.TimeoutExpired:
# 超时,杀死整个进程组
logger.error(f"智能体执行超时(>3分钟),杀死进程组")
try:
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
except Exception:
proc.kill()
proc.wait()
return {'success': False, 'error': '智能体执行超时(>3分钟)'}
if ok:
logger.info(f"大模型返回: {result[:500]}...")
return {'success': True, 'output': result}
else:
logger.error(f"大模型调用失败: {result}")
return {'success': False, 'error': result}
except FileNotFoundError:
return {'success': False, 'error': 'openclaw命令未找到'}
except Exception as e:
logger.error(f"智能体调用异常: {e}")
logger.error(f"大模型调用异常: {e}")
return {'success': False, 'error': str(e)}
def _parse_agent_response(self, output):