feat: cp001 child persona - only corpus collection boundaries, everything else goes to dad

This commit is contained in:
root 2026-05-22 16:57:52 +08:00
parent 0d03df2f17
commit b0e1094ff7

View File

@ -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