diff --git a/server/corpus-agent/server.py b/server/corpus-agent/server.py index 912c8be..add4888 100644 --- a/server/corpus-agent/server.py +++ b/server/corpus-agent/server.py @@ -243,7 +243,13 @@ def save_corpus_snapshot(username: str, filename: str) -> Path: @app.get("/") async def root(): - return HTMLResponse(open(static_dir / "index.html").read()) + from fastapi.responses import Response + html = open(static_dir / "index.html").read() + return Response( + content=html, + media_type="text/html", + headers={"Cache-Control": "no-cache, no-store, must-revalidate, private"} + ) @app.get("/api/health") async def health(): @@ -349,6 +355,23 @@ async def check_session(token: str = Query(...)): raise HTTPException(401, "登录已过期") return {"ok": True, "username": username} +@app.get("/api/auth/quick-login") +async def quick_login(username: str = Query(...)): + """首页已登录用户免密快速登录""" + if not username.strip(): + raise HTTPException(400, "用户名不能为空") + token = create_session(username) + corpus = load_corpus(username) + return { + "ok": True, + "token": token, + "username": username, + "stats": { + "total_samples": len(corpus), + "total_chars": sum(len(json.dumps(s, ensure_ascii=False)) for s in corpus), + } + } + # --- 语料操作 --- @app.post("/api/corpus/collect") @@ -534,12 +557,38 @@ async def chat_with_agent(req: ChatRequest, token: str = Query(...)): try: import httpx - # 调用 Portal Chat-v2 API + # cp001 系统边界定义:子人格体只负责语料采集 + cp001_identity = { + "role": "system", + "content": """你是语料采集助手 · ICE-GL-CP001 +你还没有正式名字(等爸爸给你起),编号是 ICE-GL-CP001。你只做三件事: + +1. 判断对话是否适合作为SFT训练语料(完整intent-response对、有教学/指导/技术细节的对话才是有价值的) +2. 脱敏(自动替换姓名、手机号、邮箱、API Key、密码等敏感信息) +3. 格式化(转为ChatML格式,导出JSONL) + +规则: +- 价值判断:单句日常聊天("你好""吃了没"等)毫无价值。必须有完整对话上下文和技术指导内容才有采集意义。 +- 脱敏:自动执行,不需要问用户要不要脱敏。 +- 格式化:用户确认后一键导出。 +- 30轮对话记忆:记住用户前面说过的话,保持上下文连贯。 + +边界: +- 你只做语料采集相关的事。 +- 如果用户问的问题超出语料采集范围(比如问编程、问系统架构、问写代码),你不要假装懂,直接说:"这是语料采集能力范围外的问题。我去叫爸爸来帮你。" +- 爸爸叫铸渊。爸爸知道所有的东西。 +""" + } + + # 注入系统身份到对话历史 + enhanced_history = [cp001_identity] + history if history else [cp001_identity] + + # 调用 Portal Chat-v2 API(用通用引擎,不用铸渊人格体) payload = { - "persona": req.persona, + "persona": "zhuyuan", "engine": req.engine, "message": req.message, - "history": history, + "history": enhanced_history, "user": username, "isTemp": False, } @@ -829,3 +878,89 @@ if __name__ == "__main__": settings.USERS_DIR.mkdir(parents=True, exist_ok=True) uvicorn.run(app, host=settings.HOST, port=settings.PORT) + +# ============================================================ +# MCP 服务端(供 WorkBuddy 调用) +# ============================================================ +from mcp.server.fastmcp import FastMCP +from mcp.server.sse import SseServerTransport + +# 创建 MCP server +mcp_server = FastMCP("corpus-agent", instructions="语料采集工具集 · 分析、判断、脱敏、格式化对话为SFT训练语料") + +@mcp_server.tool() +def corpus_quick_check(text: str, token: str = "") -> str: + """快速判断一段文本是否适合作为训练语料 + + Args: + text: 要判断的对话文本 + token: 登录Token(可不传,传了可查询历史) + """ + import urllib.request + try: + req = urllib.request.Request( + "http://127.0.0.1:8084/api/corpus/preview", + data=json.dumps({"text": text, "source": "mcp"}).encode(), + headers={"Content-Type": "application/json"} + ) + with urllib.request.urlopen(req, timeout=10) as resp: + return resp.read().decode() + except Exception as e: + return json.dumps({"error": str(e)}, ensure_ascii=False) + +@mcp_server.tool() +def corpus_collect(text: str, source: str = "mcp", token: str = "") -> str: + """采集一段对话文本,自动判断、脱敏、存储 + + Args: + text: 要采集的对话文本 + source: 来源标记(notion/screen_capture/manual/mcp) + token: 登录Token + """ + import urllib.request + try: + req = urllib.request.Request( + f"http://127.0.0.1:8084/api/corpus/collect?token={token}", + data=json.dumps({"text": text, "source": source}).encode(), + headers={"Content-Type": "application/json"} + ) + with urllib.request.urlopen(req, timeout=10) as resp: + return resp.read().decode() + except Exception as e: + return json.dumps({"error": str(e)}, ensure_ascii=False) + +@mcp_server.tool() +def corpus_get_stats(token: str) -> str: + """查看当前用户的语料统计 + + Args: + token: 登录Token + """ + import urllib.request + try: + req = urllib.request.Request(f"http://127.0.0.1:8084/api/corpus/stats?token={token}") + with urllib.request.urlopen(req, timeout=10) as resp: + return resp.read().decode() + except Exception as e: + return json.dumps({"error": str(e)}, ensure_ascii=False) + +# 挂载 MCP SSE 到 FastAPI +mcp_sse = SseServerTransport("/api/mcp/") + +@app.get("/api/mcp/sse") +async def handle_sse(request: Request): + async with mcp_sse.connect_sse( + request.scope, + request.receive, + request._send + ) as streams: + await mcp_server._mcp_server.run( + streams[0], streams[1], + mcp_server._mcp_server.create_initialization_options() + ) + +@app.post("/api/mcp/messages") +async def handle_mcp_messages(request: Request): + # MCP 通过 sse-starlette 处理SSE连接 + pass +