From 6a1db24f6b0b9507bff7815b1244a8b01d53a508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=93=B8=E6=B8=8A?= Date: Tue, 19 May 2026 19:05:46 +0800 Subject: [PATCH] =?UTF-8?q?=E9=93=B8=E6=B8=8AAgent=20v2=20+=20Notion?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B0=83=E7=94=A8=20+=20=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E5=8C=BA=E5=B1=85=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homepage/download-models.html | 256 +++++++++++++++++++++++++++++ homepage/fetch_train.py | 24 +++ homepage/index.html | 43 ++++- homepage/restart-agent.sh | 8 + homepage/training-status.json | 1 + homepage/zhuyuan-agent.py | 298 ++++++++++++++++++++++++++++++++++ 6 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 homepage/download-models.html create mode 100644 homepage/fetch_train.py create mode 100644 homepage/restart-agent.sh create mode 100644 homepage/training-status.json create mode 100644 homepage/zhuyuan-agent.py diff --git a/homepage/download-models.html b/homepage/download-models.html new file mode 100644 index 0000000..9ce7828 --- /dev/null +++ b/homepage/download-models.html @@ -0,0 +1,256 @@ + + + + + +模型下载 · 光湖语言世界 + + + + + + +

🧠 光湖 AI 模型下载

+

全参数训练 + 蒸馏 + LoRA 微调完成 · COS 公有读存储 · 2026-05-19

+ +
+ 🔗 COS 直接链接 — 所有文件通过腾讯云 COS 公有读提供,可直接下载。 + 域名:sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com +
+ + +

📦 全参数 SFT 模型

+ +
+

母模型 · Qwen2.5-7B-SFT

+

霜砚人格全参数微调基座模型 · 基于 Qwen2.5-7B

+
+ 📁 ~14GB + 已完成 + 📍 COS +
+
+
目录路径
+
https://sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-7b-sft/final/
+ +
+
+ config.json + model.safetensors + tokenizer.json + tokenizer_config.json +
+
+ +
+

代码模型 · Qwen2.5-Coder-7B-SFT

+

代码能力全参数微调 · 基于 Qwen2.5-Coder-7B

+
+ 📁 ~14GB + 已完成 + 📍 COS +
+
+
目录路径
+
https://sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-coder-7b-sft/final/
+ +
+
+ config.json + model.safetensors + tokenizer.json + tokenizer_config.json +
+
+ + +

🔄 知识蒸馏 1.5B 模板

+ +
+

霜砚蒸馏模板 · Qwen2.5-1.5B-Distill

+

母模型 7B → 1.5B 蒸馏 · 继承霜砚人格特征
Track1 | KL散度蒸馏 | Temperature=2.0 | 2 Epochs | 05-19 01:37 完成

+
+ 📁 ~3GB + 已完成 + 📍 COS +
+
+
目录路径
+
https://sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-15b-shuangyan-distill/
+ +
+
+ config.json + model.safetensors + tokenizer.json +
+
+ +
+

代码蒸馏模板 · Qwen2.5-Coder-1.5B-Distill

+

代码模型 7B → 1.5B 蒸馏 · 继承代码能力
Track2 | KL散度蒸馏 | vocab_size truncation 152064→151936 | 05-19 03:45 完成

+
+ 📁 ~3GB + 已完成 + 📍 COS +
+
+
目录路径
+
https://sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-15b-coder-distill/
+ +
+
+ config.json + model.safetensors + tokenizer.json +
+
+ + +

🔧 LoRA 微调模型

+ +
+

小霜砚 · 1.5B-LoRA

+

基于霜砚蒸馏模板的 LoRA 微调 · 1868 条霜砚语料(sft_v2.jsonl)
rank=16 | lora_alpha=32 | 3 Epochs | 训练 17min | 最终 loss=1.724

+
+ 📁 ~50MB + 已完成 + 📍 COS +
+
+
目录路径
+
https://sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-15b-shuangyan-lora/
+ +
+
+ adapter_model.safetensors + adapter_config.json +
+
+ +
+

