Files
CyberGril/config/semantic.example.yaml
T
zonggongandmultica-agent 440d95cad8 HEL-889 rework: shutdown never closes under a live worker, and a queued-cancelled worker is released by the authoritative future state — SemanticMemory now runs batch workers on its OWN dedicated ThreadPoolExecutor and keeps each work item's concurrent.futures.Future: cancelling the awaiting tick propagates to that future, and its post-cancel state is the deterministic queued/started oracle (the manager's review_hel886_edges.py reproduced the old false-liveness on the shared default executor: register _live, await to_thread, executor busy -> cancel tick cancels the QUEUED item, _run and its finally never run, _live=1 forever with the batch pending and recovery permanently skipping a worker that does not exist, restart the only way out; now a CANCELLED future proves nothing ever ran: _release_never_started removes the registry entry idempotently and returns the batch to queued with its attempt restored and zero sends, while a running/finished future keeps HEL-886 semantics — the worker thread owns the batch fate, single send, no reclaim, no resend), and aclose becomes a real ownership protocol instead of a bounded promise it could break: a closing flag makes tick refuse new claims and makes both backends' _add_group_sync check should_stop BEFORE each row's wire work (a _ShutdownAbort at a boundary keeps finished rows' sem_row_done proofs and requeues only the unsent remainder — never a mid-row cut, and a single-row batch still completes done); never-started workers are cancelled out of the executor queue under the same authoritative CANCELLED proof; started workers finish their current row and flush, and aclose WAITS for every live worker to actually exit before backend/executor close — drain_timeout only bounds the polite phase (after expiry: honest warning, keep waiting; a live worker is never treated as dead), bounded in practice because Mem0Backend now sets the gateway OpenAI client's timeout (gateway_timeout config, default 60s) instead of the 600s default that made the drain effectively open-ended; a cancelled aclose closes NOTHING (shielded finish, closing signal reset, layer stays usable), concurrent aclose calls serialize on an asyncio.Lock and are idempotent, and the app lifespan comment now states the ownership contract — the manager's fixed-SDK reproduction of the OLD timeout branch (loopback gateway accepts the first HTTP then withholds the answer, aclose(drain_timeout=0.05) returns with the worker alive, app closes the store, late worker dies on 'QdrantLocal instance is closed'/'Cannot operate on a closed database', reopen shows pending/row_done=0/llm_used=1) is closed: aclose does not return while the worker is alive, the worker's writes land BEFORE the app-order store.close(), and the reopened store shows done + row_done=1 + full entry_ids with llm_used=1 and zero late errors; crash semantics unchanged and still honest (unknown wire outcomes billed, proof-gated retries, strict-once not claimed); verification: 294 python tests pass on 3.11.16 venv + requirements.lock offline (7 new tests/test_rework_hel889.py cases, all barrier-driven real product paths: queued-cancel release + in-process recovery via injected single-worker executor / shutdown-timeout never closes under the live worker and flushes before app-order store close / between-rows abort requeues and the next process sends ONLY the missing row / shutdown-before-start zero sends then resumes / cancelled aclose leaves everything open / concurrent aclose serializes / closed layer refuses new claims), ruff clean (src/tests/tools), no frontend changes (frontend checks not re-run, stated honestly); pinned mem0 2.2.1 on the deterministic boundary (512-dim stand-in before TextEmbedding construction, loopback-only): new tools/verify_shutdown_queued_hel889.py 12/12 (Q1 queued-cancel phantom released and recovered in-process with the manager's 41s lease probe, S1 fixed-SDK shutdown-timeout branch, S2 between-rows abort + missing-row-only resume proven by gateway POST counts, S3 cancelled aclose, S4 concurrent aclose), the manager's review_hel886_edges.py re-run VERBATIM on the pristine f1b50ad first reproduced both defects exactly (docs/rework/hel889/edges-verbatim-before-fix-f1b50ad.json matches the manager's independent JSON), the same-shape rerun review_hel886_edges_rerun_hel889.py now passes both scenarios with every original result key (queued: done/attempts=1/registered 0/entries 1/row_done 1; timeout: aclose_returned_while_worker_alive=false, late_worker_error null, persisted done+entry_ids+row_done=1, llm_used=1) while the verbatim original stops at its own premise assert executor._work_queue.qsize()==1 — batch workers no longer ride the loop default executor, the encoded defect is structurally gone (original negative and baseline kept); regressions all green: verify_inflight_lifecycle_hel886.py 6/6 (single-send preserved), verify_semantic_recovery_hel885.py 7/7, verify_mem0_real_hel874.py 12/12, check_release_offline_hel882.py R1-R7 15/15, both manager review_hel882_{partial,extra}.py scripts re-run verbatim pass, review_hel885_inflight.py still fails at its 2-add barrier exactly as documented in HEL-886; runbook §4 gains the HEL-889 ownership/queued-cancel semantics, config/semantic.example.yaml documents gateway_timeout, docs/requirements/handoff-HEL-889.json written, dual-target requirement checks 4/4 (origin/main and origin/agent/agent/de8874a3803b, with and without handoff); zero real LLM, zero real embeddings, zero non-loopback outbound, authoritative ledgers and freezes untouched, not deployed, PR not merged, no visual changes
Co-authored-by: multica-agent <github@multica.ai>
2026-10-27 01:24:11 +08:00

64 lines
4.1 KiB
YAML
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.
# CyberGril 语义记忆层(Mem0 固定源)脱敏配置示例
#
# 复制为 config/semantic.yaml 并由总管在获批准的预算数值内填写。
# embed_cap=0 / llm_cap=0 是安全默认:真实嵌入与 SDK 内部提取 LLM 全部关闭
# (fail-closed,任何真实工作前拒绝),不会碰 498/500 的冻结余额。只有白栖知
# 批准的新预算经总管核定后才可调高。硬闸与执行表见 docs/budget-plan-hel874.md。
#
# 后端选择(环境变量 CYBERGRIL_SEMANTIC):
# off —— 关闭(默认;对话记忆检索回落到 R3 词面 FTS 通道)
# stub —— 编排接通的自测后端(确定性子串匹配,非语义,不冒充语义能力)
# mem0 —— 真实固定源 Mem0(2c81af3bbe27d9bccb09c772d583df9e9be72ee5,mem0ai 2.2.1,
# 需发布 venv 已离线并入固定栈 wheel,见 deploy/hel874-release.md §4)
# HEL-874/882 已对固定安装实测:search 必须带 user_id 过滤、payload 直读走
# SDK 自带 client、内部 LLM 过独立 llm_ops 闸(证据 docs/rework/hel874/)。
# 嵌入账本:沿用 HEL-857 系列唯一权威账(embed_ops 表)续账,绝不另起新账;
# SDK 内部提取 LLM 记同库 llm_ops 新表(独立 cap,互不挪用)
#
# ── 累计上限算式(HEL-885,写入前照此换算,禁止直接填增量)───────────────
# 权威账现状:已用 498 条 / 原批上限 500(其中冻结不动 2 条余额)。
# 白栖知批准的 B 部分新增嵌入额度(例如 180 条)并入同一累计账:
# embed_cap = 500 + 180 = 680 # 原上限 + 新批增量(不是 180!)
# embed_reserve = 2 # 原冻结余额 2 条继续保留不动
# 可用余量 = 680 − 498 − 2 = 180
# 错误示例:embed_cap: 180(对已用 498 的账等于瞬间耗尽并掩盖历史);
# 动到旧冻结 2 条(min_used: 498 会拒绝)。
ledger_path: /absolute/path/to/embed_ledger.sqlite3
embed_cap: 0 # 本产品环境允许使用的嵌入条数累计上限;0=未批准=全部拒绝
embed_daily: 0 # 嵌入条数日顶(执行表 C:建议 60/日,待批);0=未批准=拒绝
embed_reserve: 0 # 预留不动的余量(例如为复测保留 40;冻结口径见上方算式)
llm_cap: 0 # Mem0 内部提取 LLM 调用累计上限(执行表 B:建议 60,待批)
llm_daily: 0 # Mem0 内部提取 LLM 日顶(执行表 C:建议 40/日,待批)
# 冻结前缀(HEL-885 起为必填,不可注释掉):只要 embed_cap / llm_cap 任一 >0,
# 缺少、留空或填错的 authority 块都会让 mem0 模式在构造前直接拒绝
# (ledger_authority_required / ledger_authority_*,fail-closed,不降级)。
# ledger_path 必须仍是含冻结前缀的权威账——指向新空文件重开余额同样被拒。
# 数值由总管按权威账现状填写(前 N 行逐行 SHA256 与最少已用条数):
authority:
prefix_rows: 181
prefix_sha256: "<权威账前 N 行逐行摘要,总管填写>"
min_used: 498
# 记录作用域:add(user_id=) 与 search(filters={"user_id":…}) 必须一致;
# mem0 2.2.1 拒绝无过滤 search,此键一般不需要改
user_scope: cybergril
# 网关(现有配置同源,config/models.yaml 的 halogen 网关)
gateway_base_url: http://192.168.200.11:8731/v1
gateway_model: halogen-qwen3.8-flash-next
gateway_key: cybergril-local-test
# 每次网关调用的硬超时(秒,HEL-889):关闭排空阶段因此有界——已被接受但
# 长期不答的请求不会把 worker 与进程关闭无限挂住。默认 60,一般不改。
gateway_timeout: 60
# 统一请求闸:产品进程会把 router 的 PersistentBudget 传给后端,SDK 内部提取
# LLM 每次发送前在 "semantic" 类别 + 日 token 池里原子预占(models.yaml 的
# daily_requests.semantic 与 daily_tokens 批准值生效),无需在此重复配置。
# 本地向量库与推导史目录(独立数据目录,随实例一起备份/回滚)
vector_root: /absolute/path/to/sem/vec
history_root: /absolute/path/to/sem/hist
collection: cg_public