阿良按此文档逐步搭建 · 预计4-6小时完成基础骨架
版本 v1.0 | 2026-05-26 | 存档: /mnt/sda/web/pages/advisor-implementation.html
目标: 本地模型能接收问题 → 提取关键词 → 返回答案
# 1.1 确认本地模型可用
ollama list | grep qwen3.6
# 如果没有,拉取:
ollama pull qwen3.6:27b-q4
# 1.2 创建总顾问工作目录
mkdir -p /mnt/sda/agents/advisor/{scripts,config,logs,identity}
cd /mnt/sda/agents/advisor
# 1.3 创建总顾问路由脚本
cat > scripts/router.py << 'PYEOF'
import json, subprocess, sqlite3, re
DB = "/mnt/sda/agents/advisor/data/consultation_log.db"
# 子顾问路由表:关键词 → 顾问代号
ROUTES = {
"gpu|量化|mtp|dflash|vllm|sglang|llama|显存|tps|推理|token": "A",
"网络|dns|frp|nginx|路由|域名|ccs|wireguard|代理|反代|ping": "B",
"工控|wincc|plc|西门子|scada|opc|modbus|profinet|自动化": "C",
"数据库|sqlite|postgres|nas|备份|存储|chromadb|rag|知识库": "D",
"安全|ssh|ssl|加密|凭证|密码|防火墙|ufw|入侵|key": "E",
"前端|web|api|flask|vue|openwebui|企微|bot|html|界面": "F",
"文档|总结|排坑|方案|撰写|markdown|报告|对比": "G",
}
def route(question):
"""关键词匹配,返回对应的顾问代号列表(最多3个)"""
matches = []
for pattern, advisor in ROUTES.items():
if re.search(pattern, question, re.IGNORECASE):
matches.append(advisor)
return matches[:3] if matches else ["G"] # 默认G通用顾问
def ask_ollama(prompt):
"""调用本地 Ollama 模型"""
import urllib.request
data = json.dumps({
"model": "qwen3.6:27b-q4",
"prompt": prompt,
"stream": False,
"options": {"num_predict": 500}
}).encode()
req = urllib.request.Request("http://localhost:11434/api/generate", data=data,
headers={"Content-Type": "application/json"})
resp = urllib.request.urlopen(req, timeout=120)
return json.loads(resp.read()).get("response", "")
def ask_deepseek(question, advisor_name):
"""调用 DeepSeek Pro(子顾问用),实际由阿良的 settings.json 配置的 URL"""
# 通过 claude --print 模式调用子顾问
prompt = f"你{advisor_name},领域专家。请专业回答:{question}"
result = subprocess.run(
["claude", "--print", prompt, "--dangerously-skip-permissions",
"--model", "deepseek-v4-pro", "--max-turns", "1"],
capture_output=True, text=True, timeout=180
)
return result.stdout.strip()[:2000]
def init_db():
conn = sqlite3.connect(DB)
conn.execute("""CREATE TABLE IF NOT EXISTS consultations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question TEXT NOT NULL,
routed_to TEXT,
answers TEXT,
final_answer TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)""")
conn.commit()
return conn
# --- main ---
if __name__ == "__main__":
import sys
question = sys.argv[1] if len(sys.argv) > 1 else "你好"
advisors = route(question)
print(f"路由: {advisors}")
answers = {}
for adv in advisors:
ans = ask_deepseek(question, f"顾问{adv}")
answers[adv] = ans
print(f"顾问{adv}: {ans[:100]}...")
# 小顾综合
summary_prompt = f"用户问题:{question}\n\n专家回答汇总:\n"
for adv, ans in answers.items():
summary_prompt += f"[顾问{adv}]: {ans}\n"
summary_prompt += "\n请综合以上专家意见,给出最终答案。"
final = ask_ollama(summary_prompt)
print(f"\n=== 小顾最终答案 ===\n{final}")
# 记日志
conn = init_db()
conn.execute("INSERT INTO consultations (question, routed_to, answers, final_answer) VALUES (?,?,?,?)",
(question, json.dumps(advisors), json.dumps(answers), final))
conn.commit()
PYEOF
# 1.4 初始化数据库
python3 -c "
import sqlite3
db = '/mnt/sda/agents/advisor/data/consultation_log.db'
conn = sqlite3.connect(db)
conn.execute('''CREATE TABLE IF NOT EXISTS consultations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question TEXT NOT NULL,
routed_to TEXT,
answers TEXT,
final_answer TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)''')
conn.commit()
print(f'数据库就绪: {db}')
"
# 1.5 测试
python3 scripts/router.py "GPU显存不够怎么办"
目标: 7个子顾问各带 mem0 记忆
# 2.1 安装 mem0
pip install --break-system-packages mem0ai
# 2.2 批量创建子顾问配置
for adv in A B C D E F G; do
mkdir -p /mnt/sda/agents/advisor/advisors/$adv/{config,memory}
cat > /mnt/sda/agents/advisor/advisors/$adv/config.json << EOF
{"name": "顾问$adv", "model": "deepseek-v4-pro", "mem0_db": "/mnt/sda/agents/advisor/advisors/$adv/memory"}
EOF
done
# 2.3 初始化 mem0 知识库(每个顾问独立)
for adv in A B C D E F G; do
python3 -c "
from mem0 import Memory
m = Memory()
m.add('系统初始化——顾问$adv 知识库就绪', user_id='advisor_$adv')
print('顾问$adv mem0 OK')
"
done
目标: 每次问答自动转为 OpenWebUI 对话记录,人类可按主题搜索、翻看
# 3.1 在 adviser router 输出阶段加 OpenWebUI 发布
# 在 router.py 底部加这段:
def publish_to_openwebui(question, final_answer, advisors):
"""将问答发布为 OpenWebUI 聊天记录"""
import urllib.request
chat_data = {
"chat": {
"title": question[:50],
"messages": [
{"role": "user", "content": question},
{"role": "assistant", "content": f"[顾问: {','.join(advisors)}]\n\n{final_answer}"}
]
}
}
# 写入共享目录,OpenWebUI 定时扫描
outdir = "/mnt/sda/openwebui-chats"
import os, time
os.makedirs(outdir, exist_ok=True)
fname = f"{outdir}/advice_{int(time.time())}.json"
with open(fname, 'w') as f:
json.dump(chat_data, f, ensure_ascii=False, indent=2)
print(f"已发布: {fname}")
# 3.2 在 main 末尾调用
publish_to_openwebui(question, final, advisors)
目标: heusion.site 上增加论坛板块,按主题分类浏览历史问答
| 功能 | 说明 | 优先级 |
|---|---|---|
| 问答列表 | 按时间倒序、分页;显示标题/标签/日期/回答数 | 高 |
| 全文搜索 | 关键词搜索问题和答案 | 高 |
| 标签分类 | GPU/网络/工控/安全/... 按顾问筛选 | 中 |
| 人工标注 | 对答案打星/补充/纠错 | 中 |
| 邮件订阅 | 特定标签有新问答时通知 | 低 |
技术方案:Flask 直接读 SQLite 渲染页面。复用 qr-system 现有 Flask 框架。