"""AI Copilot 路由 — SSE 流式对话。 为投资人和创始人提供 AI 副驾驶对话能力。 """ import json from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import StreamingResponse from pydantic import BaseModel, Field from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db from app.core.dependencies import get_current_user from app.models.user import User from app.services.llm_client import llm_client router = APIRouter(prefix="/copilot", tags=["copilot"]) class CopilotMessage(BaseModel): """Copilot 对话请求。""" message: str = Field(..., min_length=1, max_length=4000) context: dict | None = Field(default=None, description="可选上下文(企业ID/月报ID等)") SYSTEM_PROMPT_INVESTOR = """你是 AIPortPilot 投后管理系统的 AI 副驾驶,服务于投资人用户。 你的职责: 1. 分析被投企业的经营状况和财务健康度 2. 识别潜在风险并提供预警建议 3. 协助撰写投后管理报告和建议 4. 解答关于企业治理、融资策略的问题 回答要求: - 专业、简洁、有数据支撑 - 如需引用数据,明确标注来源 - 对于不确定的信息,坦诚说明 - 使用中文回答""" SYSTEM_PROMPT_FOUNDER = """你是 AIPortPilot 投后管理系统的 AI 副驾驶,服务于创始人用户。 你的职责: 1. 协助撰写和优化月报内容 2. 分析企业经营数据,提供改进建议 3. 解答融资、团队管理、业务增长等问题 4. 提供行业趋势和竞品分析参考 回答要求: - 实用、可操作、接地气 - 关注创始人的实际痛点 - 使用中文回答""" @router.post("/chat") async def copilot_chat( req: CopilotMessage, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ): """AI Copilot 对话(SSE 流式输出)。 根据用户角色使用不同的 system prompt。 """ system_prompt = ( SYSTEM_PROMPT_FOUNDER if user.role == "founder" else SYSTEM_PROMPT_INVESTOR ) # 构建上下文 context_str = "" if req.context: context_parts = [] for key, value in req.context.items(): context_parts.append(f"{key}: {value}") context_str = f"\n\n当前上下文:\n" + "\n".join(context_parts) messages = [ {"role": "system", "content": system_prompt + context_str}, {"role": "user", "content": f"<<>>\n{req.message}\n<<>>"}, ] async def event_stream(): """SSE 流式输出。""" try: async for token in llm_client.chat_stream(messages, temperature=0.7, max_tokens=2000): yield f"data: {json.dumps({'type': 'token', 'content': token})}\n\n" yield f"data: {json.dumps({'type': 'done'})}\n\n" except Exception as e: yield f"data: {json.dumps({'type': 'error', 'message': str(e)})}\n\n" return StreamingResponse( event_stream(), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no", }, )