""" 智能体唯一 HTTP 入口(合并原 8810/8809/8811)。 本文件职责:组装 FastAPI、挂载通道、启动 inbox/outbox 线程、暴露 /health。 禁止:在请求处理里同步 LibreOffice / 等完整 LLM / invoke 整图。 """ from __future__ import annotations import logging from contextlib import asynccontextmanager import uvicorn from fastapi import FastAPI from agent.channel.aibot import router as aibot_router from agent.channel.bridge import router as bridge_router from agent.channel.h5 import router as h5_router from agent.channel.runtime import start_channel_runtime from agent.channel.wecom.callback import router as wecom_router from agent.config import get_settings logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """ 进程生命周期:启动企微 inbox/outbox 消费线程。 为何放在 HTTP 进程:回调快回后需同机消费;与 LibreOffice 无关的快路径。 """ settings = get_settings() runtime = start_channel_runtime(settings) app.state.channel_runtime = runtime logger.info( "通道运行时就绪 ytd_env=%s wecom_agent_id=%s corp_configured=%s", settings.ytd_env, settings.wecom_agent_id, bool(settings.wecom_corp_id and settings.wecom_secret), ) yield runtime.stop() def create_app() -> FastAPI: """构建 FastAPI 应用(可供测试 import)。""" settings = get_settings() app = FastAPI( title="YTD Inquiry Agent HTTP", version="0.2.0", description="询价智能体 HTTP:企微回调入队 + echo 出站联调", lifespan=lifespan, ) @app.get("/health") def health() -> dict[str, object]: """存活探测:不访问 PG/Redis;附带通道配置是否齐全(不含密钥)。""" return { "status": "UP", "service": "agent-http", "ytd_env": settings.ytd_env, "port": settings.agent_http_port, "wecom_crypto_ready": bool( settings.wecom_callback_token and settings.wecom_encoding_aes_key and settings.wecom_corp_id ), "wecom_send_ready": bool(settings.wecom_corp_id and settings.wecom_secret), } app.include_router(wecom_router) app.include_router(h5_router) app.include_router(bridge_router) app.include_router(aibot_router) return app def main() -> None: """进程入口:默认端口 8910。""" logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s [%(name)s] %(message)s", ) settings = get_settings() # test 环境默认允许本机注入,便于并存期联调 if settings.ytd_env == "test" and not settings.dev_inject_enabled: # pydantic 冻结后不能改;通过环境变量在启动前设置更干净。此处仅打日志提示。 logger.info("提示:test 下可用 POST /internal/dev/inject-text 联调(无需改 Nginx)") app = create_app() logger.info( "启动智能体 HTTP ytd_env=%s port=%s", settings.ytd_env, settings.agent_http_port, ) uvicorn.run(app, host="0.0.0.0", port=settings.agent_http_port, log_level="info") if __name__ == "__main__": main()