luopan_chat
Ask follow-up questions about a Bazi chart reading. Provide a session_id from luopan_analyze to continue, with up to 5 questions per session and a 2-hour expiry.
Instructions
Ask a follow-up question against an existing chart-reading session.
Session_id comes from a prior luopan_analyze call. The backend enforces a 2-hour TTL and a 5-question cap; when either is hit, call luopan_analyze again to open a new session.
Returns (normalized): answer: the AI reply text followup_remaining: questions you still have left after this one followup_count: questions used so far (incl. this one) max_followups: hard cap (5) session_id: echoed back
When followup_remaining == 0 or this call returns session_expired,
the next turn must call luopan_analyze to open a fresh session.
When followup_remaining == 1, warn the user before they spend it.
Args: session_id: The session_id returned by luopan_analyze question: The user's follow-up question (specific events / years / topics yield better answers than vague "is my fate good")
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| session_id | Yes | ||
| question | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/mcp_luopan/server.py:100-150 (handler)Main handler for the 'luopan_chat' tool. Validates session_id and question, delegates async HTTP POST via LuopanClient.chat(), normalizes the response with followup_remaining/count/max_followups/session_id, and handles errors (session_expired, service_unreachable, internal).
@mcp.tool() async def luopan_chat(session_id: str, question: str) -> str: """Ask a follow-up question against an existing chart-reading session. Session_id comes from a prior luopan_analyze call. The backend enforces a 2-hour TTL and a 5-question cap; when either is hit, call luopan_analyze again to open a new session. Returns (normalized): answer: the AI reply text followup_remaining: questions you still have left after this one followup_count: questions used so far (incl. this one) max_followups: hard cap (5) session_id: echoed back When `followup_remaining == 0` or this call returns `session_expired`, the next turn must call luopan_analyze to open a fresh session. When `followup_remaining == 1`, warn the user before they spend it. Args: session_id: The session_id returned by luopan_analyze question: The user's follow-up question (specific events / years / topics yield better answers than vague "is my fate good") """ if not session_id or len(session_id) > 64: return _err("bad_input", "session_id 无效") if not question.strip(): return _err("bad_input", "问题不能为空") try: data = await _client.chat(session_id, question.strip()) except LuopanServiceError as e: if e.kind == "session_expired": return _err("session_expired", "此盘气已散(session 过期或追问已尽),需重新起盘。") if e.kind == "service_unreachable": return _err("service_unreachable", f"罗盘后端未就绪:{_config.api_base}。请先启动 uvicorn。") return _err(e.kind, e.detail) except Exception as e: # noqa: BLE001 logger.exception("luopan_chat unexpected error") return _err("internal", str(e)) count = data.get("followup_count", 0) cap = data.get("max_followups", 5) normalized = { "answer": data.get("reply", ""), "followup_remaining": max(cap - count, 0), "followup_count": count, "max_followups": cap, "session_id": data.get("session_id", session_id), } return _dump(normalized) - src/mcp_luopan/server.py:100-101 (registration)The tool is registered via FastMCP's @mcp.tool() decorator on the luopan_chat function. No separate registration table; decorator-based registration on line 100.
@mcp.tool() async def luopan_chat(session_id: str, question: str) -> str: - src/mcp_luopan/server.py:102-123 (schema)Schema/contract for the tool: parameters (session_id: str, question: str) and return structure (answer, followup_remaining, followup_count, max_followups, session_id). Defined via docstring and the normalized dict in the response.
"""Ask a follow-up question against an existing chart-reading session. Session_id comes from a prior luopan_analyze call. The backend enforces a 2-hour TTL and a 5-question cap; when either is hit, call luopan_analyze again to open a new session. Returns (normalized): answer: the AI reply text followup_remaining: questions you still have left after this one followup_count: questions used so far (incl. this one) max_followups: hard cap (5) session_id: echoed back When `followup_remaining == 0` or this call returns `session_expired`, the next turn must call luopan_analyze to open a fresh session. When `followup_remaining == 1`, warn the user before they spend it. Args: session_id: The session_id returned by luopan_analyze question: The user's follow-up question (specific events / years / topics yield better answers than vague "is my fate good") """ - src/mcp_luopan/client.py:57-61 (helper)Helper HTTP client method 'chat' on LuopanClient that calls the backend POST /api/chat with session_id and question. This is the underlying network call supporting luopan_chat.
async def chat(self, session_id: str, question: str) -> dict[str, Any]: return await self._post( "/api/chat", {"session_id": session_id, "question": question}, ) - src/mcp_luopan/server.py:35-41 (helper)Helper functions _err (error response) and _dump (JSON formatting) used by the luopan_chat handler.
def _err(kind: str, hint: str) -> str: return json.dumps({"error": kind, "hint": hint}, ensure_ascii=False) def _dump(payload: dict) -> str: return json.dumps(payload, ensure_ascii=False, indent=2)