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:
@@ -0,0 +1,237 @@
|
||||
"""
|
||||
大模型调用服务 - 直接调用 OpenAI 兼容接口完成智能体任务
|
||||
不再使用 openclaw agent 智能体,全部由大模型直接完成
|
||||
"""
|
||||
import json
|
||||
import time
|
||||
import requests
|
||||
import logging
|
||||
from config import Config
|
||||
|
||||
logger = logging.getLogger('llm_client')
|
||||
|
||||
# 默认大模型配置(首次启动自动写入数据库)
|
||||
DEFAULT_LLM_CONFIG = {
|
||||
'name': '本地Qwen3.6',
|
||||
'base_url': 'http://192.168.2.7:18003/v1',
|
||||
'api_key': 'sk-xxxx',
|
||||
'model_name': 'unsloth/Qwen3.6-27B-Q4_K_M',
|
||||
'max_context': 262144,
|
||||
'is_active': 1
|
||||
}
|
||||
|
||||
|
||||
class LLMClient:
|
||||
"""大模型客户端 - 管理多个模型配置并调用"""
|
||||
|
||||
def __init__(self):
|
||||
self._active = None # 缓存当前激活的配置
|
||||
|
||||
# ========== 配置管理 ==========
|
||||
|
||||
def get_all_configs(self):
|
||||
"""获取所有模型配置"""
|
||||
from models.database import db
|
||||
return db.get_llm_configs()
|
||||
|
||||
def get_active_config(self, force=False):
|
||||
"""获取当前激活的模型配置"""
|
||||
if self._active and not force:
|
||||
return self._active
|
||||
|
||||
from models.database import db
|
||||
config = db.get_active_llm_config()
|
||||
if config:
|
||||
self._active = config
|
||||
else:
|
||||
# 无激活配置时使用默认值
|
||||
self._active = dict(DEFAULT_LLM_CONFIG)
|
||||
return self._active
|
||||
|
||||
def add_config(self, name, base_url, api_key, model_name, max_context=262144):
|
||||
"""新增模型配置"""
|
||||
from models.database import db
|
||||
return db.add_llm_config(
|
||||
name=name,
|
||||
base_url=base_url,
|
||||
api_key=api_key,
|
||||
model_name=model_name,
|
||||
max_context=max_context
|
||||
)
|
||||
|
||||
def update_config(self, config_id, **kwargs):
|
||||
"""更新模型配置"""
|
||||
from models.database import db
|
||||
db.update_llm_config(config_id, **kwargs)
|
||||
self._active = None # 使缓存失效
|
||||
|
||||
def delete_config(self, config_id):
|
||||
"""删除模型配置"""
|
||||
from models.database import db
|
||||
return db.delete_llm_config(config_id)
|
||||
|
||||
def set_active(self, config_id):
|
||||
"""切换激活的模型配置"""
|
||||
from models.database import db
|
||||
ok = db.set_active_llm_config(config_id)
|
||||
self._active = None
|
||||
return ok
|
||||
|
||||
def test_connection(self, config=None):
|
||||
"""
|
||||
测试模型连接
|
||||
Args:
|
||||
config: 可选,直接测试指定配置;None 时测试当前激活配置
|
||||
Returns:
|
||||
(success, message)
|
||||
"""
|
||||
cfg = config or self.get_active_config()
|
||||
try:
|
||||
url = cfg['base_url'].rstrip('/') + '/chat/completions'
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
api_key = cfg.get('api_key', '')
|
||||
if api_key:
|
||||
headers['Authorization'] = f'Bearer {api_key}'
|
||||
|
||||
payload = {
|
||||
'model': cfg['model_name'],
|
||||
'messages': [
|
||||
{'role': 'user', 'content': 'ping,请只回复pong'}
|
||||
],
|
||||
'max_tokens': 16,
|
||||
'temperature': 0
|
||||
}
|
||||
|
||||
resp = requests.post(url, json=payload, headers=headers, timeout=60)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
reply = data.get('choices', [{}])[0].get('message', {}).get('content', '')
|
||||
return True, f"连接成功: {reply[:50]}"
|
||||
else:
|
||||
return False, f"HTTP {resp.status_code}: {resp.text[:200]}"
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
# ========== 调用大模型 ==========
|
||||
|
||||
def chat(self, messages, temperature=0.3, max_tokens=8192, timeout=600, config=None):
|
||||
"""
|
||||
调用大模型对话接口
|
||||
|
||||
Args:
|
||||
messages: [{'role': 'user'/'system'/'assistant', 'content': '...'}]
|
||||
temperature: 温度
|
||||
max_tokens: 最大输出token数
|
||||
timeout: 超时时间(秒)
|
||||
config: 可选,指定使用的模型配置;None 使用当前激活配置
|
||||
|
||||
Returns:
|
||||
(success, result)
|
||||
success=True 时 result 为文本内容
|
||||
success=False 时 result 为错误信息
|
||||
"""
|
||||
cfg = config or self.get_active_config()
|
||||
|
||||
try:
|
||||
url = cfg['base_url'].rstrip('/') + '/chat/completions'
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
api_key = cfg.get('api_key', '')
|
||||
if api_key:
|
||||
headers['Authorization'] = f'Bearer {api_key}'
|
||||
|
||||
payload = {
|
||||
'model': cfg['model_name'],
|
||||
'messages': messages,
|
||||
'temperature': temperature,
|
||||
'max_tokens': max_tokens
|
||||
}
|
||||
|
||||
logger.info(f"[LLM] 调用 {cfg['model_name']} @ {cfg['base_url']} | 消息数: {len(messages)} | 输入字符: {sum(len(m.get('content','')) for m in messages)}")
|
||||
|
||||
resp = requests.post(url, json=payload, headers=headers, timeout=timeout)
|
||||
if resp.status_code != 200:
|
||||
logger.error(f"[LLM] HTTP {resp.status_code}: {resp.text[:300]}")
|
||||
return False, f"大模型接口返回错误 HTTP {resp.status_code}: {resp.text[:300]}"
|
||||
|
||||
data = resp.json()
|
||||
reply = data.get('choices', [{}])[0].get('message', {}).get('content', '')
|
||||
usage = data.get('usage', {})
|
||||
logger.info(f"[LLM] 返回 {len(reply)} 字符 | usage: {usage}")
|
||||
return True, reply
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
return False, f"大模型调用超时(>{timeout}秒)"
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
return False, f"无法连接大模型服务: {e}"
|
||||
except Exception as e:
|
||||
logger.error(f"[LLM] 调用异常: {e}")
|
||||
return False, str(e)
|
||||
|
||||
def chat_json(self, messages, temperature=0.1, max_tokens=8192, timeout=600, config=None):
|
||||
"""
|
||||
调用大模型并解析 JSON 输出
|
||||
|
||||
Returns:
|
||||
(success, data_or_error)
|
||||
"""
|
||||
# 追加要求JSON输出的系统提示
|
||||
sys_prompt = (
|
||||
"你是一个严格输出JSON的程序化助手。"
|
||||
"你必须只输出一个合法的JSON对象,不要输出任何多余文字、解释或markdown代码块标记。"
|
||||
"确保JSON语法正确,可以被json.loads直接解析。"
|
||||
)
|
||||
full_messages = [{'role': 'system', 'content': sys_prompt}] + messages
|
||||
|
||||
ok, result = self.chat(full_messages, temperature=temperature, max_tokens=max_tokens, timeout=timeout, config=config)
|
||||
if not ok:
|
||||
return False, result
|
||||
|
||||
parsed = self._extract_json(result)
|
||||
if parsed is None:
|
||||
return False, f"大模型输出无法解析为JSON: {result[:300]}"
|
||||
return True, parsed
|
||||
|
||||
def _extract_json(self, text):
|
||||
"""从文本中提取JSON对象"""
|
||||
if not text:
|
||||
return None
|
||||
text = text.strip()
|
||||
|
||||
# 去掉 markdown 代码块标记
|
||||
if text.startswith('```'):
|
||||
lines = text.split('\n')
|
||||
# 去掉第一行 ```json 或 ```
|
||||
lines = lines[1:]
|
||||
# 去掉最后一行 ```
|
||||
if lines and lines[-1].strip().startswith('```'):
|
||||
lines = lines[:-1]
|
||||
text = '\n'.join(lines).strip()
|
||||
|
||||
# 直接尝试解析
|
||||
try:
|
||||
return json.loads(text)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 尝试提取 {...} 块
|
||||
import re
|
||||
match = re.search(r'\{.*\}', text, re.DOTALL)
|
||||
if match:
|
||||
try:
|
||||
return json.loads(match.group(0))
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 尝试提取 [...] 块
|
||||
match = re.search(r'\[.*\]', text, re.DOTALL)
|
||||
if match:
|
||||
try:
|
||||
return json.loads(match.group(0))
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
# 全局大模型客户端实例
|
||||
llm_client = LLMClient()
|
||||
+97
-125
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user