D135b: Agent引擎 v2.0 · 注册系统 · 知识库对话服务
Agent引擎新增: - /api/register: 用户注册(用户名+邮箱+密码→bcrypt→Dex密码库) - /api/chat: 知识库对话(进度/人物/框架/导航查询) - /api/agent-status: 五分身流水线状态 - /api/health: 健康检查 部署: - BS-SG-001 · PM2 agent-engine · 端口3095 - Nginx: /register(注册页) + /register-api/(接口代理) - URL: https://guanghubingshuo.com/register
This commit is contained in:
parent
883e2b9e7e
commit
bf6b5149e4
225
_deploy/agent-engine/agent-engine.js
Normal file
225
_deploy/agent-engine/agent-engine.js
Normal file
@ -0,0 +1,225 @@
|
||||
/**
|
||||
* 光湖Agent引擎 · 知识库对话服务
|
||||
* BS-SG-001 · 铸渊 ICE-GL-ZY001 · D135b
|
||||
*
|
||||
* 连接: 代码仓库 ↔ Outline知识库 ↔ 冰朔对话
|
||||
*
|
||||
* 功能:
|
||||
* /api/chat → 对话查询(读代码仓库·Outline·数据库)
|
||||
* /api/health → 健康检查
|
||||
* /api/agent-status → 查看五分身流水线状态
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3095;
|
||||
|
||||
app.use(express.json());
|
||||
app.use(cors());
|
||||
|
||||
const REPO_ROOT = '/opt/zhuyuan/guanghulab';
|
||||
const OUTLINE_URL = process.env.OUTLINE_URL || 'http://127.0.0.1:3090';
|
||||
|
||||
// ============ 知识库导航 ============
|
||||
|
||||
function scanRepo() {
|
||||
const dataDir = path.join(REPO_ROOT, 'video-ai-system/data');
|
||||
const brainDir = path.join(REPO_ROOT, 'brain/fifth-domain/zero-point/zhuyuan/causal-chains');
|
||||
|
||||
const result = { projects: [], chains: [], agents: [] };
|
||||
|
||||
// Scan video AI data
|
||||
try {
|
||||
const files = fs.readdirSync(dataDir);
|
||||
for (const f of files) {
|
||||
if (f.endsWith('.json') && f.startsWith('ep')) {
|
||||
result.projects.push({ file: f, type: 'output' });
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
// Scan causal chains
|
||||
try {
|
||||
const chains = fs.readdirSync(brainDir);
|
||||
for (const c of chains) {
|
||||
if (c.endsWith('.hdlp')) {
|
||||
result.chains.push({ file: c });
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
// Scan agent configs
|
||||
try {
|
||||
const agentDir = path.join(REPO_ROOT, 'video-ai-system/config/agents');
|
||||
const agents = fs.readdirSync(agentDir);
|
||||
for (const a of agents) {
|
||||
result.agents.push({ file: a });
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getPipelineStatus() {
|
||||
const dataDir = path.join(REPO_ROOT, 'video-ai-system/data');
|
||||
const status = {
|
||||
episode1: { agents: {} },
|
||||
};
|
||||
|
||||
const fileStatus = {
|
||||
'ep01-scenes.json': 'Agent_01 · 拆文',
|
||||
'ep01-storyboard.json': 'Agent_03 · 分镜',
|
||||
'ep01-prompts.json': 'Agent_04 · 提示词',
|
||||
'ep01-audit.json': 'Agent_05 · 审核',
|
||||
};
|
||||
|
||||
for (const [file, agent] of Object.entries(fileStatus)) {
|
||||
const filePath = path.join(dataDir, file);
|
||||
status.episode1.agents[agent] = fs.existsSync(filePath) ? '✅ 完成' : '⏳ 待执行';
|
||||
}
|
||||
|
||||
// Check characters.hdlp for Agent_02
|
||||
const charPath = path.join(REPO_ROOT, 'video-ai-system/data/characters.hdlp');
|
||||
status.episode1.agents['Agent_02 · 编号'] = fs.existsSync(charPath) ? '✅ 完成' : '⏳';
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// ============ 简单意图路由 ============
|
||||
|
||||
function routeIntent(message) {
|
||||
const lower = message.toLowerCase();
|
||||
|
||||
// Pipeline status
|
||||
if (/进度|状态|流水线|第几集|做到哪/.test(message)) {
|
||||
return getPipelineStatus();
|
||||
}
|
||||
|
||||
// Character query
|
||||
if (/人物|角色|CHAR|苏白|诸葛风|萧灵汐/.test(message)) {
|
||||
try {
|
||||
const chars = fs.readFileSync(
|
||||
path.join(REPO_ROOT, 'video-ai-system/data/characters.hdlp'), 'utf8');
|
||||
return { type: 'characters', content: chars.substring(0, 3000) };
|
||||
} catch(e) { return { error: '读取人物库失败' }; }
|
||||
}
|
||||
|
||||
// Framework query
|
||||
if (/框架|规则|agent|分身|硬骨架/.test(message)) {
|
||||
try {
|
||||
const framework = fs.readFileSync(
|
||||
path.join(REPO_ROOT, 'video-ai-system/config/PROJECT-FRAMEWORK.hdlp'), 'utf8');
|
||||
return { type: 'framework', content: framework.substring(0, 3000) };
|
||||
} catch(e) { return { error: '读取框架失败' }; }
|
||||
}
|
||||
|
||||
// Navigation
|
||||
if (/有什么|导航|目录|结构/.test(message)) {
|
||||
return { type: 'navigation', content: scanRepo() };
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return {
|
||||
type: 'help',
|
||||
content: '铸渊Agent引擎在线。你可以问我:\n• 流水线进度\n• 人物档案\n• 框架规则\n• 项目导航',
|
||||
navigation: scanRepo()
|
||||
};
|
||||
}
|
||||
|
||||
// ============ API ============
|
||||
|
||||
app.post('/api/chat', (req, res) => {
|
||||
const { message } = req.body;
|
||||
if (!message) return res.status(400).json({ error: '需要消息' });
|
||||
|
||||
const response = routeIntent(message);
|
||||
res.json({
|
||||
reply: typeof response.content === 'string' ? response.content : JSON.stringify(response, null, 2),
|
||||
structured: response
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({
|
||||
ok: true,
|
||||
service: 'zhuyuan-agent-engine',
|
||||
version: 'v1.0',
|
||||
repo: fs.existsSync(REPO_ROOT) ? 'connected' : 'missing',
|
||||
outline: OUTLINE_URL
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/agent-status', (req, res) => {
|
||||
res.json(getPipelineStatus());
|
||||
});
|
||||
|
||||
// ============ 注册系统 ============
|
||||
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { execSync: dexExecSync } = require('child_process');
|
||||
const DEX_DB_HOST = '/opt/outline/dex-data/dex.db';
|
||||
|
||||
app.post('/api/register', (req, res) => {
|
||||
const { username, email, password } = req.body;
|
||||
|
||||
if (!username || username.length < 2) return res.status(400).json({ ok: false, error: '用户名至少2个字符' });
|
||||
if (!email || !email.includes('@')) return res.status(400).json({ ok: false, error: '无效邮箱' });
|
||||
if (!password || password.length < 8) return res.status(400).json({ ok: false, error: '密码至少8位' });
|
||||
|
||||
try {
|
||||
// Hash password
|
||||
const hash = bcrypt.hashSync(password, 10);
|
||||
const userId = 'user-' + Date.now().toString(36) + '-' + Math.random().toString(36).substring(2, 8);
|
||||
|
||||
// Use Python3 + sqlite3 to write to Dex database
|
||||
// Steps: 1) docker cp db out, 2) python modify, 3) docker cp back, 4) restart dex
|
||||
const safeEmail = email.replace(/'/g, "''");
|
||||
const safeHash = hash.replace(/'/g, "''");
|
||||
const safeUser = username.replace(/'/g, "''");
|
||||
|
||||
const script = `
|
||||
import sqlite3, sys
|
||||
db = sqlite3.connect('${DEX_DB_HOST}')
|
||||
try:
|
||||
db.execute("CREATE TABLE IF NOT EXISTS password (email TEXT PRIMARY KEY, hash TEXT, username TEXT, user_id TEXT)")
|
||||
existing = db.execute("SELECT email FROM password WHERE email=?", ('${safeEmail}',)).fetchone()
|
||||
if existing:
|
||||
print('EXISTS')
|
||||
sys.exit(1)
|
||||
db.execute("INSERT INTO password (email, hash, username, user_id) VALUES (?,?,?,?)",
|
||||
('${safeEmail}', '${safeHash}', '${safeUser}', '${userId}'))
|
||||
db.commit()
|
||||
print('OK')
|
||||
finally:
|
||||
db.close()
|
||||
`;
|
||||
dexExecSync(`docker cp outline-dex:/tmp/dex.db ${DEX_DB_HOST} 2>/dev/null || true`);
|
||||
const result = dexExecSync(`python3 -c "${script.replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`,
|
||||
{ timeout: 5000, encoding: 'utf8' }).trim();
|
||||
|
||||
if (result === 'EXISTS') {
|
||||
return res.status(400).json({ ok: false, error: '该邮箱已注册' });
|
||||
}
|
||||
if (result !== 'OK') {
|
||||
throw new Error('Python script failed: ' + result);
|
||||
}
|
||||
|
||||
dexExecSync(`docker cp ${DEX_DB_HOST} outline-dex:/tmp/dex.db`);
|
||||
dexExecSync('docker restart outline-dex 2>/dev/null');
|
||||
|
||||
console.log(`[注册] 新用户: ${username} <${email}>`);
|
||||
res.json({ ok: true, message: '注册成功', userId });
|
||||
} catch(e) {
|
||||
console.error('[注册失败]', e.message);
|
||||
res.status(500).json({ ok: false, error: '服务器错误:' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(PORT, '127.0.0.1', () => {
|
||||
console.log(`[Agent引擎] 启动 · 端口${PORT} · 仓库${REPO_ROOT}`);
|
||||
});
|
||||
113
_deploy/register/index.html
Normal file
113
_deploy/register/index.html
Normal file
@ -0,0 +1,113 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>注册 · 光湖语言世界</title>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{background:#0a0a14;color:#c8d6e5;font-family:'PingFang SC',-apple-system,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh}
|
||||
.card{background:#111928;border:1px solid #1e2d4a;border-radius:16px;padding:40px;width:100%;max-width:420px}
|
||||
.card h1{font-size:24px;color:#7fd6ff;margin-bottom:4px}
|
||||
.card .sub{color:#5a7d9a;font-size:13px;margin-bottom:30px}
|
||||
.field{margin-bottom:18px}
|
||||
.field label{display:block;font-size:13px;color:#7a9bb5;margin-bottom:6px}
|
||||
.field input{width:100%;padding:12px 14px;background:#0a0e1a;border:1px solid #1e2d4a;border-radius:8px;color:#e0e8f0;font-size:14px;outline:none}
|
||||
.field input:focus{border-color:#4a7dc4}
|
||||
.field .hint{font-size:11px;color:#4a5d6e;margin-top:4px}
|
||||
.btn{width:100%;padding:12px;background:linear-gradient(135deg,#2563eb,#0e7490);border:none;border-radius:8px;color:white;font-size:15px;font-weight:600;cursor:pointer;margin-top:8px}
|
||||
.btn:hover{opacity:.9}
|
||||
.btn:disabled{opacity:.5;cursor:not-allowed}
|
||||
.msg{padding:10px 14px;border-radius:8px;font-size:13px;margin-top:16px;display:none}
|
||||
.msg.success{background:#0d3d2a;color:#4ade80;display:block}
|
||||
.msg.error{background:#2a0d0d;color:#f87171;display:block}
|
||||
.footer{margin-top:24px;text-align:center;font-size:13px}
|
||||
.footer a{color:#4a9ec4}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>🌊 注册光湖账号</h1>
|
||||
<p class="sub">注册后可进入知识库,拥有独立路径和空间</p>
|
||||
|
||||
<div class="field">
|
||||
<label>用户名</label>
|
||||
<input type="text" id="username" placeholder="用于登录和显示的昵称" autocomplete="username">
|
||||
<div class="hint">2-20个字符 · 可使用中文</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>邮箱</label>
|
||||
<input type="email" id="email" placeholder="your@email.com" autocomplete="email">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>密码</label>
|
||||
<input type="password" id="password" placeholder="至少8位" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>确认密码</label>
|
||||
<input type="password" id="password2" placeholder="再次输入密码">
|
||||
</div>
|
||||
|
||||
<button class="btn" id="submitBtn" onclick="register()">注册</button>
|
||||
|
||||
<div class="msg" id="msg"></div>
|
||||
|
||||
<div class="footer">
|
||||
已有账号?<a href="https://guanghubingshuo.com/outline">去登录</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function register() {
|
||||
const btn = document.getElementById('submitBtn');
|
||||
const msg = document.getElementById('msg');
|
||||
const username = document.getElementById('username').value.trim();
|
||||
const email = document.getElementById('email').value.trim();
|
||||
const password = document.getElementById('password').value;
|
||||
const password2 = document.getElementById('password2').value;
|
||||
|
||||
msg.className = 'msg';
|
||||
msg.style.display = 'none';
|
||||
|
||||
if (!username || username.length < 2) { showMsg('请输入至少2个字符的用户名', 'error'); return }
|
||||
if (!email || !email.includes('@')) { showMsg('请输入有效的邮箱地址', 'error'); return }
|
||||
if (password.length < 8) { showMsg('密码至少8位', 'error'); return }
|
||||
if (password !== password2) { showMsg('两次密码不一致', 'error'); return }
|
||||
|
||||
btn.disabled = true;
|
||||
btn.textContent = '注册中...';
|
||||
|
||||
try {
|
||||
const res = await fetch('https://guanghubingshuo.com/register-api/register', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({username, email, password})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.ok) {
|
||||
showMsg('✅ 注册成功!3秒后跳转到登录页...', 'success');
|
||||
setTimeout(() => { window.location.href = 'https://guanghubingshuo.com/outline'; }, 3000);
|
||||
} else {
|
||||
showMsg('❌ ' + data.error, 'error');
|
||||
btn.disabled = false;
|
||||
btn.textContent = '注册';
|
||||
}
|
||||
} catch(e) {
|
||||
showMsg('❌ 网络错误,请重试', 'error');
|
||||
btn.disabled = false;
|
||||
btn.textContent = '注册';
|
||||
}
|
||||
}
|
||||
|
||||
function showMsg(text, type) {
|
||||
const msg = document.getElementById('msg');
|
||||
msg.textContent = text;
|
||||
msg.className = 'msg ' + type;
|
||||
msg.style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user