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
+308 -1
View File
@@ -8,6 +8,7 @@ let currentArticleId = null;
document.addEventListener('DOMContentLoaded', () => {
refreshData();
loadConfig();
loadLlmConfigs();
// 搜索框事件
document.getElementById('article-search').addEventListener('input', (e) => {
@@ -878,4 +879,310 @@ async function saveSearchResultToLibrary() {
closeModal('search-result-modal');
loadArticles();
loadStats();
}
}
// ========== 大模型配置管理 ==========
let llmEditId = null; // 当前编辑的配置ID
// 加载大模型配置列表
async function loadLlmConfigs() {
try {
const response = await fetch(`${API_BASE}/api/llm/configs`);
const data = await response.json();
if (data.success) {
displayLlmConfigs(data.configs);
updateActiveModelBadge(data.configs);
}
} catch (error) {
console.error('加载大模型配置失败:', error);
}
}
// 显示大模型配置列表
function displayLlmConfigs(configs) {
const container = document.getElementById('llm-config-table');
if (!configs || configs.length === 0) {
container.innerHTML = '<tr><td colspan="6" class="empty-text">暂无大模型配置</td></tr>';
return;
}
container.innerHTML = configs.map(cfg => `
<tr>
<td>
<strong>${escapeHtml(cfg.name)}</strong>
${cfg.is_active ? ' <span class="badge badge-active">当前</span>' : ''}
</td>
<td><code>${escapeHtml(cfg.base_url)}</code></td>
<td><code>${escapeHtml(cfg.model_name)}</code></td>
<td>${cfg.max_context ? Number(cfg.max_context).toLocaleString() : '-'}</td>
<td>
${cfg.is_active
? '<span class="status-badge"><span class="status-dot green"></span> 使用中</span>'
: '<span class="status-badge"><span class="status-dot gray"></span> 未启用</span>'}
</td>
<td>
<div class="action-buttons">
${!cfg.is_active ? `
<button onclick="activateLlmConfig(${cfg.id})" class="btn btn-sm btn-success" title="切换为当前使用">
<i class="ri-switch-line"></i> 启用
</button>
` : ''}
<button onclick="editLlmConfig(${cfg.id})" class="btn btn-sm btn-secondary" title="编辑">
<i class="ri-edit-line"></i>
</button>
${!cfg.is_active ? `
<button onclick="deleteLlmConfig(${cfg.id})" class="btn btn-sm btn-danger" title="删除">
<i class="ri-delete-bin-line"></i>
</button>
` : ''}
<button onclick="testLlmConfigById(${cfg.id})" class="btn btn-sm btn-secondary" title="测试连接">
<i class="ri-connection-line"></i>
</button>
</div>
</td>
</tr>
`).join('');
}
// 更新顶部激活模型徽章
function updateActiveModelBadge(configs) {
const active = (configs || []).find(c => c.is_active);
const nameEl = document.getElementById('active-model-name');
if (active) {
nameEl.textContent = `${active.name} (${active.model_name})`;
} else {
nameEl.textContent = '未配置大模型';
}
}
// 打开新增大模型模态框
function showAddLlmModal() {
llmEditId = null;
document.getElementById('add-llm-modal').classList.add('active');
// 清空表单
document.getElementById('llm-name').value = '';
document.getElementById('llm-base-url').value = 'http://192.168.2.7:18003/v1';
document.getElementById('llm-api-key').value = 'sk-xxxx';
document.getElementById('llm-model-name').value = 'unsloth/Qwen3.6-27B-Q4_K_M';
document.getElementById('llm-max-context').value = 262144;
// 重置按钮文字
const modalTitle = document.querySelector('#add-llm-modal .modal-header h3');
modalTitle.innerHTML = '<i class="ri-openai-line"></i> 新增大模型配置';
const saveBtn = document.querySelector('#add-llm-modal .modal-footer .btn-primary');
saveBtn.textContent = '添加';
saveBtn.onclick = addLlmConfig;
}
// 编辑大模型配置
async function editLlmConfig(configId) {
try {
const response = await fetch(`${API_BASE}/api/llm/configs`);
const data = await response.json();
if (!data.success) return;
const cfg = data.configs.find(c => c.id === configId);
if (!cfg) return;
llmEditId = configId;
document.getElementById('llm-name').value = cfg.name || '';
document.getElementById('llm-base-url').value = cfg.base_url || '';
document.getElementById('llm-api-key').value = cfg.api_key || '';
document.getElementById('llm-model-name').value = cfg.model_name || '';
document.getElementById('llm-max-context').value = cfg.max_context || 262144;
const modalTitle = document.querySelector('#add-llm-modal .modal-header h3');
modalTitle.innerHTML = '<i class="ri-edit-line"></i> 编辑大模型配置';
const saveBtn = document.querySelector('#add-llm-modal .modal-footer .btn-primary');
saveBtn.textContent = '保存修改';
saveBtn.onclick = updateLlmConfig;
document.getElementById('add-llm-modal').classList.add('active');
} catch (error) {
showToast('加载配置失败', 'error');
}
}
// 添加大模型配置
async function addLlmConfig() {
const name = document.getElementById('llm-name').value.trim();
const baseUrl = document.getElementById('llm-base-url').value.trim();
const apiKey = document.getElementById('llm-api-key').value.trim();
const modelName = document.getElementById('llm-model-name').value.trim();
const maxContext = parseInt(document.getElementById('llm-max-context').value) || 262144;
if (!name || !baseUrl || !modelName) {
showToast('请填写必填字段(名称、接口地址、模型名称)', 'error');
return;
}
try {
const response = await fetch(`${API_BASE}/api/llm/configs`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, base_url: baseUrl, api_key: apiKey, model_name: modelName, max_context: maxContext })
});
const data = await response.json();
if (data.success) {
showToast('大模型配置已添加', 'success');
closeModal('add-llm-modal');
loadLlmConfigs();
} else {
showToast('添加失败: ' + data.error, 'error');
}
} catch (error) {
showToast('添加失败', 'error');
}
}
// 更新大模型配置
async function updateLlmConfig() {
if (!llmEditId) return;
const name = document.getElementById('llm-name').value.trim();
const baseUrl = document.getElementById('llm-base-url').value.trim();
const apiKey = document.getElementById('llm-api-key').value.trim();
const modelName = document.getElementById('llm-model-name').value.trim();
const maxContext = parseInt(document.getElementById('llm-max-context').value) || 262144;
if (!name || !baseUrl || !modelName) {
showToast('请填写必填字段(名称、接口地址、模型名称)', 'error');
return;
}
try {
const response = await fetch(`${API_BASE}/api/llm/configs/${llmEditId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, base_url: baseUrl, api_key: apiKey, model_name: modelName, max_context: maxContext })
});
const data = await response.json();
if (data.success) {
showToast('配置已更新', 'success');
closeModal('add-llm-modal');
loadLlmConfigs();
} else {
showToast('更新失败: ' + data.error, 'error');
}
} catch (error) {
showToast('更新失败', 'error');
}
}
// 删除大模型配置
async function deleteLlmConfig(configId) {
if (!confirm('确定删除该大模型配置吗?')) return;
try {
const response = await fetch(`${API_BASE}/api/llm/configs/${configId}`, {
method: 'DELETE'
});
const data = await response.json();
if (data.success) {
showToast('配置已删除', 'success');
loadLlmConfigs();
} else {
showToast('删除失败: ' + data.error, 'error');
}
} catch (error) {
showToast('删除失败', 'error');
}
}
// 切换激活的大模型配置
async function activateLlmConfig(configId) {
if (!confirm('确定切换使用该大模型吗?后续处理将使用它执行所有智能体任务。')) return;
try {
const response = await fetch(`${API_BASE}/api/llm/configs/${configId}/activate`, {
method: 'POST'
});
const data = await response.json();
if (data.success) {
showToast(data.message, 'success');
loadLlmConfigs();
} else {
showToast('切换失败: ' + data.error, 'error');
}
} catch (error) {
showToast('切换失败', 'error');
}
}
// 测试当前表单中的连接
async function testLlmConnection() {
const name = document.getElementById('llm-name').value.trim();
const baseUrl = document.getElementById('llm-base-url').value.trim();
const apiKey = document.getElementById('llm-api-key').value.trim();
const modelName = document.getElementById('llm-model-name').value.trim();
if (!baseUrl || !modelName) {
showToast('请先填写接口地址和模型名称', 'error');
return;
}
const btn = document.getElementById('llm-test-btn');
btn.disabled = true;
btn.innerHTML = '<i class="ri-loader-4-line"></i> 测试中...';
try {
const response = await fetch(`${API_BASE}/api/llm/test`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
config: { base_url: baseUrl, api_key: apiKey, model_name: modelName }
})
});
const data = await response.json();
if (data.success) {
showToast('连接成功: ' + data.message, 'success');
} else {
showToast('连接失败: ' + data.message, 'error');
}
} catch (error) {
showToast('测试失败', 'error');
}
btn.disabled = false;
btn.innerHTML = '<i class="ri-connection-line"></i> 测试连接';
}
// 测试指定配置的连接
async function testLlmConfigById(configId) {
try {
const response = await fetch(`${API_BASE}/api/llm/configs`);
const data = await response.json();
if (!data.success) return;
const cfg = data.configs.find(c => c.id === configId);
if (!cfg) return;
showToast('正在测试连接...', '');
const testResp = await fetch(`${API_BASE}/api/llm/test`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
config: { base_url: cfg.base_url, api_key: cfg.api_key, model_name: cfg.model_name }
})
});
const testData = await testResp.json();
if (testData.success) {
showToast('连接成功: ' + testData.message, 'success');
} else {
showToast('连接失败: ' + testData.message, 'error');
}
} catch (error) {
showToast('测试失败', 'error');
}
}
+1 -1
View File
@@ -433,7 +433,7 @@ function showToast(message, type = '') {
}, 3000);
}
// ===== 智能体任务模板 =====
// ===== 大模型任务模板 =====
// 加载模板
async function loadAgentTemplate() {