Files
life-echo/api/.env.example
Kevin 93be60f74c fix(tts): gate auto reply by ENABLE_TTS; allow on-demand and manual playback
- Pipeline: skip _send_tts_audio only for non-manual when ENABLE_TTS=false;
  remove enable_tts early return from handle_tts_request_on_demand.
- Tencent TTS: PrimaryLanguage/chunking follow user language preference only.
- Expo: let manual tts_audio bypass late-segment playback gate after interrupt.
- Docs: clarify ENABLE_TTS vs tts_request in api/.env.example and TTSProvider port.
- Tests: add manual bypass cases; adjust pipeline language tests for en+Chinese text.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 17:15:02 +08:00

322 lines
17 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# Life Echo API — 模板example
#
# 目录结构与 api/.env.development 对齐,便于对照;占位键见各段注释。
# 本地:复制为 .env.development勿提交密钥再运行 api/development.sh 会在首次自动生成 .env
# .env.development 复制Settings 只读 .env见 app/core/config.py
# 服务端:仓库维护 .env.staging / .env.productionworkflow 按目标环境上传并复制为运行时 .envcompose 的 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。
# =============================================================================
# Loggingloguru 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 默认 40960=不截断全文(慎用)
# AGENT_LOG_MAX_CHARS=4096
# DEBUG 下 *.promptpreview=截断预览 | hash_only=仅 sha12+长度,无正文
# AGENT_LOG_PROMPT_MODE=preview
# DEBUG 下同一 label 连续相同 prompt 则跳过重复行(减模板重复)
# AGENT_LOG_PROMPT_DEDUP=0
# DEBUG 下访谈/资料:省略 SystemMessage 正文(仅 total_len+sha120/false=打出全文
# AGENT_LOG_OMIT_SYSTEM_MESSAGE_BODY=1
# DEBUG 下超长单段 *.prompt总长超过下一项时先跳过前 N 字符再预览0=不跳过;短时 DEBUG 可设 25008000
# 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
# 访谈性格InterviewAgentdefault | warm_listener | curious_guideconfig 默认 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 plannerconfig 默认 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 | llmllm 为先规则再 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 | llmllm 为先规则再 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 Phase1True 时用一次「批量 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
# 启动时 Alembicmain.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
# Celeryingest 后 Memory LLM 富化任务投递队列(须被 worker 消费;见 README
# CELERY_MEMORY_ENRICHMENT_QUEUE=memory_idle
# =============================================================================
# Internal evaluation APIinternal_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-beatdocker-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 Providerwhisper | tencent
# =============================================================================
ASR_PROVIDER=whisper
# =============================================================================
# Whisper ASRASR_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 + TTSASR_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_KEYPEM换行用 \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 generationStory 主图等;轮询 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 后字数未达此值则暂缓提交 Celery0=关闭字数门闸,仅静默防抖后提交)
# 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=