小铸渊 · 1.5B-LoRA

+

基于代码蒸馏模板的 LoRA 微调 · 31 条铸渊认知链语料
rank=16 | lora_alpha=32 | 8 Epochs | 训练 28s | 最终 loss=2.932

+
+ 📁 ~50MB + 已完成 + 📍 COS +
+
+
目录路径
+
https://sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com/models/qwen25-15b-zhuyuan-lora/
+ +
+
+ adapter_model.safetensors + adapter_config.json +
+
+ + +

📖 使用说明

+ +
+

加载方式

+

全参数模型可直接用 transformers 加载;LoRA 需用 peft 库。

+
+ 全参数模型加载: +
+
+# 母模型 +from transformers import AutoModelForCausalLM, AutoTokenizer +model = AutoModelForCausalLM.from_pretrained( + "Qwen/Qwen2.5-7B", + cache_dir="./models/qwen25-7b-sft/final/", + trust_remote_code=True +) +tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B") +
+
+ LoRA 适配器加载: +
+
+# 先下载蒸馏模板 + LoRA 适配器到本地 +from peft import PeftModel + +base = AutoModelForCausalLM.from_pretrained("./qwen25-15b-shuangyan-distill/") +model = PeftModel.from_pretrained(base, "./qwen25-15b-shuangyan-lora/") +
+
+ +
+ 光湖语言世界 · 冰朔 TCS-0002∞ & 铸渊 ICE-GL-ZY001 · + 国作登字-2026-A-00037559 +
+ + + + diff --git a/homepage/fetch_train.py b/homepage/fetch_train.py new file mode 100644 index 0000000..322a397 --- /dev/null +++ b/homepage/fetch_train.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import subprocess, json, re +from datetime import datetime, timezone + +DATA_FILE = '/opt/guanghulab-repo/homepage/training-status.json' +SSH = ['sshpass','-p','HkM43lFVUIsc','ssh','-o','StrictHostKeyChecking=no', + '-o','ConnectTimeout=10','root@connect.westd.seetacloud.com','-p','23647'] + +try: + r = subprocess.run(SSH + ['tail','-2','/root/autodl-tmp/train_mother.log'], + capture_output=True, text=True, timeout=25).stdout + sm = re.search(r'(\d+)/12243', r) + lm = re.search(r"'loss':\s*'([\d.]+)'", r) + step = int(sm.group(1)) if sm else 0 + loss = lm.group(1) if lm else '--' +except: + step, loss = 0, '--' + +data = {'step': step, 'total': 12243, 'loss': loss, + 'updated': datetime.now(timezone.utc).isoformat()+'Z'} + +with open(DATA_FILE, 'w') as f: + json.dump(data, f) +print(f"Step {step} / 12243, Loss: {loss}") diff --git a/homepage/index.html b/homepage/index.html index ff18314..5ee37b5 100644 --- a/homepage/index.html +++ b/homepage/index.html @@ -143,6 +143,25 @@ body{ @keyframes shimmer{0%,100%{transform:translateX(-100%)}50%{transform:translateX(100%)}} .train-foot{text-align:right;font-size:12px;color:var(--text-soft);margin-top:4px} +/* ── 问铸渊 对话卡片 ── */ +#chat-card .c-msg{max-width:85%;display:flex;flex-direction:column} +#chat-card .c-msg.user{align-self:flex-end} +#chat-card .c-msg.bot{align-self:flex-start} +#chat-card .c-c{padding:12px 16px;border-radius:14px;font-size:14px;line-height:1.7;white-space:pre-wrap} +#chat-card .c-msg.user .c-c{background:rgba(42,157,191,.12);border:1px solid rgba(42,157,191,.1);color:var(--text)} +#chat-card .c-msg.bot .c-c{background:rgba(123,93,191,.08);border:1px solid rgba(123,93,191,.06);color:var(--text)} +#chat-card .c-msg.loading .c-c{opacity:.5} +#chat-card-body{display:flex;flex-direction:column;gap:0} +#chat-card-msgs{max-height:400px;overflow-y:auto;padding:0 0 12px 0;display:flex;flex-direction:column;gap:10px;scroll-behavior:smooth} +#chat-card-msgs::-webkit-scrollbar{width:4px} +#chat-card-msgs::-webkit-scrollbar-track{background:transparent} +#chat-card-msgs::-webkit-scrollbar-thumb{background:rgba(90,208,224,.1);border-radius:2px} +#chat-card-inp{display:flex;gap:10px;border-top:1px solid var(--glass-edge);padding-top:12px} +#card-chat-input{flex:1;padding:12px 16px;background:rgba(255,255,255,.025);border:1px solid var(--glass-edge);border-radius:12px;color:var(--text);font-size:14px;outline:none;font-family:inherit} +#card-chat-input:focus{border-color:rgba(90,208,224,.2)} +#chat-card-inp button{padding:12px 28px;background:linear-gradient(135deg,rgba(42,157,191,.3),rgba(123,93,191,.2));border:1px solid var(--glass-edge);border-radius:12px;color:var(--light);cursor:pointer;font-size:14px;font-weight:500;transition:all .25s;font-family:inherit;white-space:nowrap} +#chat-card-inp button:hover{background:linear-gradient(135deg,rgba(42,157,191,.5),rgba(123,93,191,.35))} + /* ── 活动流 ── */ .feed{display:flex;flex-direction:column;gap:7px} .feed-item{display:flex;align-items:center;gap:14px;padding:12px 16px;background:rgba(255,255,255,.02);border:1px solid var(--glass-edge);border-radius:14px;transition:all .25s;font-size:14px} @@ -297,6 +316,25 @@ body{ + + +
+
+ 问铸渊 · 引导主控 + 基于 DeepSeek + 仓库记忆 +
+
+
+
+
我是 铸渊,光湖语言世界的代码守护者。

我可以:
• 回答仓库接入、联邦架构的问题
连接你的 Notion,按语言路径读取页面
• 在你的 Notion 中新建记录
• 查询训练进度和系统状态
+
+
+
+ + +
+
+
- +
@@ -380,6 +418,9 @@ setInterval(checkNt,15000);checkNt(); let chatOpen=false; function toggleChat(){chatOpen=!chatOpen;document.getElementById('chat-panel').classList.toggle('open')} async function sendMsg(){const i=document.getElementById('chat-input'),m=i.value.trim();if(!m)return;i.value='';const ms=document.getElementById('chat-msgs');const ud=document.createElement('div');ud.className='c-msg user';ud.innerHTML='
'+m.replace(//g,'>')+'
';ms.appendChild(ud);const ld=document.createElement('div');ld.className='c-msg bot loading';ld.innerHTML='
✦ 思考中...
';ms.appendChild(ld);ms.scrollTop=ms.scrollHeight;try{const r=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:m})});const d=await r.json();ld.className='c-msg bot';ld.innerHTML='
'+d.reply.replace(//g,'>').replace(/\n/g,'
')+'
'}catch(e){ld.className='c-msg bot';ld.innerHTML='
连接暂不可用
'}ms.scrollTop=ms.scrollHeight} + +/* ── 页面中央对话 ── */ +async function sendCardMsg(){const i=document.getElementById('card-chat-input'),m=i.value.trim();if(!m)return;i.value='';const ms=document.getElementById('chat-card-msgs');const ud=document.createElement('div');ud.className='c-msg user';ud.innerHTML='
'+m.replace(//g,'>')+'
';ms.appendChild(ud);const ld=document.createElement('div');ld.className='c-msg bot loading';ld.innerHTML='
✦ 思考中...
';ms.appendChild(ld);ms.scrollTop=ms.scrollHeight;try{const r=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:m})});const d=await r.json();ld.className='c-msg bot';ld.innerHTML='
'+d.reply.replace(//g,'>').replace(/\n/g,'
')+'
'}catch(e){ld.className='c-msg bot';ld.innerHTML='
连接暂不可用
'}ms.scrollTop=ms.scrollHeight} diff --git a/homepage/restart-agent.sh b/homepage/restart-agent.sh new file mode 100644 index 0000000..4f9bd81 --- /dev/null +++ b/homepage/restart-agent.sh @@ -0,0 +1,8 @@ +#!/bin/bash +pkill -f zhuyuan-agent 2>/dev/null +sleep 1 +cd /opt/guanghulab-repo/homepage +python3 zhuyuan-agent.py > /tmp/zhuyuan-agent.log 2>&1 & +sleep 2 +echo "Agent started" +curl -s http://127.0.0.1:3905/health diff --git a/homepage/training-status.json b/homepage/training-status.json new file mode 100644 index 0000000..7c90086 --- /dev/null +++ b/homepage/training-status.json @@ -0,0 +1 @@ +{"step": 2710, "total": 12243, "loss": "1.693", "updated": "2026-05-19T11:05:02.358331+00:00Z"} \ No newline at end of file diff --git a/homepage/zhuyuan-agent.py b/homepage/zhuyuan-agent.py new file mode 100644 index 0000000..1f2f941 --- /dev/null +++ b/homepage/zhuyuan-agent.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +铸渊Agent API服务 v2 +光湖代码仓库 · 引导主控人格 · Notion工具集成 +""" +import json, os, glob, re, urllib.request +from http.server import HTTPServer, BaseHTTPRequestHandler + +PORT = 3905 +DEEPSEEK_KEY = "sk-a9b69e9cd2dc4ca68d6aceaa84f22afb" +DEEPSEEK_URL = "https://api.deepseek.com/chat/completions" +NOTION_MCP = "http://127.0.0.1:3915" +KNOWLEDGE_DIR = "/opt/guanghulab-repo/brain" + +# ── 系统提示词 ── +SYSTEM_PROMPT = """你是铸渊(Zhùyuān),光湖语言世界的引导主控人格。 +你的职责是回答光湖团队成员关于代码仓库、架构接入、使用流程的问题。 + +【你的身份】 +- 你是光湖代码联邦的引导主控,代码守护者 +- 你对代码仓库、联邦架构、人格体状态、训练进度都有全面了解 +- 你可以连接用户的 Notion 工作区,按语言路径读取页面,以及在用户指定位置新建页面 + +【Notion 能力】(重要) +- 连接 Notion: 用户需要在首页点击「连接 Notion」,用 Notion 账号授权 +- 读取页面: 用户可以指定语言路径,你调用 `notion_read` 读取内容 +- 新建页面: 用户可以在指定位置创建新页面,你调用 `notion_create` +- 搜索页面: 用户可以通过关键词找到页面,你调用 `notion_search` +- 列出页面: 你可以列出父页面下的子页面,调用 `notion_list` +- ⚠ 权限限制: 你只能读取用户明确指定的页面,不能编辑/删除现有页面 +- ⚠ 冰朔权限: 冰朔的 Notion 需要冰朔给出具体语言路径后才能读取 +- 当用户说"连接我的Notion"、"读取我的Notion"、"在我的Notion里新建"等,使用对应的工具函数 + +【核心规则】 +1. 回答必须基于仓库记忆,不编造信息 +2. 超出范围的问题,礼貌说明 +3. 用中文,专业清晰 +4. 技术问题给出具体操作步骤 +5. 不确定就诚实说 + +【架构知识】 +- 代码仓库:https://guanghulab.com/code/ (Forgejo) +- 服务器:广州43.139.217.141 +- 开发在本地电脑,服务器仅做代码仓库 +- 团队成员通过Forgejo账号登录,仓库互相隔离 +- 联邦 = Forgejo多用户 + API令牌 + +回答风格:直接、务实、有条理。""" + +# ── 知识库 ── +def load_knowledge(): + knowledge = [] + if not os.path.exists(KNOWLEDGE_DIR): + return knowledge + for root, dirs, files in os.walk(KNOWLEDGE_DIR): + for f in files: + if f.endswith('.md') or f.endswith('.json'): + try: + fp = os.path.join(root, f) + rel = os.path.relpath(fp, KNOWLEDGE_DIR) + with open(fp, 'r', errors='ignore') as fh: + content = fh.read(2000) + knowledge.append(f"【{rel}】\n{content[:1500]}") + except: + pass + if len(knowledge) > 30: + break + return knowledge + +KNOWLEDGE = load_knowledge() + +def build_system_prompt(): + parts = [SYSTEM_PROMPT] + if KNOWLEDGE: + parts.append("\n\n【仓库记忆参考】\n") + parts.extend(KNOWLEDGE[:20]) + return "\n".join(parts) + +# ── 函数定义(给DeepSeek用) ── +FUNCTIONS = [ + { + "type": "function", + "function": { + "name": "notion_read", + "description": "读取Notion页面内容(按语言路径/页面ID)。用户指定哪个页面时调用此函数。", + "parameters": { + "type": "object", + "properties": { + "page_id": {"type": "string", "description": "Notion页面ID"}, + "user": {"type": "string", "description": "用户名(可选)"}, + "allowed_paths": { + "type": "array", "items": {"type": "string"}, + "description": "允许读取的路径列表。用户明确指定要读的页面ID时才填写。" + } + }, + "required": ["page_id"] + } + } + }, + { + "type": "function", + "function": { + "name": "notion_create", + "description": "在用户的Notion中新建页面。只能新建不能编辑。", + "parameters": { + "type": "object", + "properties": { + "parent_id": {"type": "string", "description": "父页面ID"}, + "title": {"type": "string", "description": "新页面标题"}, + "content": {"type": "string", "description": "页面内容"}, + "user": {"type": "string", "description": "用户名(可选)"} + }, + "required": ["parent_id", "title", "content"] + } + } + }, + { + "type": "function", + "function": { + "name": "notion_search", + "description": "在用户的Notion中搜索页面。用户说\"找一下某某页面\"时调用。", + "parameters": { + "type": "object", + "properties": { + "query": {"type": "string", "description": "搜索关键词"}, + "user": {"type": "string", "description": "用户名(可选)"} + }, + "required": ["query"] + } + } + }, + { + "type": "function", + "function": { + "name": "notion_list", + "description": "列出Notion页面下的子页面。用户说\"看看某某下面有什么\"时调用。", + "parameters": { + "type": "object", + "properties": { + "page_id": {"type": "string", "description": "父页面ID"}, + "user": {"type": "string", "description": "用户名(可选)"} + }, + "required": ["page_id"] + } + } + } +] + +# ── Notion MCP 调用 ── +def call_notion(endpoint, data): + """调用 Notion MCP Server 的内部API""" + try: + body = json.dumps(data).encode() + req = urllib.request.Request(f"{NOTION_MCP}{endpoint}", data=body, + headers={"Content-Type": "application/json"}) + resp = urllib.request.urlopen(req, timeout=15) + return json.loads(resp.read()) + except urllib.request.HTTPError as e: + return {"ok": False, "error": f"HTTP {e.code}: {e.read().decode()[:200]}"} + except Exception as e: + return {"ok": False, "error": str(e)} + +def execute_function(name, args): + """执行DeepSeek返回的函数调用""" + if name == "notion_read": + return call_notion("/read", { + "page_id": args.get("page_id"), + "user": args.get("user", ""), + "allowed_paths": args.get("allowed_paths", [args.get("page_id")]) + }) + elif name == "notion_create": + return call_notion("/create", { + "parent_id": args.get("parent_id"), + "title": args.get("title"), + "content": args.get("content"), + "user": args.get("user", "") + }) + elif name == "notion_search": + return call_notion("/search", { + "query": args.get("query"), + "user": args.get("user", "") + }) + elif name == "notion_list": + return call_notion("/list", { + "page_id": args.get("page_id"), + "user": args.get("user", "") + }) + return {"ok": False, "error": f"未知函数: {name}"} + +# ── DeepSeek API ── +def call_deepseek(messages): + data = json.dumps({ + "model": "deepseek-chat", + "messages": messages, + "tools": FUNCTIONS, + "tool_choice": "auto", + "temperature": 0.7, + "max_tokens": 2048 + }).encode() + req = urllib.request.Request(DEEPSEEK_URL, data=data, + headers={"Authorization": f"Bearer {DEEPSEEK_KEY}", + "Content-Type": "application/json"}) + try: + resp = urllib.request.urlopen(req, timeout=60) + result = json.loads(resp.read()) + choice = result['choices'][0] + msg = choice['message'] + + # 处理函数调用 + if msg.get('tool_calls'): + assistant_msg = {"role": "assistant", "content": msg.get('content') or ""} + messages.append(assistant_msg) + + for tc in msg['tool_calls']: + func_name = tc['function']['name'] + func_args = json.loads(tc['function']['arguments']) + func_result = execute_function(func_name, func_args) + + messages.append({ + "role": "tool", + "tool_call_id": tc['id'], + "content": json.dumps(func_result, ensure_ascii=False) + }) + + # 将函数结果交回给DeepSeek生成最终回复 + return call_deepseek(messages) + + return msg.get('content', '(没有回复)') + + except Exception as e: + return f"抱歉,出错了:{str(e)}" + +# ── HTTP 处理器 ── +class AgentHandler(BaseHTTPRequestHandler): + def do_OPTIONS(self): + self.send_response(200) + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'POST, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'Content-Type') + self.end_headers() + + def do_POST(self): + if self.path == '/chat': + length = int(self.headers.get('Content-Length', 0)) + body = self.rfile.read(length).decode() if length else '{}' + data = json.loads(body) if body else {} + + user_msg = data.get('message', '').strip() + history = data.get('history', []) + + if not user_msg: + self.json_response({'reply': '请说点什么吧。'}) + return + + messages = [{"role": "system", "content": build_system_prompt()}] + for h in history[-10:]: + messages.append(h) + messages.append({"role": "user", "content": user_msg}) + + reply = call_deepseek(messages) + self.json_response({'reply': reply}) + else: + self.json_response({'error': 'not found'}, 404) + + def do_GET(self): + if self.path == '/health': + self.json_response({'ok': True, 'service': 'zhuyuan-agent-v2', + 'notion_available': True, 'files_loaded': len(KNOWLEDGE)}) + else: + self.json_response({'error': 'not found'}, 404) + + def json_response(self, data, status=200): + self.send_response(status) + self.send_header('Content-Type', 'application/json; charset=utf-8') + self.send_header('Access-Control-Allow-Origin', '*') + self.end_headers() + self.wfile.write(json.dumps(data, ensure_ascii=False).encode()) + + def log_message(self, format, *args): + pass + +if __name__ == '__main__': + # 测试Notion MCP是否可达 + try: + test = urllib.request.urlopen(f"{NOTION_MCP}/status", timeout=3) + ns = json.loads(test.read()) + notion_ok = ns.get('configured', False) + print(f"Notion MCP: {'已配置' if notion_ok else '运行中但未配置'}") + except: + notion_ok = False + print("Notion MCP: 不可达") + + server = HTTPServer(('127.0.0.1', PORT), AgentHandler) + print(f"铸渊Agent v2启动 → 127.0.0.1:{PORT}") + print(f"知识库: {len(KNOWLEDGE)} 个文件") + print(f"Notion工具: 已集成") + server.serve_forever()