# ============================================================================= # Life Echo API — 模板(example) # # 目录结构与 api/.env.development 对齐,便于对照;占位键见各段注释。 # 本地:复制为 .env.development(勿提交密钥),再运行 api/development.sh 会在首次自动生成 .env(从 # .env.development 复制);Settings 只读 .env(见 app/core/config.py)。 # 服务端:仓库维护 .env.staging / .env.production;workflow 按目标环境上传并复制为运行时 .env,compose 的 env_file 统一指向 .env。 # 不要把真实密钥提交到仓库。 # ============================================================================= # ============================================================================= # Docker Compose(宿主机独立 Caddy 反代到本 API) # ============================================================================= # 映射到宿主机的端口:不设置则由 Docker 随机分配,避免与同机其它项目冲突;随机时用 `docker compose port api 8000` 查看。 # 需固定端口时取消下行注释并改为未占用端口,Caddyfile 中 reverse_proxy 到 127.0.0.1:该端口。 # LIFE_ECHO_API_HOST_PORT=8000 # 若 Caddy 跑在独立容器且非 host 网络,不要用 127.0.0.1,应把 Caddy 加入与本 compose 相同的 Docker 网络,并对 http://life-echo-api-prod:8000 做 reverse_proxy。 # ============================================================================= # Logging(loguru sink 最低级别:TRACE / DEBUG / INFO / WARNING / ERROR / CRITICAL) # ============================================================================= # 生产/预发:保持 INFO,避免 DEBUG 把全文 prompt/响应打进日志。排查 Agent 耗时可仅开 LOG_AGENT_VERBOSE。 LOG_LEVEL=INFO # Agent 单行 INFO 摘要(耗时、sha、字符数);与 LOG_LEVEL 独立,生产可短时设为 1 # LOG_AGENT_VERBOSE=0 # DEBUG 下 prompt/响应预览最大字符数(Settings 默认 4096);0=不截断全文(慎用) # AGENT_LOG_MAX_CHARS=4096 # DEBUG 下 *.prompt:preview=截断预览 | hash_only=仅 sha12+长度,无正文 # AGENT_LOG_PROMPT_MODE=preview # DEBUG 下同一 label 连续相同 prompt 则跳过重复行(减模板重复) # AGENT_LOG_PROMPT_DEDUP=0 # DEBUG 下访谈/资料:省略 SystemMessage 正文(仅 total_len+sha12);0/false=打出全文 # AGENT_LOG_OMIT_SYSTEM_MESSAGE_BODY=1 # DEBUG 下超长单段 *.prompt:总长超过下一项时,先跳过前 N 字符再预览(0=不跳过;短时 DEBUG 可设 2500–8000) # AGENT_LOG_JSON_PROMPT_PREFIX_CHARS=0 # AGENT_LOG_JSON_PROMPT_PREFIX_ONLY_IF_LEN_GT=4000 # 第三方 stdlib logging(空=自动:LOG_LEVEL 为 DEBUG/TRACE 时 Celery→INFO;否则 Celery 与 httpx 默认 WARNING;需原始框架行时设为 INFO) # CELERY_LOG_LEVEL= # HTTPX_LOG_LEVEL= # 聚合用 JSONL(空=不写);与 stderr 并存,loguru serialize=True、按 20MB 切割、保留 7 天 # LOG_JSON_FILE=/var/log/life-echo/app.jsonl # ============================================================================= # LLM / DeepSeek # ============================================================================= DEEPSEEK_API_KEY=your_deepseek_api_key DEEPSEEK_BASE_URL=https://api.deepseek.com # 官方新模型名见 https://api-docs.deepseek.com/zh-cn/quick_start/pricing DEEPSEEK_MODEL=deepseek-v4-flash # v4-flash 主链路非思考须显式关(对齐旧版 deepseek-chat;默认 false) # DEEPSEEK_THINKING_ENABLED=false # ============================================================================= # Memory 向量(智谱 BigModel 国内 embedding-3;与 DeepSeek/OpenAI 用途分离) # 文档:https://docs.bigmodel.cn/cn/guide/models/embedding/embedding-3 # 本期固定 1024 维;库表经迁移与 MEMORY_EMBEDDING_DIMENSION 一致。 # ============================================================================= ZHIPU_API_KEY=your_zhipu_api_key # 默认国内通用端点(与 ZhipuAiClient 一致) # EMBEDDING_BASE_URL=https://open.bigmodel.cn/api/paas/v4 EMBEDDING_MODEL=embedding-3 # Chat 访谈:每轮根据用户内容判定主人生阶段(关则仅用关键词,省一次 LLM) # CHAT_STAGE_DETECTION_ENABLED=true # CHAT_STAGE_DETECTION_MAX_TOKENS=128 # 年代/流行文化联想块(config 默认 true;若减少「文艺硬接」可设 false) # CHAT_ERA_CONTEXT_ENABLED=true # 访谈性格(InterviewAgent):default | warm_listener | curious_guide(config 默认 default) # CHAT_INTERVIEW_PERSONA=default # 访谈回复长度档位(brief/standard/expanded)联动:极短输入 / 默认 / 长段+新细节(若与当前代码不一致以 config 为准) # CHAT_INTERVIEW_BRIEF_MAX_TOKENS=240 # CHAT_INTERVIEW_BRIEF_MAX_CHARS_PER_SEGMENT=180 # CHAT_INTERVIEW_EXPANDED_MAX_TOKENS=400 # CHAT_INTERVIEW_EXPANDED_MAX_CHARS_PER_SEGMENT=300 # 访谈/开场采样温度(config 默认 0.93;偏「好访谈者」体验时可试 0.60~0.70) # CHAT_INTERVIEW_TEMPERATURE=0.93 # 访谈主回复:统一 max_tokens / 单段字数(代码截断) # CHAT_INTERVIEW_MAX_TOKENS=512 # CHAT_INTERVIEW_MAX_CHARS_PER_SEGMENT=380 # CHAT_INTERVIEW_MAX_SEGMENTS=2 # 访谈:是否按本轮用户话检索记忆并注入提示词(关则不调 retrieve) # CHAT_MEMORY_RETRIEVAL_ENABLED=true # CHAT_MEMORY_TOP_K=8 # CHAT_MEMORY_EVIDENCE_MAX_CHARS=4096 # 规则 TurnPlan 之后再调一轮 JSON focus planner(config 默认 false;开启则多一次 LLM) # CHAT_REPLY_PLANNER_LLM_ENABLED=true # CHAT_REPLY_PLANNER_MAX_TOKENS=256 # CHAT_REPLY_PLANNER_TEMPERATURE=0.2 # Memoir:批处理/抽取更新 slot 时是否允许改写 MemoirState.current_stage(默认 false,访谈 switch_stage 仍可推进) # True 时仅当 proposed 与 existing 在同一 chat_bucket 才对齐 current_stage # MEMOIR_EXTRACTION_UPDATES_CURRENT_STAGE=false # Memoir:叙事前口述归一(segment 原文仍落库;仅 story 流水线派生输入) # MEMOIR_ORAL_NORMALIZE_ENABLED=true # off | rules | llm(llm 为先规则再 LLM 纠错,失败回退规则结果) # MEMOIR_ORAL_NORMALIZE_MODE=llm # MEMOIR_ORAL_NORMALIZE_LLM_MAX_TOKENS=512 # MEMOIR_ORAL_NORMALIZE_LLM_MAX_INPUT_CHARS=8000 # Chat:模型消费净稿(segment 原文仍落库;访谈编排层归一后注入 Agent / 记忆检索) # CHAT_INPUT_NORMALIZE_ENABLED=true # off | rules | llm(llm 为先规则再 LLM;失败回退规则;编排层已带 LLM 时不重复在 Agent 调) # CHAT_INPUT_NORMALIZE_MODE=rules # CHAT_INPUT_NORMALIZE_LLM_MAX_TOKENS=512 # CHAT_INPUT_NORMALIZE_LLM_MAX_INPUT_CHARS=8000 # True:仅 is_from_voice 时走 LLM 纠错;键盘输入仅规则归一 # CHAT_INPUT_NORMALIZE_LLM_VOICE_ONLY=true # Memoir Phase1:True 时用一次「批量 JSON」做抽取+分类(单段或多段均可;失败自动回退逐段)。 # False 时始终逐段(与启用本开关前的行为一致,含防抖合并后的多段任务)。 # MEMOIR_PHASE1_BATCH_LLM_ENABLED=false # MEMOIR_PHASE1_BATCH_LLM_MAX_TOKENS=4096 # ============================================================================= # Database # ============================================================================= # 本地开发(docker-compose.dev.yml 固定宿主端口 48291,避免与本机 5432 冲突) # DATABASE_URL=postgresql://postgres:postgres@localhost:48291/life_echo # Docker / 服务端(主机名一般为 compose 服务名 postgres): # DATABASE_URL=postgresql://postgres:postgres@postgres:5432/life_echo DATABASE_URL=postgresql://postgres:postgres@localhost:48291/life_echo # 启动时 Alembic(main.py);生产可设 ALEMBIC_STARTUP_FAIL_FAST=true,迁移失败则拒绝启动 # ALEMBIC_RUN_ON_STARTUP=true # ALEMBIC_STARTUP_FAIL_FAST=false # ALEMBIC_STARTUP_MAX_RETRIES=3 # ALEMBIC_STARTUP_RETRY_BASE_SECONDS=1.0 # ============================================================================= # Redis # ============================================================================= # 本地开发(docker-compose.dev.yml 固定宿主端口 48307,避免与本机 6379 冲突) # REDIS_URL=redis://localhost:48307/0 # Docker / 服务端: # REDIS_URL=redis://redis:6379/0 REDIS_URL=redis://localhost:48307/0 REDIS_SESSION_TTL=86400 # Celery:ingest 后 Memory LLM 富化任务投递队列(须被 worker 消费;见 README) # CELERY_MEMORY_ENRICHMENT_QUEUE=memory_idle # ============================================================================= # Internal evaluation API(internal_main / internal-eval.sh;与主 API 进程隔离) # ============================================================================= # 本地:`openssl rand -hex 32`;不用 internal eval 时可留空 INTERNAL_EVAL_API_KEY= # INTERNAL_EVAL_ENABLE_DOCS=1 # 评测台选 DeepSeek 评审:默认 deepseek-v4-flash + 非思考(与 https://api-docs.deepseek.com/zh-cn/quick_start/pricing 一致) # EVAL_JUDGE_DEEPSEEK_MODEL=deepseek-v4-flash # 仅写 v4-flash 模型 id 时是否启用思考(弃用名 deepseek-reasoner 仍始终为思考) # EVAL_JUDGE_DEEPSEEK_THINKING_ENABLED=false # ============================================================================= # Memory compaction(近重复 memory chunk 软排除;Celery + Redis 防抖) # 模板统一默认开启;须同时运行 celery worker 与 celery-beat(docker-compose 已含 beat,负责 memory_compaction_sweep)。 # ============================================================================= MEMORY_COMPACTION_ENABLED=true # MEMORY_COMPACTION_DEBOUNCE_SECONDS=105 # MEMORY_COMPACTION_LOCK_TTL_SECONDS=600 # MEMORY_COMPACTION_CHUNK_SIMILARITY_THRESHOLD=0.92 # MEMORY_COMPACTION_MIN_LAYERS_FOR_EXCLUDE=2 # MEMORY_COMPACTION_MAX_CHUNKS_PER_RUN=200 # MEMORY_COMPACTION_MAX_EXCLUDES_PER_RUN=50 # MEMORY_COMPACTION_MAX_NEIGHBORS_PER_CHUNK=25 # MEMORY_COMPACTION_TEXT_JACCARD_MIN=0.55 # MEMORY_COMPACTION_METADATA_EVENT_YEAR_WINDOW=1 # MEMORY_COMPACTION_SWEEP_RECENT_HOURS=24 # ============================================================================= # Story 流水线(post-commit、章节物化、append 上限、evidence 检索) # ============================================================================= # STORY_IMAGE_ENQUEUE_DEDUP_TTL=300 # RECOMPOSE_CHAPTER_DELAY_SECONDS=8 # CHAPTER_PIPELINE_LOCK_TTL_SECONDS=120 # STORY_APPEND_MAX_CANONICAL_CHARS=12000 # STORY_APPEND_MAX_VERSIONS=20 # EVIDENCE_TOP_K_DEFAULT=10 # EVIDENCE_TOP_K_LARGE_BATCH=5 # EVIDENCE_LARGE_BATCH_THRESHOLD=3 # # Memoir 可靠性(叙事 faithful、标题 slots、证据渗漏、Phase1→2 追踪) # MEMOIR_FIDELITY_FAIL_OPEN_ON_PARSE_ERROR=false # MEMOIR_NARRATIVE_EVIDENCE_OVERLAP_MIN_CHARS=14 # MEMOIR_EVIDENCE_SCENE_ANCHOR_CHECK_ENABLED=true # MEMOIR_TITLE_SLOTS_REQUIRE_BODY_OR_ORAL_MATCH=true # MEMOIR_TITLE_HAY_GROUNDING_STRICT_PHRASES_ENABLED=true # MEMOIR_RECOMPOSE_RETRY_ON_LOCK_CONTENTION=true # MEMOIR_PHASE2_SINGLEFLIGHT_IMMEDIATE=true # # ============================================================================= # Auth # ============================================================================= # 建议使用: openssl rand -hex 32 SECRET_KEY=replace_with_a_strong_random_secret ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=120 # 内网评测:开启后可用 POST /api/auth/mock/sms-login(跳过短信);APP_ENV=production 时该路由仍返回 404 # MOCK_SMS_LOGIN_ENABLED=1 # ============================================================================= # Tencent Cloud — 短信 # ============================================================================= # 短信、一句话 ASR/TTS、COS 为不同产品;同一主账号可共用同一对 SecretId/SecretKey(分别填三处)。 TENCENT_SMS_SECRET_ID=your_tencent_sms_secret_id TENCENT_SMS_SECRET_KEY=your_tencent_sms_secret_key # 短信应用 SDK AppID TENCENT_SMS_SDK_APP_ID=your_sms_sdk_app_id # 短信签名内容(不包含【】符号) TENCENT_SMS_SIGN_NAME=your_sms_sign_name # 短信模板 ID TENCENT_SMS_TEMPLATE_ID=your_sms_template_id # 短信模板参数数量(1=仅验证码,2=验证码+过期时间) # 若遇 TemplateParamSetNotMatchApprovedTemplate,请对照控制台模板配置 TENCENT_SMS_TEMPLATE_PARAM_COUNT=1 # ============================================================================= # ASR Provider(whisper | tencent) # ============================================================================= ASR_PROVIDER=whisper # ============================================================================= # Whisper ASR(ASR_PROVIDER=whisper 时使用) # ============================================================================= ASR_MODEL_SIZE=small ASR_DEVICE=cpu ASR_COMPUTE_TYPE=int8 # GPU 环境(示例,按需启用) # ASR_MODEL_SIZE=medium # ASR_DEVICE=cuda # ASR_COMPUTE_TYPE=float16 # ============================================================================= # Tencent Cloud — 一句话 ASR + TTS(ASR_PROVIDER=tencent 或 TTS_PROVIDER=tencent) # ============================================================================= TENCENT_SECRET_ID=your_tencent_asr_secret_id TENCENT_SECRET_KEY=your_tencent_asr_secret_key # ============================================================================= # TTS(文字转语音,Agent 回复朗读)— 与 ASR 独立 # ============================================================================= # ENABLE_TTS:关闭时禁用「助手每轮自动生成 TTS」(tts_this_turn 链路);不影响 WebSocket「按需朗读」tts_request。 # 每轮是否自动生成:客户端 `data.tts_this_turn`,且 ENABLE_TTS=true、skeleton skip_tts 均未阻止时才会合成。 ENABLE_TTS=true TTS_PROVIDER=tencent # 仅 TTS_PROVIDER=openai 时需要 # OPENAI_API_KEY= # 音色 ID 见 https://cloud.tencent.com/document/product/1073/92668 TTS_VOICE_TYPE=501004 TTS_CODEC=mp3 # ============================================================================= # WeChat Pay # ============================================================================= WECHAT_PAY_APP_ID=your_wechat_pay_app_id WECHAT_PAY_MCH_ID=your_wechat_mch_id WECHAT_PAY_API_V3_KEY=your_wechat_api_v3_key # 商户私钥:推荐使用文件路径,避免 .env 中长 PEM 转义问题 WECHAT_PAY_PRIVATE_KEY_PATH=certs/apiclient_key.pem # 若不用文件,可配置 WECHAT_PAY_PRIVATE_KEY(PEM,换行用 \n) # WECHAT_PAY_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" WECHAT_PAY_CERT_SERIAL_NO=your_wechat_cert_serial_no WECHAT_PAY_NOTIFY_URL=https://your-domain.com/api/payment/notify/wechat # 平台公钥模式(仅当无法走平台证书自动拉取时使用);勿填商户私钥路径 # WECHAT_PAY_PLATFORM_PUBLIC_KEY_PATH=certs/wechat_platform_public_key.pem # WECHAT_PAY_PLATFORM_PUBLIC_KEY_ID=your_wechat_platform_public_key_id # ============================================================================= # Alipay(未接入时可为空字符串) # ============================================================================= ALIPAY_APP_ID= ALIPAY_PRIVATE_KEY= ALIPAY_PUBLIC_KEY= ALIPAY_NOTIFY_URL=https://your-domain.com/api/payment/notify/alipay # ============================================================================= # Misc # ============================================================================= ENABLE_TEST_SUBSCRIPTION=0 # ============================================================================= # Memoir image generation(Story 主图等;轮询 Liblib 任务) # ============================================================================= MEMOIR_IMAGE_ENABLED=false MEMOIR_IMAGE_POLL_INTERVAL=3 MEMOIR_IMAGE_MAX_ATTEMPTS=20 MEMOIR_IMAGE_PROVIDER=liblib MEMOIR_IMAGE_STYLE_DEFAULT=watercolor MEMOIR_IMAGE_SIZE_DEFAULT=1280x720 # 章节正文内至少多少张 asset:// 插图才生成/展示章节封面(默认 1=有一张正文图即可) MEMOIR_MIN_INLINE_IMAGES_FOR_CHAPTER_COVER=1 # Story 正文至少多少字才生成主图 intent / 调图(0=不限制) STORY_IMAGE_MIN_BODY_CHARS=400 # 叙事模型输出相对口述过短则回退为口述原文 MEMOIR_NARRATIVE_FALLBACK_BODY_RATIO=0.5 MEMOIR_NARRATIVE_FALLBACK_MIN_CHARS=20 # 回忆录 segment 入队:累计 strip 后字数未达此值则暂缓提交 Celery(0=关闭字数门闸,仅静默防抖后提交) # MEMOIR_SEGMENT_BATCH_MIN_CHARS=50 # 本批首条入队起最长等待(秒),超时仍提交;测试可调低,生产可调高 # MEMOIR_SEGMENT_BATCH_MAX_WAIT_SECONDS=60 # 可选,Liblib 返回图片域名不在默认白名单时(逗号分隔) # MEMOIR_IMAGE_DOWNLOAD_HOSTS=liblib.cloud,liblibai.cloud # ============================================================================= # Liblib image provider # ============================================================================= LIBLIB_ACCESS_KEY=your_liblib_access_key LIBLIB_SECRET_KEY=your_liblib_secret_key LIBLIB_BASE_URL=https://openapi.liblibai.cloud LIBLIB_TEMPLATE_UUID=your_liblib_template_uuid # ============================================================================= # Tencent Cloud — COS(回忆录图片存储) # ============================================================================= TENCENT_COS_SECRET_ID=your_tencent_cos_secret_id TENCENT_COS_SECRET_KEY=your_tencent_cos_secret_key TENCENT_COS_REGION=ap-shanghai TENCENT_COS_BUCKET=your_bucket_name TENCENT_COS_BASE_URL=https://your_bucket_name.cos.ap-shanghai.myqcloud.com # 可选临时凭证 # TENCENT_COS_TOKEN=