Skip to content
AirObjectPublic

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Iris:试用对话、记忆管理与宿主接口

需要 uv 和 Python 3.12 及以上。开发设备为 macOS,仓库内 .venv 使用 Python 3.13;以下命令也适用于 Linux 和 Windows PowerShell:

uv sync --python 3.13
uv run pytest
uv run --locked --isolated --python 3.12 pytest

两个版本的测试依次运行,不同时运行;3.12 使用临时环境,保持仓库 .venv 为 3.13。目前只在 macOS 验证,Windows 验证暂停。

可用 uv run iris setup --name Iris 创建初始角色。学习命令行可用 uv run iris ingest messages.jsonl 接收 UTF-8 消息,再执行 uv run iris learn <入口标识> --force。JSONL 每行至少包含 entry_id、platform、sender、content、带时区的 ISO occurred_at 和入口内唯一的 dedupe_key;可选 kind 为 message、self_output、action_result 或 event。引用可带 quote_author、quote_author_account_id 和 quote_content;场景事件有固定的“场景”主体,行动结果属于“我”。

把 test-models.example.toml 复制为仓库根目录的 test-models.toml 并填写测试模型配置;该文件被 Git 忽略。在其他 worktree 中设置 IRIS_TEST_MODELS 指向同一份配置的绝对路径,不复制密钥文件。2026-10-05 起对话模型为 glm-5.3-flash,embedding 为 doubao-embedding-vision。方舟 GLM 在 [chat] 中显式设置 reasoning_effort = "high"(依据 DECISIONS.md 2026-10-08「GLM 默认推理档位改为 high」);它不能关闭推理。high 的学习调用更慢(隐藏集上批次 P95 约 35—43 秒),实时入口的 1 分钟目标需在预演中复核。此字段是可选的服务商字符串,未配置时请求中不发送,其他 OpenAI 兼容模型按自身文档选择。iris models check 显示当前档位。然后运行:

uv run iris models check
uv run iris eval learning

用 IRIS_TEST_MODEL_PROFILE=chat2 选择同一 TOML 中的 [chat2] 作为对话配置(例如 IRIS_TEST_MODEL_PROFILE=chat2 uv run iris models check);不设置此变量时仍使用 [chat] 和各用途自己的节。选中档位必须有非空 base_url、model,以及 api_key(无密钥服务可显式填空字符串)或 api_key_env。选择档位后,学习等 chat 用途、召回判断、目标去重判断都使用该档位,忽略文件中的独立判断及图片理解节,embedding 保持原配置。图片理解由支持图片的选中 chat 派生;未声明图片能力时仍用占位。派生用途只继承来源显式配置的 reasoning_effort;来源未配置时不发送该字段,GLM 显式配置的 high 保持不变。

支持图片的对话模型可在对应 TOML 节设置 vision = true,默认 false。这是 Iris 的能力标记,不发送给服务商,也不改变纯文本请求。本地模型设置保存同一字段;PUT /admin/api/settings/models/chat 接受布尔值 vision,省略时保留已有值,旧配置默认 false,保存照常留下操作记录。当前界面尚无此勾选项。

本机服务

uv run iris

首次运行会创建 ./data 并自动在系统浏览器打开 http://127.0.0.1:8080/setup。在页面设置管理员密码、角色名、可选背景和模型即可进入试用;不配置模型也可以完成,顶部持续提示“未配置模型,暂不学习”,接收与全文召回照常。uv run iris serve --no-open 关闭自动打开。运行服务无需 Node。默认仍只监听回环地址;Docker 镜像支持 Linux amd64/arm64,部署方式见下文。

部署优先级为 命令行 > 环境变量 > iris.toml > 默认值。环境变量为 IRIS_DATA_DIR、IRIS_HOST、IRIS_PORT、IRIS_ALLOWED_HOSTS;当前目录的 iris.toml 只接受以下顶层项,可用 --config <文件> 指定其他文件。文件中的相对数据目录相对该配置文件解析。--data-dir 放在子命令前;兼容原来的 --db,显式传入时其父目录就是数据目录。

data_dir = "./data"
host = "127.0.0.1"
port = 8080
allowed_hosts = []
uv run iris --data-dir /path/to/iris-data serve --port 8081 --no-open

角色、模型的非敏感字段、每日 token 上限与学习并发保存在 iris.db;API key 仅在同目录的 secrets.json,Unix 权限为 0600,页面与接口只显示是否已设置。模型配置优先级:serve --models-config <文件> > IRIS_TEST_MODELS > 数据库设置+secrets.json。前两种模式明确显示“配置来自外部文件,只读”,不自动打开浏览器,但管理界面仍要求设置密码/登录。未显式指定时,serve 不会隐式读取当前目录的 test-models.toml;评测和旧离线命令仍沿用测试模型配置约定。

开发时可用下面的命令导入,文件由程序读取,命令不显示密钥。导入可以在服务运行时执行;本地配置模式下自动重载并恢复对应用途,外部配置模式仍以外部文件为准。导入和页面保存遇到配置锁占用时最多等待 3 秒,超时提示稍后重试;后台重载遇忙留到下次轮询。要在页面编辑导入后的模型,启动服务时须取消 IRIS_TEST_MODELS。

IRIS_TEST_MODELS=/absolute/path/test-models.toml uv run iris --data-dir /path/to/iris-data models import
# 也可用 models import --from /absolute/path/test-models.toml
# 随后在没有 IRIS_TEST_MODELS 的环境中启动:
uv run iris --data-dir /path/to/iris-data serve

/setup、/login 及其静态资源可在本机打开;完成设置后,试用/记忆/入口与学习/状态/设置页与管理接口均要求管理员会话。交互接口文档 /docs、/openapi.json 也要求登录。宿主 /api/v1 自 M4 起需要宿主令牌(见下文「宿主接入」);普通模式下服务只绑定回环地址;仅 IRIS_CONTAINER=1 允许非回环监听。

服务还会在所有路由之前校验 HTTP Host,默认接受 127.0.0.1、localhost、[::1](可带端口);可用 iris.toml 的 allowed_hosts 列表或 IRIS_ALLOWED_HOSTS 逗号列表增加明确的主机名。其他域名即使解析到回环地址,也返回 400,不返回业务数据或执行操作;界面、静态资源、/admin/api、/api/v1、接口文档均受保护。此检查用于阻断 DNS 重绑定,不解析域名、不信任转发 Host,也不启用 CORS。它与管理员会话和 CSRF 校验共同生效。白名单不接受 URL 或通配符;请求可带端口,配置项若附端口也只取主机名。未列入白名单的 Docker 服务名同样被拒绝。浏览器通过本机映射地址访问时仍执行同源 Origin、Sec-Fetch-Site 与 CSRF 校验。

试用页在角色回复失败时也显示已完成准备的召回候选编号、当时修订、支持分与降级原因。复用已发布回复时标明原回复编号、准备时间与召回记录编号,不重新准备或调用模型。诊断附在既有召回记录中,只增加编号、修订、分数与结果等元数据,不复制准备材料正文、不增加使用次数;失败响应仍为 502,通过 result.diagnostic 提供诊断,成功/复用响应为 diagnostic。诊断中的记忆链接打开当前详情。旧回复没有记录、或原召回记录已按 30 天保留期限清理时会明确提示不可还原。

Docker 部署(0.0.1)

镜像 ghcr.io/airobject/iris-core:0.0.1 同时提供 linux/amd64、linux/arm64,另有 latest 标签。推荐固定版本;iris --version 可查看已安装版本。镜像基于固定的官方 Python 3.13 小版本,以 UID/GID 10001:10001 运行,默认 0.0.0.0:8080,数据目录 /data,健康检查只访问本机 /login,不需要宿主令牌。

只把端口映射到本机,或把 Iris 放在可信 Docker 网络中;不要直接暴露到公网,本服务不提供 TLS。 只有环境变量严格等于 IRIS_CONTAINER=1 才允许非回环监听;true 等其他值不会开启。镜像已设此开关,普通安装默认关闭。Host 白名单、宿主令牌、管理员会话和 CSRF 仍全部生效,不信任代理头,也不启用 CORS。

AstrBot 在宿主机时:

docker run -d --name iris -p 127.0.0.1:8080:8080 -v iris-data:/data ghcr.io/airobject/iris-core:0.0.1
docker logs iris

从启动日志取得“首次设置码”,浏览器打开 http://127.0.0.1:8080/setup,按表单填写设置码、管理员密码、角色和模型。Docker 的端口转发通常使请求来自容器网桥地址,因此经本机映射访问也会要求设置码。插件地址填 http://127.0.0.1:8080,在管理页“接入”创建宿主令牌后填入插件。

还没有管理员密码时,每次启动生成 256 位随机设置码;内存只存 SHA-256 摘要,明文只输出到服务控制台//data/logs/iris.log,不会存入数据库或 API 响应。管理员密码建立后立即失效,不用等待其余设置完成;中途重启可用管理员密码登录继续。尚未建立密码就重启时,必须改用新日志中的设置码。回环客户端可沿用不填码的首次设置。请勿分享包含设置码的启动日志。

AstrBot 也在 Docker 中时,将它的现有服务配置接入同一网络,保留原镜像、端口和数据卷;下面展示要合并的服务与网络配置,ASTRBOT_IMAGE 填目前使用的 AstrBot 镜像,不要求升级插件或连接器:

services:
  iris:
    image: ghcr.io/airobject/iris-core:0.0.1
    environment:
      IRIS_ALLOWED_HOSTS: iris
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - iris-data:/data
    networks: [iris-net]
  astrbot:
    image: ${ASTRBOT_IMAGE:?请填写现有 AstrBot 镜像}
    # 保留现有 AstrBot 的数据卷和端口设置。
    networks: [iris-net]
networks:
  iris-net:
volumes:
  iris-data:

用 docker compose logs iris 取设置码,经本机浏览器完成设置后,AstrBot 插件填 http://iris:8080 和宿主令牌。IRIS_ALLOWED_HOSTS=iris 只增加服务名,不允许其他任意 Host;默认白名单仍保留本机地址。允许访问 Iris 的网络应只加入可信容器。需要通过文件配置时,iris.toml 可写 allowed_hosts = ["iris"] 并以 --config 指定;环境变量优先,空字符串可清空附加名单。

模型可在页面配置;也可在创建容器时把现有模型配置只读挂载到例如 /config/models.toml,再执行:

docker exec iris iris --data-dir /data models import --from /config/models.toml

配置文件和密钥不放进镜像,不在命令行参数中填写密钥。容器中的 127.0.0.1 指 Iris 容器本身;模型在其他容器时填该服务地址,模型在宿主机时按 Docker 的宿主网络配置填写可达地址。

推荐在管理页“接入”创建令牌;离线命令须先停止服务,沿用相同数据卷,令牌仅在创建输出中显示一次:

docker stop iris
docker run --rm -v iris-data:/data ghcr.io/airobject/iris-core:0.0.1 tokens create --host astrbot --prefix 'astrbot:'
docker start iris

/data 包含数据库、媒体、模型密钥、日志和备份,重启或替换容器时保留同一数据卷。新命名卷会继承镜像的目录权限;绑定宿主目录时须事先让 UID/GID 10001 可写。每日定时备份默认在 /data/backups,保留 7 份且不含模型密钥;仍应把备份保存到数据卷之外,管理页也可下载在线导出。恢复使用停服后的 backup import,规则见“在线导出与离线导入”。不要把删除数据卷当作容器升级步骤。

发布流程由维护者推送与 pyproject.toml 版本一致的 v* 标签触发:依次通过 Python 3.13、3.12 全量测试与 uv build 后,使用仓库自带的 GITHUB_TOKEN 推送双架构版本镜像和 latest,创建 GitHub Release 并附 wheel、sdist。版本不匹配或任何验证失败都会阻止发布,无需额外密钥。

首次设置、登录与设置

本地 secrets.json 缺失、不可读或损坏不阻止服务启动。引用了不可用密钥的用途(含继承它的判断用途)进入 configuration_error,消息仍可接收;无密钥服务和其他有效用途照常工作。独立判断用途的密钥缺失时不会改为继承 chat。设置页保留原端点和模型名,管理员设置响应对受影响用途附 key_missing:true、key_set:false。重新输入该用途的 API key 并保存即可恢复;确实无需密钥时须显式移除密钥。不能通过省略 API key 沿用进程内的旧值。

修复损坏或不可读的文件时,设置保存以原有 0600 原子写入重建文件,尚未重新提供密钥的用途继续暂停,数据库中的端点与未修复引用保留。文件为符号链接时拒绝覆盖,须先修正路径。models import 遇到不可读或损坏的密钥文件会退出并提示检查权限或先启动服务、在设置页重新输入密钥;不覆盖已有配置,也不打印文件内容或底层异常。

首次设置先建立管理员密码,再填写角色(默认 Iris、浏览器时区)及可选模型。对话模型提供火山方舟 GLM(Agent Plan,reasoning_effort=high)、DeepSeek 和本机服务预设;模型名和可选推理档位以服务商为准。方舟 GLM 档位依据上述 2026-10-08 决定,仅影响以后选用该预设的配置,已保存的模型配置不自动改写。方舟预设使用 Agent Plan 官方接入地址 /api/plan/v3,应使用对应套餐密钥;其他方舟产品请按所用产品填写自定义地址。“测试连接”只发送固定短请求,单次总预算 10 秒,展示结果和毫秒耗时,不返回服务商正文;它不保存草稿,测试已保存配置时更新现有用途健康状态。

设置页只提供本阶段的角色名称/背景/时区、对话和 embedding 模型、每日 token 上限、学习并发(1—32,默认 2)与模型立即重试。保存后调用既有内部接口对新工作生效,401 显示“密钥无效”;接收和召回仍可用,改对后恢复学习。API key 省略只在接口 origin(协议、主机、有效端口)未变时保留原值;测试或保存到新 origin 必须重新输入密钥,否则返回 body.api_key 字段错误且不发送请求。无密钥服务须显式传 api_key=""(页面勾选“移除”);路径改变但 origin 相同仍可沿用。更换背景撤销旧的初始设定对象、保存修订并建立新设定,学习所得记忆保留;角色名变化不重复建立初始记忆,persona 保留版本历史。修改均写入操作记录,设置页显示最近 30 条。

管理员密码以随机盐+scrypt(N=32768、r=8、p=3)保存,使用 OWASP 的 scrypt 参数组合。会话为 256 位随机值,数据库仅存摘要,绝对有效期 12 小时,登录轮换、退出撤销;Cookie 为 HttpOnly、SameSite=Strict、Path=/,HTTPS 时增加 Secure(M1 本机 HTTP 不启用 TLS)。写请求必须携带会话绑定的 X-Iris-CSRF 与 JSON Content-Type,同时检查 Origin 和 Sec-Fetch-Site;不能只依赖 SameSite。五分钟内五次错误密码后暂停登录一分钟,计数持久保存。首次设置核对实际客户端地址:普通模式仍限回环;容器模式下非回环客户端需一次性设置码。serve 不信任代理转发地址;Host 白名单继续覆盖所有路由,不启用 CORS。

管理接口未登录返回 401,未完成设置返回 409,JSON 的 error.redirect 指向 /login 或 /setup;页面本身返回 303。GET /admin/api/session 返回状态与 CSRF token,并建立短期匿名会话,首次设置和登录也需要该 token。跨站 Origin/Sec-Fetch-Site 的 session 请求返回 403,不创建会话。匿名会话有效期 30 分钟、最多 128 个,新建时淘汰最早过期的匿名项;读取 session 或签发会话时清理过期项,已登录会话不受匿名上限淘汰。匿名响应不含角色、记忆或模型配置。登录、设置密码和完成设置的请求体最多 16 KiB,含分块传输,超限返回 413。页面/管理响应不缓存,页面拒绝被 iframe 嵌入。

方法与 /admin/api 下的路径 用途
GET /session 登录/设置状态和会话绑定的 CSRF token
POST /setup/password、POST /setup/complete 首次管理员密码、完成角色设置
POST /login、POST /logout 登录、撤销当前会话
GET /settings 角色、脱敏模型、限制、健康和最近操作
PATCH /settings/role 保存 name、background、timezone
PUT /settings/models/{chat|embedding} 保存模型;api_key 省略保留,空字符串清除,enabled=false 停用
POST /settings/models/{chat|embedding}/test 空 JSON 测已保存配置,模型 JSON 测草稿
POST /settings/models/{chat|embedding}/retry 对暂时不可用/账户问题请求重试
PATCH /settings/limits daily_token_limit(null 不限)、learning_concurrency、learning_image_limit、native_image_learning、media_retention_days(默认 7,整数 1—36500);所有字段省略即保留现值,daily_token_limit 显式 null 才表示不限;操作记录只包含实际提交的字段

中文试用与管理界面

服务根路径 / 提供 React+Vite+TypeScript 界面,适配桌面和手机;/#/trial 是试用对话,/#/memories 是记忆管理,/#/learning 是入口与学习,/#/status 是运行状态,/#/settings 是设置。构建产物已经提交到 src/iris/web/ 并随 wheel 分发,运行服务不需要 Node,也不从 CDN 加载脚本或字体。

  • 试用对话:先创建一个群聊或私聊入口,默认发言人“我(用户)”是他人主体,区别于角色的 self。可添加虚拟发言人;同一发言人跨试用入口保持身份,同名新增发言人仍是独立主体。入口默认实时节奏,可请求立即学习。消息列表每次取最近 100 条,支持读取更早消息。
  • 角色回复:新入口默认关闭,按入口在当前浏览器本地保存选择;切换入口或刷新后恢复。存储不可用或读写失败时不保存选择,当前页面仍可开启,刷新或重新挂载后回到关闭。开启后通过现有 prepare 取 persona、记忆、本入口近期消息、状态和目标,再用 trial_reply_v1 和 json_chat 生成 JSON。只有校验后的 reply 正文发布为角色实际输出 self_output,进入正常学习队列;失败保留原消息且不发布输出。每入口最多一个回复调用,同一消息成功回复后重试复用已发布输出(含服务重启后);没有成功输出且原消息已超出近期 20 条上下文时拒绝生成。不自动把召回记忆标成已使用。对外 /api/v1 继续只准备材料,不生成回复。
  • 学习动态:每两秒刷新待学习数量、当前/最近批次、本入口最近 12 条新增或更新记忆,可展开来源与前后文。接收、学习成功、形成记忆、等待重试、放弃和拒绝分别显示;回复准备结果只在发送或请求回复时获取,轮询不会反复记召回记录。Persona、当前状态和目标为只读;右侧显示当前状态摘要(活动、情绪、持续时长、可能过时),只读取管理只读接口,不调用回复准备。
  • 记忆管理:按正文/标签文本、人物、类型、出处入口、事件日期筛选(无事件日期时用创建时间),按文本相关度、更新时间或保留强度排序,分页每页 30 条。全文使用已有 jieba 索引,管理浏览不调用模型、不写召回记录。已删除历史可按原正文子串查询。详情显示分数、人物、来源前后文、派生关系、修订和人工操作、真实召回/使用次数。正文编辑和删除都携带 expected_revision;409 时保留草稿并要求重新读取最新修订。
  • 人物关系图:人物页进入 /#/people-graph,从现有表的一个只读快照即时生成,不保存第二份图谱、不调用模型、不写召回/使用记录。只统计 active 且 purged_at IS NULL 的记忆;节点显示当前名字、有效别名、账号数与记忆数(作为 about 涉及人或说话人,每条只算一次)。self 是角色节点,scene 不参与;合并主体只显示保留人物,无有效记忆且无联系的主体默认不显示,显式聚焦时保留该人物。节点可打开该人物全部入口的有效记忆列表。
  • 关系图连线与筛选:同一记忆的多个 about 主体两两共现,按不同记忆数加权;共现不是关系事实,同一对 about 主体中 kind=关系 的正文另作摘要(每对最多 3 条、每条 80 字,完整依据分页可看)。现有未确认 same_as 与未撤销 roleplay 附在连线上,排除已否认/已解决/合并占位;折叠联系保留来源和扮演方向/场景。它们可没有支撑记忆,详情分别标明共同涉及记忆与联系同来源的有效记忆,不把同来源当作身份确认。入口筛选只认至少一条直接消息来源,不采用旧 memories.entry_id 字段或沿派生来源扩大范围;独立联系按其证据消息入口筛选。共现最少记忆数(默认 1)仅过滤共现连线,独立联系仍显示;可按人物聚焦一跳/两跳。默认最多 60 节点、100 连线,可调至 100/200;超限按共同涉及记忆数、关系记忆数降序及主体 ID 稳定截断,先保留聚焦人物,连线两端同时保留,再补记忆数较多的孤立节点,并显示筛选后的总量与截断提示。SVG 不引入绘图库,位置和距离没有关系强弱含义;手机可横向滚动,下方列表提供相同操作。
  • 删除:只把目标对象标为 deleted,保留它的 ID、来源和修订,不删除共享消息或其他记忆;原 ID 不会复活,也不参与普通/深度召回。后端允许编辑/删除遗忘记忆;M2 的置顶、遗忘、恢复、分数调整、彻底清除与按旧内容新建在记忆详情页提供,另有“即将删除”列表。
  • 入口与学习:同时列出试用与宿主入口的名称、平台、类型、节奏、待学习数量和最近/当前批次。批次列表可按入口和组成日期筛选;详情包括冻结的历史/目标/后续消息、消息类型与发送者、每次尝试的时间/总耗时/解析结果、模型请求的结束原因与推理档位、原始正文与 JSON 修正正文、规整及丢弃记录,以及新增/更新/确认的记忆详情链接。原始正文默认折叠,页面不会展示服务商推理字段。
  • 重新学习与记忆缺口:仅放弃或内容拒绝的批次可重新学习;保留原批次 ID、三段范围、历次尝试与缺口,将状态改回 waiting、目标消息改为 batched,本轮尝试数归零(重新获得 4 次)。历次尝试编号持续递增。已在等待/运行/成功的批次返回 409,重复操作不会排队两次;模型暂停时排队等待恢复,同入口在途批次结束后调度。失败仍只保留该批次的一条缺口,原因与时间按最新终止结果更新;第 4 次尝试被重启中断时也替换该缺口,避免唯一键冲突阻止启动。缺口接口与页面使用稳定的 batch_id 标识;只有学习成功的事务才清除缺口(设计 7.6、17.7)。缺口页按入口和消息时间区间筛选,重新排队/学习时标为“重新学习中”。只读浏览不调用模型、不写召回或学习状态。
  • 运行状态:复用既有状态数据,显示对话/embedding 的健康与错误、队列、今日 token 用量、近 24 小时学习耗时/超时,以及最近用途和调度错误摘要。没有定价信息时不估算费用;状态接口不提供滚动日志全文。batches 只保留进行中的批次及每入口最近 20 个已结束批次,近 24 小时调用/耗时统计仍完整;历史批次从管理端分页查看。

管理请求和响应使用 UTF-8 JSON,额外字段和非法值被拒绝;消息正文沿用 32KB UTF-8 上限。所有管理接口在 /admin/api,与宿主接口分开:

方法与路径 用途
GET /admin/api/trial 试用入口、发言人、角色名
POST /admin/api/trial/entries 建立入口:name、kind=group/private
POST /admin/api/trial/speakers 添加虚拟发言人:name
GET /admin/api/trial/entries/{id} 对话、最近记忆与角色上下文;可传 before 读取早期消息
POST /admin/api/trial/entries/{id}/messages 接收:speaker_id、content、dedupe_key
POST /admin/api/trial/entries/{id}/learn 请求立即学习,暂停时仍接受请求
POST /admin/api/trial/entries/{id}/prepare 获取回复准备材料
POST /admin/api/trial/entries/{id}/reply 为 message_id 生成并发布试用回复;失败与复用均返回既有准备的诊断
GET /admin/api/catalog 记忆筛选用人物、入口目录
GET /admin/api/people-graph entry_id/person_id/hops/min_memories/node_limit/edge_limit;有效记忆与现有联系的只读关系图
GET /admin/api/people-graph/edge source_id/target_id/entry_id/limit/offset;连线当前有效依据,每页默认 30、最多 100 条
GET /admin/api/memories text/person_id/kind/entry_id/time_from/time_to/lifecycle/sort/limit/offset
GET /admin/api/memories/{id} 详情(包括已删除历史)
PATCH /admin/api/memories/{id} 编辑正文:expected_revision、content
DELETE /admin/api/memories/{id} 撤销对象:JSON 中传 expected_revision
GET /admin/api/entries 所有入口及待学习数量、当前/最近批次
GET /admin/api/batches 批次分页:entry_id/time_from/time_to/limit/offset
GET /admin/api/batches/{id} 三段消息、历次尝试、正文、规整/丢弃/记忆结果
POST /admin/api/batches/{id}/relearn JSON {};受管理员会话和 CSRF 保护,已排队等状态返回 409
GET /admin/api/memory-gaps 缺口分页:同批次参数,日期匹配消息时间区间的交集
GET /admin/api/status 现有服务状态的管理入口

批次/缺口分页默认 30 条、最多 100 条;日期按角色时区解释,结束日期含整天,带时区时间按实际时刻比较。重新学习与 admin_operations 中的 batch_relearn 记录在同一事务提交。运行状态的近 24 小时学习调用按 created_at DESC, id DESC 返回,页面取前 12 条,耗时分位数仍使用全部记录。调用诊断按同一批次和尝试起止时间关联;历史记录不足或进程中断无法准确关联时单列,不用当前模型配置推断。详情只展示持久保存的正文,未返回正文的拒绝/网络失败明确显示“没有记录”。规整和丢弃信息来自已提交的批次结果,失败尝试只展示已保存的正文与诊断。

编辑/删除在同一事务中写 memory_revisions;该记录包含对象、操作者、时间、前后内容、动作理由,详情分别投影为修订历史与人工操作记录。迁移 008 将人工修订的标识汇入 admin_operations,其他生命周期操作也在其中记录,不重复保存正文。人工编辑与在途学习的冲突通过修订号拦截;最近一次正文修改来自人工编辑时,后续学习也不会自动改写正文或相信程度,会跳过该项并记录原因。再次确认仍增加保留强度(设计 12.5)。来源修订不匹配的派生记忆在详情标为待复核,确定性的依据失效扣减见下文,模型重整留到 M3。

只有修改界面时需要 Node 与 npm(Node 22.12 及以上):

cd frontend
npm ci
npm test
npm run build
cd ..
uv run pytest
uv run --locked --isolated --python 3.12 pytest
uv build

npm test 使用 Vitest+Testing Library+jsdom,覆盖开发代理的同源 CSRF、首次设置、登录/退出、模型设置及外部只读,以及发送、角色回复、编辑冲突、删除、筛选、状态页、入口列表、批次详情、重新学习/冲突和缺口筛选。npm run build 先执行 TypeScript 检查,再更新 Python 包内的资源;源码和产物要一起提交。npm run format 格式化前端。开发时先启动 iris serve,再在 frontend/ 执行 npm run dev;Vite 仅监听回环地址,代理 /admin/api 到本机 8080 端口,保留浏览器 Host,使 Origin/CSRF 校验仍按浏览器同源执行。静态包没有前端开发依赖。

宿主接入

宿主 v1 契约已冻结,完整接入说明、15 个操作、错误与重试及状态区分见 宿主 API v1 接入契约。新增可选字段和响应字段保持兼容;删除、重命名或改变已有字段类型/含义须使用新版本。完成首次设置并登录管理界面后,可查看 /docs 和 /openapi.json 中的中文说明与示例。

本轮接通 POST /api/v1/media:用 JSON 的 entry_id、content_type、data_base64 上传,可附 understanding_text,成功 201 返回 id;消息的 media_ids 数组按顺序引用媒体。单文件 10 MiB、整个媒体 JSON 14 MiB,超限 413;支持 PNG/JPEG/GIF/WebP、MP3/WAV/Ogg/FLAC、MP4/WebM,须匹配 MIME 与文件头。上传与接收均不等待模型。上传对象绑定入口,引用时同时检查上传入口与接收入口权限;文件按内容哈希共享。本文末尾 MD 后端阶段“尚由 AP 线接入”的说明为历史状态,以本接入节为准。

回复准备只在 hints 追加 other_entries_pending:其他授权入口中的待学习入口数、UTC 接收时间范围,不含入口标识、正文或人物。POST /api/v1/memories/search 可传 entry_id 并校验令牌范围;省略时不传递入口上下文。本分支兼容 VS 前后的检索签名,入口隐私过滤须与 VS 一起部署,详见接入契约的过渡说明。

宿主错误统一为 {"error":{"code":"invalid_request","message":"请求格式或内容不合法","fields":[{"field":"body","message":"字段说明"}],"retry_after_seconds":null,"retry_at":null}}。状态为 400/401/403/404/409/413/429/503;429 和 503 带 Retry-After、建议等待秒数及预计重试时间。状态和目标的超大请求统一返回 413,管理接口行为不变。

宿主与 Iris 可在同一台电脑运行,或位于同一可信 Docker 网络;普通模式只监听回环地址,两种方式都校验 Host。所有 /api/v1 请求都需要 Authorization: Bearer <令牌>;管理员 Cookie 不能代替宿主令牌,宿主令牌也不能登录管理界面。连接器经独立的 AstrBot 插件接入,平台不需要直接连接 Iris。下文及其他小节的宿主请求示例均须携带这个请求头。

停服后可用离线命令创建、列出或撤销令牌(沿用服务的数据库互斥锁,服务运行时命令失败):

uv run iris tokens create --host astrbot --prefix 'astrbot:'
uv run iris tokens create --host local-tool --entry group-a --entry private-a
uv run iris tokens create --host trusted-host --all
uv run iris tokens list
uv run iris tokens revoke <令牌ID>

--data-dir/--db 等全局参数放在 tokens 前。创建输出一份 JSON,含 id、host、scope 和仅此一次返回的 token;妥善交给宿主保存,不把终端输出重定向到日志。后续列表只显示 ID、宿主名称、范围、创建/最近使用/撤销时间。凭据由随机 256 位秘密值构成;数据库只保存独立随机盐和 SHA-256 摘要。它不是用户口令,不需要使用耗时的口令派生算法。撤销提交后,后续请求立即失效。

在线管理接口使用现有管理员会话;写入还须 JSON Content-Type 和 X-Iris-CSRF:

接口 请求或返回
POST /admin/api/tokens {"host":"astrbot","scope":{"kind":"prefix","prefix":"astrbot:"}};201,一次性返回令牌
GET /admin/api/tokens items 为元数据列表,另附 rate_limits;不返回盐、摘要或凭据
POST /admin/api/tokens/{id}/revoke 正文 {};重复撤销仍成功,仅首次写撤销记录
PATCH /admin/api/settings/host-tokens {"rate_per_second":20,"burst":60};保存限流设置并记录操作

范围为 {"kind":"all"}、{"kind":"entries","entries":["group-a","private-a"]} 或 {"kind":"prefix","prefix":"astrbot:"}。列表与前缀均按入口 ID 原字符、区分大小写匹配;%、_ 不解释为通配符,可以授权尚未创建的入口。接收消息、回复准备(含近期消息)和立即学习在正文解析及业务执行前检查入口权限;服务状态中的入口、批次和关联学习调用按范围筛选。无效/缺失令牌返回 401 和 WWW-Authenticate: Bearer,越界入口返回 403。

目标以自己的 entry_id 判定归属;没有 entry_id 的全局目标对所有令牌可见;创建、修改及去重合并写入全局目标须使用 all 范围令牌,受限令牌写入返回 403。列表计数、分页以及 prepare/search 的目标分区都先按范围过滤;去重候选与可能重复 ID 同样过滤,后台复核持久保存注入时的范围,重启后继续遵守,写回前复核修订号和范围。范围外的目标不能被宿主修改或合并,提醒在分页和标记“已取走”之前按所属目标过滤。同名宿主换用更窄范围的令牌,也不能通过旧 host_key 回执或合并跳转取得范围外目标。

按设计 11.4,已形成的记忆仍是跨入口共享的角色知识,prepare/search 的记忆项与排序不因令牌范围而改写;来源条目保留,但范围外来源的 entry_id、entry_name、occurred_at 置为 null,管理员来源保持完整;当前状态与 persona 也是角色全局信息。令牌范围保护入口原始消息和目标,不代替后续可见范围工作线的记忆隐私规则。状态中的入口明细按范围筛选,模型健康、用量等全局统计保留。

每令牌默认持续 20 次/秒、突发 60 次,所有宿主路由共用该令牌的桶,不影响其他令牌或管理员会话。超过返回 429 和整数秒 Retry-After。配置保存在 runtime_settings.host_tokens,运行中立即读取;桶在单进程内维护,重启后补满。最近使用时间记录通过认证及限流的请求,包括随后被入口权限拒绝的请求。创建、撤销和修改限流均写入操作记录。

当前状态报告和宿主目标注入的 host 兼容字段仍接受校验,但来源一律取令牌绑定名称;冲突时忽略请求自报名称。目标的可选 host_key 按绑定宿主名称隔离;同名宿主的不同令牌共用这个命名空间。使用反馈只接受本令牌发起的召回;换令牌后需重新准备/查询。状态报告历史、目标来源及宿主操作记录保留绑定名称。

端到端评测在首次启动服务前调用同一个离线创建命令,仅在内存捕获一次性标准输出;所有 HTTP 请求携带该令牌,强制重启继续使用它。令牌不进入临时模型配置、服务日志或评测报告。本接入节覆盖前面历史实现说明中“宿主令牌仍在 M4”的旧状态;下文状态报告中的宿主名称也以令牌为准。

下面的 Python 示例依次调用接收消息、回复准备和使用反馈;请求与响应均使用 UTF-8 JSON:

import os
import httpx

with httpx.Client(base_url="http://127.0.0.1:8080", timeout=10,
                  headers={"Authorization": "Bearer " + os.environ["IRIS_HOST_TOKEN"]}) as client:
    received = client.post("/api/v1/entries/group-a/messages", json={
        "platform": "chat", "sender": "小林", "account_id": "lin-001",
        "content": "我喜欢桂花乌龙茶", "occurred_at": "2026-09-29T20:00:00+08:00",
        "dedupe_key": "host-message-001", "kind": "message",
    })
    received.raise_for_status()
    print(received.json())  # message_ids、pending_count;重复键返回原消息

    prepared = client.post("/api/v1/entries/group-a/prepare", json={
        "text": "给小林准备什么饮料?", "participants": ["小林"],
        "known_memory_ids": [], "recent_limit": 20,
    })
    prepared.raise_for_status()
    material = prepared.json()
    print(material["persona"], material["memories"], material["recent_messages"])

    # 宿主生成回复后,仅填写确实使用的记忆;不要把所有返回记忆自动当作已使用。
    actually_used = []
    feedback = client.post("/api/v1/feedback", json={
        "recall_id": material["recall_id"], "memory_ids": actually_used,
    })
    feedback.raise_for_status()

接收接口也接受上述消息对象的数组,整批提交在一个事务中完成。正文与引用正文各最多 32768 个 UTF-8 字节;字节超限返回 413,不截断。entry_name、account_id、scene_identity、quote_author、quote_author_account_id 各最多 200 字符,entry_kind、occurred_at 各最多 100 字符;字符超限返回 400。入口 ID 长 1—200 字符,不能全空白或含 ?、#、控制字符,按路由的解码路径校验,冒号仍可使用。具体字段上限见 宿主契约。首次入口自动创建,可以在消息中用 pace 选择 realtime、standard(默认)、economy,或对象 {"count":6,"idle_seconds":30,"max_wait_seconds":180} 自定义节奏;入口创建后沿用已保存的节奏。接收返回后,由后台按数量、空闲、最长等待、关注信号触发学习。等待时间按接收时间计算,历史消息的发生时间仍用于学习事实和相对日期。

离线 iris learn group-a --force 只允许在服务停止时使用。服务和离线写入命令使用操作系统文件锁,避免两个进程共同写同一数据库;崩溃后锁自动释放。不要手动删除锁文件来绕过它。

实时/标准/省流的数量分别为 4/12/30 条,空闲为 5 秒/10 分钟/30 分钟,最长等待为 1 分钟/1 小时/4 小时。待学习消息提到角色名、@、记住 或 别忘了 时,空闲条件缩短到至多 1 分钟。手动触发接受后立即返回,暂停期间也接受并给出原因:

print(httpx.post("http://127.0.0.1:8080/api/v1/entries/group-a/learn").json())
# {"accepted": true, "pending_count": 2, "paused": false, "reason": null}

高流量入口过滤(M2)

GET /admin/api/entries/{entry_id}/settings 读取入口设置,PATCH 修改已存在入口的节奏和过滤。沿用管理员会话、JSON Content-Type、X-Iris-CSRF、Origin/Sec-Fetch-Site 及 Host 校验;无效值返回 400,不存在的入口返回 404。写入与 entry_settings_updated 操作记录在同一事务完成,记录前后设置。入口与学习页可以查看和修改这些设置,也可以直接调用管理接口:

{
  "pace": "economy",
  "filters": {
    "min_chars": 3,
    "mention_only": true,
    "context_messages": 2,
    "max_batches_per_hour": 10
  }
}

PATCH 可只提供 pace 或部分 filters 字段,其余沿用旧值。pace 支持原三种预设,或完整自定义对象(count 1—1000、idle_seconds 1—86400、max_wait_seconds 1—604800)。过滤默认全部关闭:min_chars=0、mention_only=false、context_messages=0、max_batches_per_hour=0。整数严格校验,不接受布尔值或数字字符串;min_chars 范围 0—32768、context_messages 0—100、max_batches_per_hour 0—1000。长度与上限的 0 表示关闭;窗口为 0 时仅保留提及消息。

  • 长度为正文去掉首尾空白后的 Unicode 字符数,包含标点、emoji 和内部空白,不按 UTF-8 字节或汉字分词计数。
  • 提及匹配正文中的当前角色名或 subject_aliases 中属于 self 的已确认别名,采用与既有角色名关注信号相同的区分大小写的子串匹配,包含 @角色名/@别名。仅有发送者名、引用作者、引用正文、其他人的别名、单独 @别人 或“记住”“别忘了”均不能通过提及过滤。
  • 前后 K 条按本入口原始接收顺序计数,所有消息类型和短消息都占位置,其他入口不占位置。每条消息等到 K 条后续消息到达,或调度器观察到入口达到现有空闲条件后定案;关注信号仍将空闲缩短到至多 1 分钟。长度不足可立即过滤,但短提及本身仍能让相邻的足长消息通过。长度与窗口同时开启时取交集。
  • 等待窗口的尾部仍是待处理消息,不进入三段;已有足够后文的前部可以先组批。空闲关闭窗口后不再追溯恢复已过滤消息。手动学习遵守窗口与小时上限。三种节奏的数值、关注信号和 token 上限保持不变;调度触发后只从已经定案的可学习消息组成批次,可能少于条数上限。
  • filtered 是已处理的最终状态,不形成记忆缺口,不计入待学习积压,也不进入任何新批次的历史、目标或后续段。原文继续出现在本入口近期消息中,超过消息保留期且无既有引用保护时清理。放宽/关闭过滤不恢复已经过滤的消息。
  • 设置对尚未冻结的待处理消息立即生效。已进入某批任一段的消息保留原入选决定,包括已冻结后续段转为下一批目标的情况;已有三段、重试与学习请求保持不变。每个新批次保存当时的设置快照,详情以 entry_settings 返回,旧批次为 null。

小时上限按本入口滚动 60 分钟内新冻结的批次计数,所有结果均占额;同批重试和重新学习不重复计数。检查与冻结在同一写事务内完成,重启不重置额度。满额时消息按原顺序继续等待,不丢弃,也不因消息保留期而清理待学习积压。长期流入速度高于允许的处理速度时,积压与磁盘占用会持续增长;提高上限或减少流入后按原顺序消化,不扩大批次绕过 token/条数限制。

管理 /entries 的每项返回 filters、queue_wait;/status 的 entries/backlog 及 /batches 的独立 entry_waits 列表也提供 queue_wait。reason=hourly_batch_limit 表示上限等待,retry_at 为最早释放额度的时刻(不是完成承诺);reason=filter_context 表示只有尚待窗口定案的消息。另有 limit、batches_last_hour(未启用上限为 null)、filter_waiting_count、filtered_count。entry_waits 是当前入口状态,按 entry_id 筛选,不受批次日期/分页影响;它不伪造空批次。宿主状态与回复准备的既有结构不改,管理读接口不会触发过滤或学习。

默认同时学习 2 个批次,同一入口严格串行。可用 iris serve --learning-concurrency 1 修改并持久保存并发数(1—32)。手动请求持久保存到请求当时的消息位置,重启后继续处理该范围的尾部。正常关闭服务时等待在途任务完成;强制终止后由下次启动恢复。

准备结果还包含空的 state、未结束的 goals、运行 hints 和 recall_id。查询省略或为 null 时,使用默认 adaptive_6:取最新他人消息及其之前五分钟内连续的他人消息,按原顺序组成查询,最多 6 条;角色输出、行动结果和场景事件不进入查询。可用消息超过两条时,最新消息本身足够完整、又不含指代,就只用最新消息;含指代或内容不足时带上前文。只有 1—2 条可用消息时保留全部短上下文。锚点只取最新一条他人消息中的姓名/别名,更早的人名仅提供上下文,不限制返回范围。显式查询文本(包括空字符串)保持原有行为。participants 接受主体 ID 或无歧义名字/别名,同名账号用 ID;省略或为 null 时从最近 20 条消息推断,按最近发言顺序排列,传空数组不取人物要点。参与者不会排除关于我自己或其他人的记忆。

相关记忆先入选(reason: "relevant"),剩余名额按参与者轮流用其重要记忆补位(reason: "person_highlight"),要点合计最多三条,不包含我和场景。人物要点是对方的背景,不表示回答了当前问题。显式查询文本点名的主体或别名限定范围;自动查询仅以最新他人消息点名:记忆须涉及、出自或正文提及此人,同名主体都保留。recent_limit=0 关闭近期消息返回,记忆合计最多 8 条、1500 个估算 token,目标最多 10 个。模型失败时说明全文检索降级。定向 search 不补人物要点。

回复准备默认对前 8 个 relevant 候选做召回判断,支持分 ≥50 才保留;人物要点不判断,删去的相关项不改为人物要点,也不补位,其他顺序不变。/memories/search 不判断。试用回复使用同一个 prepare。判断请求中的别名只涉及前 8 个相关候选的说话人和涉及者、参与者、近期消息发送者及固定查询中点名的主体。

prepare 可以传 "judge": false 关闭本次判断,或 "judge_budget_seconds": 3 缩短预算(必须 >0 且 ≤10 秒)。响应的 judgment 和 hints 标明 applied/disabled/degraded、reason、duration_ms、queue_ms、network_ms 和 removed_memory_ids。未配置、暂停、额度耗尽、排队满、超时或非法输出都保留仍有效的原候选;调用后逐条核对修订号、生命周期、宿主已知记忆,并以最终返回的近期消息检查冗余;失效项记入 stale_memory_ids。新消息或别名变化不废弃整次判断,其他候选仍按原判断保留或移除,状态保持 applied。调用中关闭判断的配置仍生效。只有最终返回的记忆取得本次反馈资格。

recall_judge 是独立模型用途,未单独配置时继承 chat 的接口、密钥、模型及显式推理档位;chat 未配置档位时不发送,独立判断配置仍默认 high;chat 的暂停不暂停判断,判断的暂停也不暂停学习。可在测试配置中增加 [recall_judge],或通过已有管理员接口 PUT /admin/api/settings/models/recall_judge 保存独立模型及档位;enabled:false 清除该独立配置、恢复继承。全局关闭使用 PATCH /admin/api/settings/recall-judge 的 enabled:false。此 PATCH 还支持 concurrency(1—8,默认 1)和 queue_limit(0—64,默认 8),省略字段保留现值;模型来自外部文件时模型配置仍只读。管理设置响应列出实际配置、inherited 和独立健康状态;设置页的模型部分可以配置、关闭和测试召回判断,运行状态页显示它的健康状态,试用页显示每次回复准备的判断结果。

判断最长总预算 10 秒,包含排队,只有一次网络尝试,不修正 JSON。达到并发/队列上限时有界等待或立即降级;429 及账户限额按服务商错误码优先分类。前两次临时限流进入独立 rate_limited 退避,有有效 Retry-After 时按它等待(支持秒数或 HTTP 日期),没有时分别等待 2、4 秒;retry_at 标明截止时间。期间新 prepare 直接降级,不排队、不发请求,到期由下次业务请求恢复,无需探测。连续第三次可重试错误(含限流)进入暂停,后台按 60/120/240/480/600 秒探测;更长的 Retry-After 或仍未到期的退避均保留。迟到的成功不提前解除退避,迟到的限流不缩短已有等待;成功业务请求或成功探测清零计数。账户限额继续按账户问题暂停。判断与学习使用独立并发槽位;同账号仍共享服务商额度,不能保证没有账号限流。已达每日 token 上限时不再开始判断或判断恢复探测,基础召回继续。

定向查询和状态示例:

import httpx

result = httpx.post("http://127.0.0.1:8080/api/v1/memories/search", json={
    "text": "观星", "people": ["小林"], "kinds": ["计划"],
    "stances": ["亲历"], "time_from": "2026-09-01", "time_to": "2026-10-31",
    "include_forgotten": False, "limit": 8,
}).json()
print(result)
print(httpx.get("http://127.0.0.1:8080/api/v1/status").json())

深度查询显式设置 include_forgotten,结果的 lifecycle 标明 active/forgotten;读取不改变状态。反馈有效期为 24 小时,同次召回同条记忆只强化一次(默认 +8,上限 100),按双阈值决定恢复,不改变相信程度或修订号;已删除对象不能反馈。错误码:400 参数/反馈无效、404 不存在/已删除、413 消息过大、503 暂不可用;409 保留用于修订冲突,当前宿主接口不修改记忆正文。

M3 persona(后端)

自我认知记忆保存具体经历与观点,persona 从中提炼相对稳定的自我描述。当前活动、情绪和待办分别留在状态与目标分区。首次设置仍立即生成原有模板正文,不调用模型;角色名和模板有设定依据。后续生成使用有效、涉及“我”、且说话人是“我”或立场为“设定”的记忆,先为仍有效的初始模板依据预留材料,其余按置顶、重要度和保留强度排序;两者合计最多 6000 估算 token。别人对我的评价本身不会入选。

候选逐句记录正文、来源、依据记忆 ID 与修订号,以及依据追溯到的不同日期数。先检查非空、依据有效、没有遗留编号和全文不超过 800 字,再由模型逐句检查依据支持、虚构、监管要求和单一日期的场景限定。少于 300 字只提示,不靠扩写凑字。自动句使用中性人物指称,代码按完整分词检查“他/她/你/您”等代词,避免自动句推断性别或把读者当成未绑定的对象;命中后进入同一句子恢复流程,不直接替换文字。管理员原文和锁定设定不受此措辞限制。上一版仅帮助保持措辞稳定,不是新的事实依据;失效依据支持的句子不能因此保留。删改管理员手写句会确定性地把变化程度提升为“大”。管理员直接编辑只做确定性检查,改动或新增的句子标为手写,原样保留的句子沿用原依据。

优化线启用 persona_generate_v20/persona_check_v33/persona_degree_v1,修复改为确定性摘录后独立复查;旧提示词文件保留,persona_repair_v18 不再调用。生成与检查均逐个实质分句绑定人物、动作、事件日期和完成状态;一个分句的人物、日期或完成证据不能借给另一个分句。生成 v20 用独立短提示词选择少量核心认识,通常 3—6 个自动句,资料少可更少;该句数是提炼指导,不截断或删除已生成的合格句。每句保持自身活动范围,不再输出与独立检查重复的证据笔记。认同/认可个人特质即使有日期也须保留具体活动范围;否认无限泛化不能代替领域限定。转述他人的评价、建议或告知须分别核对被转述行为与本人认同的时间/场景;代码对有界句中“他人说我……”等明确结构要求原话自己的事件限定或实际作者证据,不能借用当前转述的记录日期。该保守语法检查只提供必要条件,事实含义仍由独立检查核对。生成、检查、复检和修复以原始摘录支持动作及完成状态,当前记忆正文只另作范围与更正约束,不能补证原话没有的行动;没有摘录的记忆仍使用自身正文。完整摘要和来源图继续保存在审计快照中,用于修订及来源复核,选材预算与顺序不变。检查 v33 只负责事实与监管,独立短提示词 degree v1 负责整版程度;事实窗口先读本句来源实际证明的关系,再判断候选;逐片段选择本句来源的原文编号并给判定,存疑直接判不支持,不为候选补找成立解释;“说要做”与“已经做完”、“先不做”与“确实没做”分别核对。每句先标明内容类别,事实有据的进度快照/待办也不能进入 persona,添加历史日期不豁免。代码从最近一次实际角色设置读取有记忆绑定的“初始设定:……”原句,不从上一版模型正文恢复设定;这些句子原样置于候选开头,模型只生成其他内容。名字仍在角色设置和首次模板中可核对;没有记忆绑定的名字介绍不在后续自动候选中重复。模板按完整句子处理,任一依据被修改、遗忘、删除或无法装入预算时,整句不带入并记录原因,不拼接旧事实。空背景的占位句不在后续反复保留。锁定句也接受监管检查;若有问题,删去整句而不让模型改写设定。首次设置的原始正文保持逐字不变。

有原话的记忆(含初始修订)另给 current_revision_text 作为当前范围与更正约束;原话省略的活动、领域和对象限制仍须保留,明确更正的旧断言不得沿用。此字段不能补证原话没有的动作、充当事实证明片段或额外日期。完整句子的当次限定适用于各个焦点,纯场景片段用本次事件元数据核对,不将文字切片当作脱离语境的断言。

没有明确当次或事件日期限定的自动非设定句,检查另返 scope_check:候选范围原文、本句绑定依据中的活动范围短引,以及范围是否保留。范围短引优先且只能取本句依据的当前正文约束,无当前正文才使用原话,避免省略上下文的原话绕过明确领域。正向结论要求短引逐字保留在候选范围中,且该范围实际出现在本句;缺范围、扩宽范围或错借来源则走修复/删除。不同日期和入口不能抵消范围丢失。来源短引若额外附带布尔 preserved,保留该原始判定并取不利结论:任一来源为 false 就拒绝整句,true 也不能覆盖总判定或逐字范围检查。缺字段、非布尔和未知字段仍视为无效结构。精确短引只是必要条件,具体短语是否确为活动范围及概括是否适当仍由模型判断并接受外部评测;不按活动词表猜测领域。

活动范围检查要求的同一完整对象若出现在分句内,也逐份按相同字段、类型、出处和字面范围规则校验,保留原层级。缺少句级范围对象时,每个已返回分句都必须有完整范围对象;不能用部分分句的范围填补其他分句。已有句级对象时也校验所有额外分句对象,任一否定取不利结论、任一坏字段仍为无效结构;不能以正向对象遮盖负向对象。范围对象不补事实证明或独立日期。

如果必需的范围对象完全缺失,但已返回且字段合法的焦点已被原话、日期或明确否定独立判为不支持,整句仍可进入修复/删除;记录 unchecked_scope_check,不把未查范围当作通过。只有整行否定、没有独立焦点反证,或任何已提供范围对象缺字段、非布尔、只覆盖部分分句,仍为无效检查。scope_check_required=true 不因模型选择 episode 或声称有界而取消;要保留的句子必须完整通过范围和全部焦点检查。

生成、检查与修复先判断内容适合性:真实发生的宣布或承诺,若核心仍是未实施的具体安排,仍属待办;日期、曾宣布或如实说明尚未执行都不使其成为有界亲历。明确以完整片段写“这只是一个计划”“还只是新的打算”等的自动非设定句,确定性进入修复/删除,修复原文也受同一保护。它是保守的措辞检查,不是完整意图识别;否定、条件、篇名中的相同词语和“计划”一词本身不触发此规则。已经实施的经历、有依据的长期方向与价值仍按原有事实与监管规则核对。原始候选、拒绝理由和未查片段保留。

有界事件也不能补出具体题材。对“在那次……中/里/时”及句首日期后的同类场景短语,代码检查限定词是否出现在本句当前绑定正文或来源中,并在每个正向分句的实际原话引用范围内再查一次;不能从其他句子、未用于该分句的记忆或同一记忆的另一段未引用来源借用。来源入口类型只提供直播/群聊/私聊/游戏等通用词。“与/和/跟/同”等连接词不需要字面重复,但人名和题材仍须有出处,引号或书名号中的同形词仍按实词核对。当前正文仍仅提供范围约束,不证明动作;词项出现也不证明否定、人物或语义支持。这是有限句首语法和分词的必要约束,不是完整中文场景解析,别的句式仍依赖模型;合法同义改写可能被保守拒绝。生成提示词优先使用依据中的场景用词,无法证实具体活动时使用有据日期,不猜测活动名称。手写与设定保持原有豁免。

对自动非设定句的“了/过+次数或数量”,代码要求绑定原话中存在同一动作的明确完成标记;“先不/暂不/不打算”等不能仅由记忆摘要转换成实际未做。先筛生成句与修复选项,再逐焦点只检查实际选中的来源;其他焦点、未引用来源或旧修订不能补证。条件和篇名不按事实断言处理,无原话记忆仍用自身正文。它是有限措辞的必要检查,不代替人物、事件与语义判定,合法同义表达仍可能被保守拒绝。原始意见和拒绝原因保留。

自动句还须独立交代本句事件;“也是在那次”“同样是在那场”“在同一场”等句首场景照应进入修复/删除,不能借邻句把不同日期的经历关联为同一次。它是保守措辞约束,非完整中文指代解析;手写和设定原文不受这一措辞限制,事实与监管仍独立核查。

检查 v31 允许显式的否定短路:按顺序检查到首个不支持焦点时,整句只能进入修复/删除,逐项记录尚未检查的焦点;任何要保留的自动句仍须覆盖并通过全部焦点。同一句已确定不支持或违规后,代码跳过它剩余的窗口并记录 skipped_windows 及所有未查片段;其余句子仍须完整检查,这不能把失败句变成可保留句。短路前缀规则仅用于实际少项的窗口;完整返回时仍逐项校验所有焦点,多余的布尔短路标记不起省略作用。返回的有序非空前缀末项及整行均明确 supported=false 时,即使省略冗余的 stop_after_failure,也保留该拒绝并记录规整及未查片段;不能保留此句。少项时显式 false 的短路标记、非布尔标记、空判定、坏字段或漏掉其他句子仍为无效检查;正向缺项必须按下述规则补查或确定性拒绝,不能当作支持,旧作废运行不追认。若模型额外返回完整原句中的其他片段,只有编号均为原句内整数、且本次请求的全部编号首次出现次序与请求一致时才兼容;每个额外片段也完整校验字段、来源、日期和范围,额外片段不支持同样拒绝整句。同一显式编号返回多条意见时,每条均须独立通过全部校验,任何否定或坏字段不能被另一条正面意见覆盖;不把多条意见的日期或原话合起来补足单条证明。缺少请求片段、请求编号首次出现乱序、错号或坏字段仍无效,重复编号不能填补缺项,也不猜测归属。请求片段、额外片段 additional_clause_checks 和同一请求片段的重复意见 repeated_clause_checks 分开记录,规整记录列明编号,原始响应不变;补查合并也保留重复的旧前缀意见,不能以后一次意见覆盖先前意见。后续正向窗口仍照常检查;已校验的额外片段不再误列为未查。

若整行和已返回焦点都明确为正向、编号为请求的严格非空前缀,且所有必要字段合法,但本前缀已触发独立的原话、日期或范围保护,则无需补查就确定性拒绝整句。记录 guard_rejected_prefix、实际检查及未查编号;保留模型原始正向意见,不伪造后缀判定,也不混入其他日期。仅在无短路标记、无模型违规/虚构标记且内容类型可入选时适用此分类;坏字段或错号仍为无效检查。未触发保护的正向前缀仍须实际补查,不能利用此规则直接删除或接受。

若正向检查只返回了请求焦点的一个严格前缀,且已有字段、来源、日期和范围均通过校验,代码仅向模型补查尚未返回的后缀一次。首查与补查共用固定的整阶段截止时间和本批的超时重试次数;前缀不会因漏项直接获得支持,补查的否定和额外负面判定同样拒绝整句。原始响应、漏项编号、补查输出及完成状态均保留。错号、坏字段、无有效证明或含否定的漏项不能借此放行;补查仍有单句字段错误时,按下述格式重问与保守拒绝处理;无法确定句子归属的整批结果仍拒绝。已经完成的其他句子不重新判断;补查不产生整版幅度结论。

有明确逐句判定的问题不再必然拒绝整版:保留通过的句子,每个问题句只选一次各自原有依据中的完整原文,送独立模型复查。修复只能逐字选择本句已绑定的一条非设定记忆的完整单句正文(去外层空白,缺少句末标点时可补句号),或删除;不能自由改写、任意删词、拼接不同记忆,也不提取可能截断或含他人引语的消息片段。代码把修复句的依据收窄到选中的那条记忆,并重新计算日期,不能借其余引用扩大日期数。摘录并不证明事实成立,复查仍需核对原始来源与监管要求;仍不通过或没有有效修复的句子被删去。只对修改过的句子复查,其他通过句不重复模型检查;复查事实通过后,再单独比较最终草稿与上一当前版的变化程度。删去后重新做全文确定性检查。若复查又删了句子,程度比较不再请求,代码保守判大;若最终正文逐字等于上一版则判小,管理员手写删改始终覆盖为大。逐句序号完整且能定位时,某一句的结构化字段不合格会仅重问该句一次,使用原提示词、完整原句及原绑定依据,并附格式错误说明。修正结果仍须通过所有原有校验,已出现的否定、虚构、违规、日期/来源/范围问题不能被新的正面意见覆盖。仍无效或无法在剩余阶段/材料预算内重问时,由控制器明确标为 verdict_origin=conservative_rejection,按原有修复/删除规则处理该句,不把它当作模型已确认有依据,也不记为整轮 incomplete_check。这同样适用于设定与手写句,后者的删改仍保守判大。invalid_sentence_checks 与原始输出记录首次错误、重问、修正失败及保留的不利结论。句序不明、整批缺句、空正文或长度超限仍拒绝;网络失败、超时重试耗尽、暂停和调用预算不足仍记录失败/跳过,不作为语义删句依据。

不足两个来源日期的自动句须有明确当次或事件日期限定;这一已有证明校验也提前用于修复选项,避免把必然不通过的文字送入复查。代码另行拦截“一向/通常/经常/总是/每次”等惯常措辞,以及没有当次限定的句首“遇到……/如果……/只要……”条件式;模型即使返回 scene_qualified=true 也不能覆盖。管理员手写和初始设定不受这一措辞限制。非设定自动句中的“今天/昨晚/最近/目前”等相对日历时间也进入修复,不按请求日期猜事件日期。匹配使用完整分词及相邻词组合,避免“体现在”“接近日落”里的子串误报。此规则是保守的表面约束,不能替代逐分句核对事实、人物和时间;多条依据的日期并集也不代表每个分句都获得多日支持。严格摘录和措辞约束可能提高删句率,覆盖率须在后续真实评测中验证。

初检和复查还会按本句绑定的记忆 ID 与修订号核对两类表面约束:只来自游戏入口的句子须明确游戏/联机场景;若原话明确叙述现实或线下事件,可保留同一现实限定,不能仅按入口类型认定虚构。自动句断言“读完/完成”等结果时(包括没有“了”的肯定结果叙述),本句原始来源须有同一动作的明确完成措辞;有原始来源时不能用记忆摘要中的完成式覆盖原话的打算,既有无消息来源的记忆才以自身正文核对。这只是一项必要措辞检查,不能证明动作归属、否定或完成语义;通过后仍需模型核对。生成提示词用通用对照例子区分目标与结果,逐个主张计日期,避免将多日材料中只有一次的偏好混入一般认识。检查输入给模板、手写与自动句一并编号,模型返回错号仍拒绝,不自动纠正判定归属。 检查还把逗号、分号划分的片段作为核对焦点;这些片段不是语法解析,模型须结合整句判断。每个自动非设定片段分别选择本句绑定来源和原话片段的编号,代码还原逐字短引并核对引用归属及原话存在,并按该片段的证明独立计日期。一般认识须有至少两日,单日须在正文明确当次或事件日期;不能借别的片段凑日期。手写和模板免查记忆证明。精确短引仍不能证明语义蕴涵,事实支持仍需独立评测。检查显式提供角色名字以核对场景记录中的实际行动者。

生成候选后,先执行已有的确定性措辞与依据保护;明确不合格的句子直接进入原有修复/删除流程,不再为它请求语义初检。precheck_rejections 单独记录原候选序号与拒绝理由;initial_model 只保存实际检查过的句子,initial_model_sentence_indices 将其序号映射回原候选,全部预检失败时为 null/空映射,不补造模型意见。已知需要修复时,初检暂不判断幅度,修复后再比较最终组装正文。其余句子仍须完整检查,修复仍只能选本句绑定原文并独立复检。任一实际请求发生超时、429、截断,或无法恢复、无法定位到单句的检查损坏,评测仍整轮作废;可定位坏字段的保守处理单列诊断,不再导致整轮不完整。预检不能追认旧失败运行。

修复前先筛去违反确定性防护的原文选项,保存在 repair_options_rejected;与已失败原句相同,或仅改首尾空白、句末句号、句首“那次/在这次/这一次”等同类当次指示词的选项也先排除,不以这些文字变化重新检查同一失败主张。比较不删除内部标点、人名、日期、否定或条件;原先没有的当次范围、不同活动范围或不同事实仍可进入独立复查。原文及被排除原因照常保留。没有合格选项的句子直接记为 stage=repair_options 并删除,不请求模型。剩余问题按原有证据快照顺序固定选择第一个合格原文,不再调用模型选择器;候选中的引用顺序不能改变此顺序。每个问题最多选择一次,独立复查拒绝后直接删除,不尝试后续选项。同正文、同一记忆修订的重复摘录只保留第一处并复查一次,后续问题句记为 stage=repair_duplicate;不同正文或修订不合并。选项通过表面保护不代表事实已证实,选中后仍复核确定性条件并交模型核对原话与监管。事实检查结束后需要比较整版时,显式使用 task=degree_only 与独立短提示词,只比较已经组装好的最终正文幅度;模型只需返回 change_degree 和 reason,代码根据已知空候选固定 sentences=[] 并记录规整,也兼容显式空数组。有实际句子、额外字段、null 或非空逐句数组时不适用这一规整,缺判定仍拒绝。提出暂停、查看或重试请求也不证明动作已发生;言语行为周围的动作须各有已发生记录。

版本详情的 checks 增加 initial_model、initial_model_errors、repair_selections、repair_options_rejected、recheck、repaired_sentences、deleted_sentences 与 original_sentence_count。删句项保存原位置、正文、来源、依据、阶段和原因,以及有过的修复稿;repair_policy=first_eligible_bound_memory_v2 标明固定摘录策略,repair_selections 保存各问题句的合格选项、所选原文和原序号;尝试输出中也保留 persona_repair_selections,中断或复查失败仍可审计。历史 repair_output/repair_outputs 继续可读;新流程的 repair_outputs=[] 表示没有请求模型选择意见,不填造判定。model 是通过的原检查与复查判定按最终句序合并的结果,标明 assembled_from,不是第三次模型检查。恢复过程中任何阶段都不更新当前版,发布及确认仍核对版本号与依据。

GLM glm-5.3-flash 的初检与复查使用流式响应,保留上述总预算和输出上限;生成以及其他模型沿用原传输方式。流式读取连续 90 秒没有新字节时主动中止,沿用网关重试和检查阶段预算;心跳、推理增量及尚未组成完整 SSE 行的字节均刷新进展时间,持续推进的慢响应仍受原总截止时间约束。每次请求只保存首个响应头、数据、推理和正文的到达时间、数据及字节间隔、字符数、停滞中止与完成标记,不保存推理正文、请求头或端点。流结束标记与有效 finish_reason 均到达后,完整正文才进入既有校验;缺尾、流错误、超时或截断不能作为支持证据,也不切回非流式重试来掩盖失败。HTTP 重试、用量和健康状态仍由既有网关管理,调用内 HTTP 重试及 JSON 修正共用单次截止时间;超时关闭在途响应。检查层的一次超时重试仍使用相同传输方式,并受固定阶段截止时间限制。评测材料记录传输方式与停滞阈值,并按同一次操作的实际调用 ID 区分恢复结果。预算内重试成功且检查完整的超时、429 或传输错误仅单列为已恢复;后续另一候选成功不能覆盖前次失败。未恢复失败、检查不完整和输出截断仍使运行无效,已恢复与未恢复的计数均保留。旧作废运行不追认。

生成/初检/复查单次输出上限均为 16000 token,包含服务商计入输出的推理用量;不能以正文长度替代总输出预算。确定性摘录不消耗模型调用或输出预算。finish_reason=length 记为 output_limit,保留原文且不做 JSON 修正重试;初检或复查未完成时整候选被拒绝,生成未完成时保留当前版并记录失败。历史模型修复的截断调用仍在评测中判为无效,旧运行不追认。评测单列输出耗尽调用数及用途,不能把截断当成支持结论。生成有 360 秒总预算。初检每批至多四个句子行、两个自动核对片段;长句分成多个检查请求,每个请求仍附完整原句作语境,不删正文或丢掉主语、否定与条件。片段序号沿用整句位置,要保留的句子须全部片段覆盖;判定按不利结论合并回原句:支持取 AND,虚构与违规取并集;模板、手写和自动设定不重复分片。window_map 保存各批到原句和片段的映射。每个实际检查请求的完整 messages(含系统提示词与 UTF-8 JSON 序列化)至多 32768 字节;在行数/片段数分批之后,仍超限的批次依次拆成较小批次与单片段。每个请求保留完整原句、全部绑定来源及其限定,不截短证据。一个片段的完整材料仍超限时,不发送该句,记录 material_rejections 并保守进入原有修复/删除;其他句子继续检查。格式重问、补查、JSON 语法修正和程度比较同样经过发送前的字节上限校验,无法拆分的程度或 JSON 请求超限会失败,不能发送超限请求。

单次检查调用至多 360 秒;初检整阶段固定至多 900 秒,复查按同样上限独立计时,只有一个窗口时也使用这一阶段上限。单批超时可重试一次,该批的首查、补查与字段重问共用这一次机会;程度比较也至多重试一次。只有剩余阶段预算且未暂停时才重试,429、调用额度不足、暂停及其他错误不使用此重试。任何重试都不延长阶段截止时间;调用内 HTTP 重试与一次 JSON 语法修正共用当前 360 秒调用预算。唯一一次 JSON 格式修正会提供解析候选中的错误类型、行列和位置,要求只修语法、保留字段及判定;原始错误响应、解析诊断和修正响应均保留,修正仍无效则失败,不增加重试。检查批次数随句数增加,实际调用均计入用量与整理独立的 persona_max_calls(默认 30);预算不足时保留当前版。包括首批在内的所有事实窗口都以 task=check_facts_and_rules 核对本批事实与监管,不携带上一版或待比较完整稿。全部事实通过后才用独立 persona_degree_v1 请求比较全文;该请求仍受原阶段截止时间和单次 360 秒限制,不因多出比较调用延长阶段预算。事实字段中的程度不作为整版结论,degree_assessment 记录实际比较或推迟原因;比较缺失、超时或截断仍拒绝候选。所有原始批次输出保留任务类型和提示词版本。句序错乱或缺句仍拒绝,不猜测归属。检查材料按句绑定记忆与来源,所引依据共享去重池,不重复铺开同一条长来源,也不携带未绑定的证据图。耗时、超时与用量按实际用途记录;真实评测同时覆盖 persona_v1 和 persona_v2,使用独立双判并取不利结论;未恢复的 429、超时或传输错误、输出耗尽或未完成的模型判定仍使评测无效,不复用该时间线;预算内恢复且完成检查的调用保留失败记录与恢复归属,单列已恢复/未恢复计数,不改变语义判分。缺少调用归属或成功记录时不推定恢复。重跑使用新输出目录并完整执行该语料。只在根对象恰有一个 answer 对象时展开一层,保留原文与规整记录,不递归解包或猜测冲突字段。评测每个操作后原子保存进度;可处理的中断(如 Ctrl-C)会先把正在执行阶段已经返回的原始输出写入失败尝试,再保留已完成检查点;未返回的请求用量仍未知,不虚构回复,也不把部分时间线当完成缓存。技术失败即停止本轮后续模型调用,报告计划/实际执行/未执行时间线数;完整的语义拒绝仍继续运行。逐句恢复不能保证模型已查出所有问题,产品仍默认全部人工确认。

自 2026-10-10 起,默认“全部人工确认”:检查通过的小、中、大变化都带逐句依据和检查结果进入待确认,管理员确认前继续使用当前版。M3 persona 门槛尚未达到,因此先由管理员把关;这是发布策略调整,不代表模型质量已达标。恢复后仍未通过的候选保留当前版并记录原因。新的候选取代旧待确认候选,管理员编辑和回滚也会取代它。版本状态为 current(当前)、pending(待确认)、rejected(被拒绝)、superseded(被取代)、history(历史);来源为 initial_setting、periodic、regenerate、admin_edit、rollback。回滚只接受曾发布的版本,新建一个正文与逐句依据相同的当前版本,历史保持线性;若旧依据已失效,新版本仍显示待更新。

当前版本的任一依据记忆被修改、遗忘或删除,会只读计算出“待更新”,并列出失效依据,不在回复准备时重写 persona。数据层 persona_due 从当前版本的生成时间起算:至少五条自我记忆新增或变化,或经过至少 168 小时且有任何变化,才满足定期更新条件。确认候选不重置生成时间;管理员编辑与回滚从新版本创建时刻起算。梦境整理的定期调用由整理线接入。

以下管理接口均需管理员会话,写请求还需 JSON 和会话绑定的 X-Iris-CSRF,沿用 Host、Origin/Sec-Fetch-Site 检查。版本 ID 是全局递增整数;写请求中的 expected_version 必须是当前版本 ID,不能传候选 ID。版本冲突或候选依据已变化返回 409,刷新后由管理员决定下一步;不存在返回 404,参数或正文检查失败返回 400。

方法与路径 请求/结果
GET /admin/api/persona current、pending 摘要,needs_update、stale_basis_count、stale_basis,以及 latest_attempt;没有对象时为 null
GET /admin/api/persona/versions 可按 status、source 筛选;列表含正文、变化程度 small/medium/large、generated_at、published_at、基准版本与回滚来源
GET /admin/api/persona/versions/{id} 另含 sentences、checks、settings、rejection_reasons;每句有 text、origin、admin_written、basis、dates/date_count;依据含记录的修订、该修订正文 content_at_revision 和当前记忆 memory
GET /admin/api/persona/diff 必填 before_version、after_version;逐句 insert/delete/replace/basis_changed 差异,位置从 1 开始
GET /admin/api/persona/self-memories 有效自我记忆,按置顶、重要度、保留强度排序;便于与逐句依据对照
PUT /admin/api/persona {expected_version, content};直接编辑并发布,返回版本详情,不调用模型
POST /admin/api/persona/versions/{id}/confirm {expected_version};确认待确认候选,返回版本详情
POST /admin/api/persona/versions/{id}/reject {expected_version, reason?};拒绝待确认候选,原因最多 1000 字
POST /admin/api/persona/versions/{id}/rollback {expected_version};按旧版本新建并发布,返回新版本详情
POST /admin/api/persona/regenerate {expected_version};立即返回 202 {accepted: true, attempt: {...}},Location 指向任务查询地址
GET /admin/api/persona/attempts、/attempts/{id} 任务列表/详情;不返回原始模型输出
PATCH /admin/api/settings/persona 局部修改 goal、rules、publish_mode,返回完整设置快照;GET /admin/api/settings 的 persona 字段读取当前设置

版本、自我记忆和任务列表均返回 {items, total, limit, offset};limit 默认 30、范围 1—100,offset 默认 0、最大 1000000。手写标记不是模型支持结论;版本详情的 checks.deterministic、checks.model 和 rejection_reasons 应分开展示。失效依据按记忆 ID 去重,包含记录的修订、当前修订和 modified/forgotten/deleted/no_longer_self_memory 原因;彻底清除或不存在的记忆投影为 null。

重新生成使用独立的单线程后台执行器。接受请求时持久化任务、基准版本与设置快照;同一时刻已有 queued/running 任务就返回 409 persona_generation_in_progress,不排第二个任务。页面按 Location 轮询:state 从 queued 到 running,stage 区分 queued/generating/checking/finished;终态是 current/pending/rejected/skipped/conflict/failed。终态可能没有 version_id,须展示 reason(如 daily_token_limit、no_self_evidence、evidence_changed、interrupted),不要把 202 当成发布成功。生成期间其他编辑仍可进行,发布前重新核对版本及依据,冲突不覆盖新版本。正常关闭等待已接受任务;进程异常中断后标记失败,不自动重跑。

生成目标默认“维持稳定的发言风格,并充分认识自我”,监管要求沿用设计 13.3 全文。goal 最多 4000 字、rules 最多 16000 字,去首尾空白后不可为空,至少提交一个字段。发布方式 all_manual 为“全部人工确认”(默认),设置页仍可选择 small_medium_auto(小或中自动发布)或 all_auto(全部自动);任何方式均不能跳过候选检查。保存设置记录操作者和改动字段名,不记录目标/规则全文。新设置仅影响之后接受的生成任务,不重新检查或自动发布已有候选;学习和整理不能改这些设置。

升级迁移 020 只调整未被管理员选择过的旧默认:有效值为 small_medium_auto,且 admin_operations 没有 actor=admin、action=persona_settings_saved 记录时,改为 all_manual。任意一次 persona 设置保存都算,包括只改目标/规则、或显式保存旧默认;这些数据库和其他已选发布方式保持原值。缺失设置行先按旧隐式默认补齐,再作同样判断。当前版、待确认候选和已接受任务的快照均不改写;首次设置模板仍立即创建。

待确认数量沿用 GET /admin/api/persona/versions?status=pending 的 total,取值为 0 或 1;新的待确认候选取代旧候选,确认或拒绝后归零。现有界面继续显示待确认候选,不新增宿主通知。

persona 评测的发布方式独立于产品默认。语料未指定时,评测在隔离数据库中显式写入 small_medium_auto,保持冻结 persona_v1 的预期;可用 persona_publish_mode 指定模式(JSONL 的时间线字段,或 JSON 的顶层默认/时间线覆盖)。实际模式进入规范化输入指纹、候选设置快照和外部判分材料,不借用产品默认,也不修改冻结语料。报告保留变化次数、有依据比例、违规、拒绝、覆盖率及调用用量,并增加删句数/比例、含删句候选数/比例和成功修复句数;删句比例的分母是所有实际生成候选修复前的句子数(含锁定模板),不把失败检查点从执行统计中删去。

宿主 prepare 的 persona 分区如下,版本未初始化时为 version: null、空正文、generated_at: null、needs_update: false、计数 0。其他分区保持独立。

{"persona": {"version": 3, "content": "……", "generated_at": "2026-10-10T08:00:00+00:00", "needs_update": true, "stale_basis_count": 1}}

完成结果的必要证据检查按动作所在的语法范围执行。定语里的“能/可以/希望/不”等不豁免主句的完成断言;明确支配该动作的能力、偏好、否定或比较仍交语义检查。每个焦点只用自己实际选择的来源核对结果式,不能借同句另一焦点的来源补上完成标记。此防护是有限措辞边界,复杂等价表达仍可能被保守删除,不能替代人物、日期和语义判定。

M3 当前状态

当前状态是一份由宿主报告的主要活动,所有入口共享。它供当下回复使用,不进入学习材料、不逐条写成记忆、不更新 persona,也不自动建立目标。值得长期记住的结果仍由宿主另发消息、场景事件或行动结果,经原学习流程处理。逐帧画面、技能冷却等控制信息留在宿主中。管理界面的“状态与目标”页显示当前状态与分页报告历史,“可能过时”的分钟数在设置页修改;管理员不能编辑状态。

方法与路径 请求/行为
GET /api/v1/state 当前状态;无活动时返回 {}
PUT /api/v1/state 必填 activity;可选 details、mood、started_at、host、entry_id,开始/更换活动
PATCH /api/v1/state 可选 details、mood、host、entry_id;{} 为心跳。无活动时返回 400,须先 PUT
DELETE /api/v1/state 结束活动,返回 {};可不带正文,或携带 host/entry_id。重复结束仍保留报告
GET /admin/api/state 管理员只读当前状态
GET /admin/api/state/reports 管理员只读报告历史,limit 默认 30、1—100;offset 默认 0、0—1000000;按报告 ID 倒序
PATCH /admin/api/settings/state {"stale_after_minutes":30};整数 1—525600,省略保留原值;写入 state_settings_saved 操作记录

PUT 的活动经去除首尾空白后,按区分大小写的完整字符串判断是否相同;不合并近义词、内部空白、标点或 Unicode 写法。相同活动按报告/心跳处理,保留开始时间及未报告的细节、情绪。更换活动时清空旧细节,保留情绪及其原更新时间,除非本次 PUT 显式提供 mood(null 清除);开始时间取本次首次报告时间。DELETE 结束活动时仍清空全部状态。宿主在 PUT 显式给出带时区的 ISO started_at 时采用它,包括校正同一活动的开始时间;不接受 null 或晚于本次报告的时间。PATCH 不接受活动或开始时间。

details 是最多 32 项的键值对:键长 1—64 字符、不能全空白,值为最多 1000 字符的字符串、有限数字或布尔值;null 删除该细节。按键部分更新,{} 保留现有细节;合并后仍受 32 项及 32KB UTF-8 的限制。mood 最多 200 字符,null 清空。被明确报告的细节/情绪即使值相同也刷新各自时间,未报告的字段保持原时间。activity 长 1—200 字符;host 为可选来源名称(1—100 字符),entry_id 为可选来源标识(1—200 字符),可先于消息入口创建。每次报告的来源独立保存,省略/null 表示未知,不沿用上一个宿主。此来源名称不是身份认证;M4 起宿主名称以令牌为准(见「宿主接入」)。

状态请求正文上限 32768 UTF-8 字节,必须使用 JSON;字段非法、未知字段、超限或无活动时 PATCH 均返回 400,error.fields 给出字段说明。Host 校验沿用现有规则。管理读取要求管理员会话;设置写入还要求 JSON Content-Type、CSRF、Origin/Sec-Fetch-Site 校验。管理员没有编辑或结束活动的管理接口。

例如宿主报告:

{"activity":"探索海岛","details":{"scene":"海岸","progress":2},"mood":"紧张","host":"game","entry_id":"game-a"}

当前状态的响应形状如下(时间按角色时区输出带偏移的 ISO 格式;未设置时区时使用 Asia/Shanghai):

{
  "activity": "探索海岛",
  "activity_updated_at": "2026-10-09T12:00:00+08:00",
  "details": {
    "scene": {"value": "海岸", "updated_at": "2026-10-09T12:00:00+08:00"},
    "progress": {"value": 2, "updated_at": "2026-10-09T12:00:00+08:00"}
  },
  "mood": "紧张",
  "mood_updated_at": "2026-10-09T12:00:00+08:00",
  "started_at": "2026-10-09T12:00:00+08:00",
  "start_time_basis": "first_report",
  "duration_seconds": 0.0,
  "updated_at": "2026-10-09T12:00:00+08:00",
  "possibly_stale": false,
  "stale_after_minutes": 30,
  "host": "game",
  "entry_id": "game-a"
}

start_time_basis=host 表示“宿主提供”,first_report 表示“自首次报告起”。持续秒数在读取时按真实时间差计算(跨时区/夏令时照常),系统时钟回退时最低为 0。activity_updated_at 是最近一次 PUT 报告活动的时间,updated_at 是最近一次任何报告的时间。情绪从未报告时 mood 和 mood_updated_at 都是 null,明确清空情绪仍记录时间。

严格超过阈值未更新时 possibly_stale=true,恰好 30 分钟仍为 false;只标注,不自动结束活动。设置修改即时影响后续读取。prepare 的 state 总是包含当前状态,召回判断完成后再读取,以免返回判断期间已经结束的活动;search 仅在 include_state=true 时提供此分区。它们都不改变记忆 ID、顺序、reason 或记忆预算。

每次有效 PUT/PATCH/DELETE 与当前值在同一事务保存报告,包括没有值变化的心跳。历史返回 id/host/entry_id/reported_at/method/action/reported/changes;action 为 start、replace、update、heartbeat 或 end,reported 保存本次明确报告的字段(来源单列),changes 保存实际值变化的 before/after,细节按键列出。并发宿主按提交序号先后写入,最新报告的同一字段覆盖旧值;矛盾的旧报告仍保留在历史中,不形成确认事实。结束后历史仍可查询,读取历史不调用模型或增加召回次数。

M3 目标、询问与提醒(后端)

目标属于角色,所有入口共享,入口只记录产生位置;它是待办意图,不是当前活动、记忆或 persona。学习仍可同时保存承诺记忆与内部目标,目标本身不加入学习材料,也不会因聊天中的含糊说法自动完成。询问为 kind=question,没有截止时间或提醒,宿主可直接注入,并决定何时问。

学习产生的内部询问(origin=internal、kind=question)超过保留天数仍为 open 时,由每日维护自动结束为 abandoned,closed_by=maintenance。保留天数使用运行设置 goals.internal_question_expiry_days,缺省 7,须为 1—365 的严格整数。GET /admin/api/settings 的 goals 分区返回此字段;可通过 PATCH /admin/api/settings/goals 提交 {"internal_question_expiry_days":14} 修改,字段省略时保留现值,字符串、布尔值、小数、null 或越界值返回 400。可与其他目标设置一起提交,所有字段与操作记录在同一事务中保存;沿用管理员会话及 CSRF 校验。页面接入由 UX 后续完成。

天数按维护任务开始时的角色时区计算自然日:创建当天为第 0 天,第 N 天仍保留,进入第 N+1 天后的首次维护才结束。例如 N=7、10 月 10 日创建,10 月 17 日仍保留,10 月 18 日起可结束。续跑使用该维护任务的开始时间与时区判断到期,结束时间记录实际执行时间;写入前核对修订号,冲突留到下一次维护。此清理不依赖模型整理/目标依据复核开关,不发通知或提醒,也不改变承诺记忆。维护报告列出数量与目标 ID,目标修订记录固定原因 question_expired 及状态变化,不复制正文。合并来源节点跳过,保留目标按自身来源与创建时间判断;“可能重复”仍需按原规则处理。

宿主或管理员创建的询问,以及所有 kind=normal 目标,不适用自动到期结束。宿主接口字段保持不变,可通过原有目标状态查询观察结束结果。

方法与路径 内容
GET /api/v1/goals state、kind、overdue、due_soon 筛选,limit(1—100,默认 30)、offset 分页
POST /api/v1/goals content 必填;可选 kind(normal/question)、deadline、reminder_minutes、people(主体 ID 数组)、entry_id、host_key;返回 201 和 {goal, submitted_id, dedup}
PATCH /api/v1/goals/{id} state=completed/abandoned、deadline、reminder_minutes;可带 expected_revision,不匹配返回 409
GET /api/v1/notifications after(默认 0)、limit 拉取;返回 {items,next_cursor,has_more}

宿主和管理接口的目标写请求最多 32KB UTF-8,正文 1—4000 字符,涉及的人最多 100 个,入口/宿主键最多 200 字符;未知字段和非法值返回 400 并标明字段。截止时间接受带时区的 ISO 时间或 YYYY-MM-DD;日期解释为角色时区当天 23:59:59,返回按角色时区转换的 ISO 时间。提前量为 0—525600 分钟,null 使用默认值 60。people 排除我和场景,同名但不同主体不会合并;已确认的人物合并按当前主体解析。

目标返回 id/content/kind/origin/state/deadline/reminder_minutes/effective_reminder_minutes/people/entry_id/host_key/revision,以及创建/更新时间、closed_at/closed_by、merged_into、overdue/due_soon 和 possible_duplicate/possible_duplicate_ids。due_soon 表示已到临近时刻,过期时也为 true。学习旧输出中无法解析的期限保留原文,并标记 deadline_unresolved=true,不臆测时间或生成提醒;宿主和管理员可修改成有效期限。

注入先保存原始目标,再跨入口在同 kind 的未结束目标中去重。decide_dedup(candidates, proposed) 返回 {status,target_id}:created、merged、possible_duplicate 或模型判断待复核的 pending。关闭目标去重判断时沿用确定性规则:仅正文压缩连续空白、忽略大小写和句末标点后完全一致,且涉及人集合相同、期限兼容时自动合并。内部标点和英文词界保留,复用 M2 的中文/阿拉伯数字与否定序列规则;一个期限为空兼容,两个明确期限不同不兼容。其他规范化文本相似度至少 0.88 的兼容项只标记可能重复。管理员新建(origin=admin)也执行同一判断,但 merged 结果降为 possible_duplicate,保留两条目标,等待管理员明确合并或驳回;宿主和学习产生的新目标仍按上述规则自动合并。这不是目标去重模型质量门槛的结论。

合并保留较早目标,原目标及其依据保留可追溯,占位的 merged_into 指向保留项;来源消息、同证据角色记忆与未取提醒归入保留项。明确截止时间补入空值;两个明确提前量不同则采用较早提醒(较大分钟数),原值仍在被合并项中。合并后的同阶段待取提醒只留一条,其余保留为取消记录。相同 host_key 的重试返回首次创建的完整回执快照,即使后来目标已修改;读取最新状态用列表。修改已合并 ID 返回 409 goal_merged 和 canonical_id,需要明确选择保留项再提交。

有期限的普通目标在临近、到期时发布提醒;创建/修改时已经错过提醒时刻则立即发布一条,未来的到期提醒仍保留。服务恢复时同一目标错过的阶段只发布一条即时提醒,不逐条补发。提前量为 0 时,临近和到期同刻,合为一条。过期后的周期提醒按角色时区每天最多一次,内容请宿主选择放弃或修改期限;停机多日只生成当前一天的一条。除上述学习产生的内部询问到期规则外,系统不自动失败、放弃或顺延。

通知的 id 是实际发布时分配的单调游标,未来计划不提前占用游标。目标提醒每条有 kind=goal_reminder、reminder_kind=soon/due/overdue/immediate、goal_id、content、发布时的期限 deadline_at、计划/发布时间、status=pending/taken/cancelled 及取走/取消时间。取走仅表示宿主读到;相同游标可重放已取记录,方便恢复丢失的 HTTP 响应,宿主按通知 ID 去重。取走不等于送达,也不完成目标。修改期限/提前量或完成/放弃会更新未来计划、取消未取的目标提醒;已取记录和去重结果通知保留。默认提前量变动重排使用默认值的未来目标,保留已过期的待取提醒。

prepare 与 search(include_goals=true) 返回最多 10 个未结束目标/询问:临近和过期优先,其次本入口、涉及当前参与者,再按期限与创建时间。此分区不改变记忆选取。管理员接口沿用会话、CSRF 和操作记录:GET/POST /admin/api/goals,GET/PATCH /admin/api/goals/{id};管理修改须带 expected_revision,可编辑正文。列表另支持 entry_id/possible_duplicate/deadline_from/deadline_to,日期筛选包含整天。详情含 sources、promise_memories(同证据角色记忆及当时修订,并非新增语义承诺分类)、merged_goals、提醒计划和通知历史。

可能重复关系通过 POST /admin/api/goals/{id}/duplicates/{other_id}/merge 或 /dismiss 处理,正文须含两方 expected_revision/other_revision;合并仍守住类型、人物、期限、数字与否定边界。GET /admin/api/notifications 按 status/goal_id 分页,只读不取走。PATCH /admin/api/settings/goals 设置 default_reminder_minutes、overdue_reminders(默认 true)和 internal_question_expiry_days(默认 7),省略的字段保留现值,写入操作记录。页面接入留到后续界面阶段。

旧状态映射:done/completed → completed,cancelled/canceled/abandoned → abandoned,其余(包括 in_progress、pending、failed 和未知值)→ open;过期/失败不推导放弃。未知旧来源映射 internal。无法确定的历史完成时间和操作者保留 null。

初版验证(2026-10-09,head 5eec156,基线 be3a98a):Python 3.13/3.12 各 1244 项通过,uv build 通过。未修改的 compare_learning_requests.py worker 由仓库外脚本补入公开 v5,116 案例、272 批,system/user 字符串、顺序、purpose 与 max_tokens 均逐字一致;候选库另有当前状态、宿主目标和询问,仍无请求差异。原脚本不输出目标,另对 14 个公开目标案例手写并冻结固定输出、在全部 116 案例重放(102 案例无目标输出):两边请求和输出仍一致,未合并目标 14→14、依据 19→19;物理行 14→19,差异仅 L009 1→2、M211 1→2、M218 1→3、N312 1→2,均为重申承诺保存后指向较早目标的占位,正文/kind/截止时刻和全部来源保留。该重放只测写入,不是模型质量评测。

初版召回验证用七份公开语料独立入库,固定各自时钟和相同真实向量缓存,judge=false,分别比较默认混合检索与全文降级的有序 (memory_id, reason):228 查询 × 2 路径共 456 组,零差异。审查修订只调整管理员创建的去重结果和目标分区读取,不改记忆选取、判断、排序或 reason,本轮未重跑召回语料。

审查修订验证:Python 3.13/隔离 3.12 各 1257 项通过(新增 13 项),uv build 通过。对 main a9af8ec 重跑五份公开学习语料,116 案例、272 批逐字一致;候选仍含状态、宿主目标和询问,完整请求 SHA-256 与初版相同。迁移保持 012。

性能先作分段归因,再在没有其他模型评测/无头浏览器测试的窗口测量。初版 5 万记忆 prepare P95 为 319.7/413.9ms,当时系统负载由 4.7 升至 8.6;这些跨次数字不能作为目标数量的因果对照。本轮观测到并行 Node/无头 Chrome 测试及 persona 评测,受干扰的测量已中止或仅作诊断,原始记录全部保留。分段计时显示较大的尖峰在未修改的 _rank 中,墙钟明显高于进程 CPU 时间;旧目标分区自身的主要成本是读取全部目标并构造 Python 对象,其次是逐行排序。不能仅凭旧日志还原当时每项等待的来源。

正式配对对照使用两份预置为 0/1000 个目标的合成库,同一进程共享相同的只读向量索引、默认召回配置、固定时钟及原基准的假模型;交替旧/新实现及目标数量,每组预热 5 次后测 60 次。测量期间不修改目标数量,关闭两边调度器以隔离分区成本,在 prepare 内直接计时。SQL 排序并 LIMIT 后只对前 10 个投影,主体合并链、旧日期和微秒边界有回归测试。下表单位为 ms;增量为逐对目标分区耗时差的 P95:

记忆数/查询 旧分区 P95(0/1000 目标) 新分区 P95(0/1000 目标) 新分区增量 P95
5 千/不点名 0.057/4.210 0.247/0.990 0.762
5 千/点名 0.047/4.238 0.250/0.749 0.541
5 万/不点名 0.087/5.070 0.327/1.125 0.802
5 万/点名 0.070/6.563 0.587/1.199 0.891
记忆数/查询 旧 prepare P95(0/1000 目标) 新 prepare P95(0/1000 目标) 新 prepare P95 差值
5 千/不点名 25.0/27.3 25.1/26.9 +1.8
5 千/点名 27.2/33.0 28.2/28.7 +0.5
5 万/不点名 156.9/208.8 192.3/137.1 -55.2
5 万/点名 226.2/735.6 570.0/585.8 +15.8

目标分区增量 P95 最大 0.891ms,完整 prepare 的 P95 差值最大 15.8ms,均在 30ms 内。双库配对中的整体墙钟仍有排序/等待波动,包含负差值,不能把两个独立 P95 的差当作确定的 CPU 节省;标准 R10 的绝对时延另用原单库基准测量。

随后在无并行评测/浏览器测试的窗口,运行未修改的 uv run python evals/benchmark_retrieval.py --default-config(每库 1000 个未结束目标、10 条待取提醒;保留原调度器,预热 5 次、测量 60 次),四组全部 ≤500ms:

记忆数/查询 prepare P95(ms) HTTP prepare P95(ms)
5 千/不点名 20.0 32.7
5 千/点名 31.7 35.1
5 万/不点名 140.1 139.8
5 万/点名 154.4 156.8

完整材料、脚本、日志及逐案例差异保存在仓库外 iris-eval-artifacts/m3-goals-20261009/ 和 iris-eval-artifacts/m3-goals-review-20261009/;模型去重与方法选择留待下一步。

宿主提醒事件流(SSE)

宿主可新增 GET /api/v1/events 订阅已发布提醒,带原宿主 Bearer 令牌;Host 和入口范围沿用现有检查。这是 v1 兼容新增,原 /api/v1/notifications 拉取与取走行为保留。reminder 载荷与拉取提醒项同形,事件流自身不标记取走;heartbeat 约每 15 秒一次。宿主按通知 ID 持久去重,断线用 Last-Event-ID 或 last_event_id 续订,收到 resync 时用自己保存的拉取游标补齐。全局目标仍按原读取规则可见,已取消提醒不再推送。

每令牌最多 2 条连接,全局 32 条;无提醒空闲 5 分钟或连接满 1 小时结束,宿主可续订。每条活跃流约 1 秒查询一次 SQLite 只读快照,每批最多 100 项,不持业务写锁;无连接时不轮询。断点窗口为 15 分钟、全服务 10000 个帧断点及最近 1000 个通知 ID,任一超出或重启后以 resync 补拉。只缓存 ID、水位与时间,不缓存提醒正文。正常关闭主动结束流,慢客户端发送也有时限。

连接器和 AstrBot 插件建议共用一个后台订阅任务与原拉取队列,订阅失败/断线时继续拉取并退避续订;SSE 帧 ID、通知 ID 和拉取游标各自保存。推送数或 taken 均不代表平台送达,不自动完成目标。请求、帧格式、错误码、补拉和接入步骤见 宿主接口文档。GET /api/v1/events/status 只返回当前连接数、累计 reminder 帧数和限额,计数在重启时归零。

来源前后文、修订快照与依据复核(GO 第五步)

管理详情的每个 sources 项保留原来的消息字段,增加 message、context、missing、notice。前后文沿用记忆详情:同一入口按消息入库 ID 取前两条、来源本条、后两条,不跨入口拼接;包含发言人名和原始消息类型,不回灌为新经历、不增加召回或使用次数。来源消息不可用时保留 message_id,返回 message=null、context=[]、missing=true 和“来源消息已清理或不可用”;当前清理逻辑仍保护被目标引用的消息。

管理接口 内容
GET /admin/api/goals/{id}/sources 来源消息及各自前后文,limit/offset 分页;详情也提供同样的前后文
GET /admin/api/goals/{id}/revisions 按快照 ID 倒序分页,返回 revision_before/revision_after/action/before/after/actor/reason/created_at
DELETE /admin/api/goals/{id}/basis-annotations/{annotation_id} 清除该目标的一条活动标注;JSON 须带 expected_revision,可带 reason;沿用管理员会话、CSRF、Host/Origin 校验和操作记录

分页沿用 limit=1—100(默认 30)、offset≥0,未知字段、非法字段返回 400,目标/标注不存在返回 404,修订不匹配或标注已被后续复核替代返回 409。对已合并目标的写入仍返回 goal_merged,需明确选择保留目标。管理编辑、合并、驳回和清除标注可提供 1—500 字符的非空白 reason;省略时记录对应操作的固定原因,不允许只提交原因而不修改目标。

迁移 018 新增 goal_revisions、goal_basis_annotations。创建记录初始值;正文、期限、提前量、状态、合并/重定向、可能重复及其驳回、依据标注的变化都在同一事务保存前后值、操作者、时间与原因。快照只存发生变化的领域字段;关系按 ID/状态记录,原始消息、记忆正文、未变的目标正文、宿主回执和模型任务信息不重复复制。修改操作记录引用快照 ID,不重复写正文。无实质变化和失败/冲突不产生快照;合并也记录被合并项、被改写的旧占位和受影响的重复关系,相关修订号随之更新。新目标的历史状态为 complete;迁移前已有目标返回 history_status=pre_migration_no_snapshot、missing_through_revision 和“迁移前无快照”,不伪造旧历史,之后的修改正常记入。详情的 revision_history 同样给出这项提示。

目标列表、详情、prepare 和 search(include_goals=true) 新增 basis_needs_review 及 basis_annotations,不改排序或记忆返回。每条标注指出 memory_id、当时的 basis_revision、观察到的修订/生命周期/彻底清除/合并去向、changes、时间和“依据可能不成立”说明。变化代码为 revision_changed/forgotten/deleted/purged/missing;不推断承诺是否取消或目标是否完成。

确定性复核只检查未结束、未合并的内部目标(含询问),也检查吸收了内部目标的保留项,避免跨来源合并后丢失内部依据。比较 goal_memories 的原始依据修订与当前记忆,不读取记忆修订历史、不调用模型、不改记忆,不完成/放弃目标、不改正文/期限/提前量/提醒。过期仍按当前时间计算,不写成新状态。

记忆仅从遗忘恢复、且仍为原依据修订时,标注自动结束;修订不同则保留“修订已变化”,不会把恢复当成重新确认承诺。当前观察变化时旧标注变为 superseded,已恢复或依据关联移除时为 resolved。管理员清除记为 cleared:同一依据修订和同一观察状态不重复提醒,新的修订、生命周期或清除状态变化再复核;不改写原依据修订。合并复制依据、标注和清除记录,原记录保留追溯;相同依据事件已有目标记录优先。

给每日维护的接口均在 iris.goals,本步提供函数,由整理线接入调度:

# 已持有写事务:检查单个目标,修订不符抛 GoalConflict。
review_goal_basis(conn, current, goal_id=goal_id, expected_revision=revision)
# 也支持省略 goal_id,在调用者现有事务内复核全部符合条件的目标。

# 推荐每日维护使用:一次有界扫描,逐目标一个短事务。
page = review_goal_basis_items(store, current, after_id=0, limit=100)
# 用 page["next_after_id"] 续扫,直到 page["has_more"] 为 False。

current 为带时区的 datetime,可注入固定时钟。返回 checked、changed_goal_ids、annotation_ids、ended_annotation_ids(含恢复结束或被新观察替代的旧标注);逐项版本另返回 conflicts/next_after_id/has_more,limit 为 1—1000。逐项版本写入前核对扫描时的目标修订,冲突跳过、留下一次从 ID 0 开始的完整复核;记忆的修订和状态只在该项写事务内读取,不从事务外的旧观察写回。可中断续扫,重复运行无重复快照或标注。本步不修改或调用每日维护、模型整理及其调度入口。

本步基线为 main 5a52b57d7c84b306518968cdc8f0335c9ebe3a8c;35 项新增确定性/接口测试。学习逐批对照覆盖 v1—v5 的 116 段、272 批(156 批带已有记忆),与 main 逐字一致,请求 SHA-256 均为 9988666d3634cdde81ded0d1a540cf92cbda5d30b65bf8ae5a85301274861092。召回在两个独立进程中给七份公开语料同样的固定 2048 维 float32 假向量,关闭召回判断,混合召回/纯全文降级共 228×2=456 次查询的记忆 ID、顺序和 reason 差异为 0;候选库额外含依据标注、快照、目标、询问、状态和待取提醒。这是返回回归检查,不是真实模型质量重评。

Python 3.13 与隔离的 3.12 按顺序运行,各通过 1635 个测试;uv build 成功。R10 沿用未修改的 benchmark_retrieval.py --default-config,main 与本分支串行运行相同的合成数据库:每库额外有 1000 个未结束目标、1000 条活动依据标注和 20 条待取提醒。每组预热 5 次、测量 60 次,使用固定假判断;四组 prepare 及 HTTP P95 均低于 500 ms。数值为本次运行结果,不把小幅计时波动解释为加速。

记忆数/查询 main prepare P95 本分支 prepare P95 main HTTP P95 本分支 HTTP P95
5000/不点名 28.8 ms 33.0 ms 27.1 ms 27.1 ms
5000/点名 32.7 ms 25.3 ms 27.1 ms 41.0 ms
50000/不点名 130.3 ms 123.3 ms 145.7 ms 146.8 ms
50000/点名 129.0 ms 125.1 ms 140.6 ms 129.2 ms

完整材料、离线脚本、首次失败和最终日志保存在仓库外 /Users/cassia/Local/Code/iris-eval-artifacts/m3-goal-review-20261010/,不读取模型配置,不调用真实模型,不访问隐藏集。

M3 目标去重判断

默认使用 D6(GD4 复核迭代,goal_dedup_judge_v6.md):模型逐候选判断 same(重复)、uncertain(需核对的可能重复)、different(不重复)。先核对动作/所问信息,再区分目标已绑定的事实与来源中讨论的备选;明确不同的对象、时段、要求或责任方向判不重复。有动作和人物事项/对象联系、时间可能重叠,但身份、指代、场次、版本、数量或执行状态尚未确认时保留可能重复,不要求先猜定同一事件。讨论中的改约不直接当作已确定的新事件;不同操作步骤和不同信息项也不因主题相同而提示。严格核对 JSON 字段、类型、候选 ID、数量与原顺序;无效输出进入既有失败/待复核流程,不修补判断结果。

公开集分开统计“不重复”误提示与“不确定”命中:C2 为 12/29(41.38%)/10/10,此前 D3 两轮均为 0/29/2/10,因此 D3 未达到复核后的 uncertain 条件。GLM high、60 秒测量预算下,D6 连续两轮均为误合并 0/13、应合并识别 28/28、separate 误提示 0/29、uncertain 命中 10/10;每轮 62 次调用,无降级或待复核,达到修订后的四项公开 dev 条件。uncertain 有候选标注时要求指向其中一个,未提供时统计是否出现提示;不再将其与 separate 合并作为误提示门槛。旧提示词和历史候选保留,既有关系不批量改写。冻结、迭代和完整数字见 GD4 候选 和 GD4 复核结果。隐藏集仍由规划者复核。

普通候选沿用 C:同 kind、未结束且未合并,人物和期限兼容,数字与否定序列一致;正文相似度至少 0.25 或有共同涉及人,最多 8 个。原 C2 的序列冲突候选仍要求人物/期限兼容,并满足规整正文相似度至少 0.25,或同一非空入口且来源消息有共同的非 self/scene 发言人;按相似度、创建时间、ID 取最多 8 个。D6 将两组一起交给模型,最多 16 个,不再按相近条件直接打标。材料仍只有正文及时间、涉及人、期限、入口类型和来源摘录。

自动合并限制保持:只有唯一 same 且其余无 same/uncertain、未超限、没有原始序列冲突才可自动合并。序列冲突配对即使模型判 same 也只能提示;其提示被模型或额外相似度门槛过滤,仍不能解除对其他候选的合并保护。管理员创建不自动合并。人物缺失按未知处理,合并保留已知人物;明确不同的人物仍拒绝合并。管理员与宿主沿用 possible_duplicate_ids 查看提示,管理员可驳回;不兼容内容须先明确编辑才可手动合并。

宿主 POST 先提交目标,再在请求内判断。超时、排队满、限流、暂停或在途修订变化返回 dedup.status=pending,由调度器补做。学习事务内只检索确定性候选并入队,不按相似度写入新弱提示;提交后才调用模型,不改学习材料/校验/提示词。宿主键重试保留首次响应,后台结果不改写回执。补做结束写操作记录,并发布 kind=goal_dedup_result 通知;宿主按通知发布游标拉取,查看最新目标列表中的合并与可能重复状态。

GD2:pending/运行中的目标在补判结束前不进入 prepare 和 search 的目标分区,也不发布提醒;管理列表和详情仍展示,并返回 dedup_pending、dedup_injectable、dedup_fallback_at。过滤先于目标分区的排序截取,待复核目标的 ID 也不作为其他目标的可能重复引用注入。内部学习入队后下一轮调度即可补判;宿主同步失败后按 30 秒、2 分钟、10 分钟退避,之后每 10 分钟重试,复用该用途的健康、并发、排队和每日 token 门槛。模型仍在事务外调用,所有参与判断的修订号、范围和门槛设置在写回前复核。

goal_dedup_judge.pending_fallback_minutes 控制兜底时长,默认 30 分钟,范围 0—1440;设为 0 或关闭判断时立即恢复注入。兜底按首次入队计时,重试、进程重启及用途暂停都不延长它;放行仍保留 pending 标注并继续补判,不把不可用当作“不重复”。30 分钟覆盖几轮正常退避,又避免暂停或每日额度耗尽让真实目标等到次日。实际截止不顺延:放行时已经临近/过期则只发布一条即时提醒,不补齐所有错过的阶段;之后继续按原期限和每日上限提醒。提醒及补判结果通知在可注入时分配游标,旧策略下尚未取走的通知会取消并在放行时重新发布,避免先创建后可见的游标漏取。已取走的通知保留记录;相同截止时间已取走的临近提醒、当天到期/过期提醒不会在放行时重发,尚未通知的新阶段照常提醒。

弱标记设置 goal_dedup_judge.possible_min_similarity 范围 0—1,0 表示不增加相似度过滤。GD3 在 GLM high 的公开集标定后将默认值从临时的 0.6 调回 0:0 保留 28/28 应合并识别;网格中的所有正门槛都会降低识别率(0.4 为 25/28,0.6 为 16/28),不符合预登记的“识别率不下降”条件。各格点自动误合并均为 0/13;当时默认 0 下不应合并中标可能重复仍为 22/39,因而在 GD4 改用上述明确标准的模型判断。详见 GD3 网格与选择报告。

分数沿用规整正文的 SequenceMatcher 相似度,在 D6 中仅作为模型 uncertain 和序列冲突提示的额外过滤,不能独立产生提示。历史 A/C2 探测仍保留原规则。模型明确 same 的管理员目标仍标为可能重复,其他 same 歧义也保留;被过滤的弱提示继续阻止自动合并。共享入口/来源人不能绕过标记门槛。既有关系保留,管理员已驳回的关系不重开。两个参数通过 Goals(store).configure_dedup(pending_fallback_minutes=30, possible_min_similarity=0, actor="admin") 校验、持久化并留操作记录,下一次读取/调度生效。已显式保存的门槛(包括 0.6)不被新默认值覆盖;当前管理 HTTP 的字段保持原样。

迁移 014 新增 goal_dedup_jobs,保存任务状态、两边修订号、尝试次数、下次执行时间及可过期租约。调度器只查询到期任务,不重新去重历史目标;失效租约可在重启后恢复。模型调用在事务外,写回核对租约、本目标和全部候选修订,并再次核对人物/来源投影;失效结果不落地。完成或放弃取消未完成复核。管理目标详情增加只读 dedup_review,含状态、方法、输入修订、尝试次数、结果、下次执行和更新时间,不暴露租约凭据。

goal_dedup_judge 用途默认继承对话端点及显式推理档位(未配置则不发送),健康状态、并发和有界排队独立于学习及召回判断。前两次 429 按 Retry-After 或 2/4 秒退避,第三次连续可重试错误暂停并探测;每日 token 上限同样阻止判断。PATCH /admin/api/settings/goal-dedup-judge 沿用会话、CSRF 和操作记录,设置 enabled、budget_seconds(大于 0 且至多 10 秒)、concurrency(1—8)和 queue_limit(0—64)。默认预算按有效探测较高 P95 加 20% 向上取整为 5 秒,并发 1、排队 8;已保存的显式设置保留。此前 C 在 5 秒产品预算下的有效双轮宿主同步 P50/P95 为 1.05/3.44 秒、1.19/2.83 秒;该阶段含作废尝试的宿主降级为 10/459,见 GO 第三步报告。历史 C2 按冻结的 60 秒测量预算运行,宿主 P50/P95 为 1.10/2.91 秒、1.18/2.58 秒;这不改变产品的 5 秒时限,也不代表以后不会超时。D6 两轮的目标判断 P50/P95 为 2.38/5.05 秒、2.42/4.45 秒,最长一次 14.06 秒;这是 60 秒质量测量,不能据此声称 5 秒产品预算无降级。显式关闭 enabled 保留确定性相同合并,不再仅凭近似正文新增弱提示;显式 A 仅供历史探测。模型不可用时保留待复核,不自动切换到 A 合并。独立模型端点的保存/测试/重试沿用 /admin/api/settings/models/goal_dedup_judge 系列接口。

探测脚本支持仓库外 --corpus 和 --out;--fake 使用本地传输,不读取模型配置、不发送网络请求,结果明确标为模拟并被选型函数拒绝。使用方法与本轮验证汇总见 探测说明。

M2 人物与身份联系(后端)

人物管理接口沿用管理员会话、CSRF、JSON Content-Type 和本机 Host 校验;人物页提供列表、详情、否认、确认合并和别名管理,有待确认联系时导航和试用页会提示。

接口 输入与行为
GET /admin/api/people text 搜索名字/有效别名;pending_only=true 只列有待确认联系的人;include_merged=true 包含占位。支持 limit、offset,返回 pending_links、别名、记忆数与主体 revision。默认不列 self/scene。
GET /admin/api/people/{id} 平台身份、别名、记忆数及按生命周期分组、memories_url、same_as 联系的相信程度和证据消息、扮演关系。合并占位带 merged_into、canonical_id。
POST /admin/api/people/links/{id}/deny { "expected_revision": 1 },使用联系修订号;保留该联系为 denied,后续学习不能恢复或反向重建。
POST /admin/api/people/links/{id}/confirm { "target_id": "A", "expected_revision": 1, "expected_source_revision": 2, "expected_target_revision": 2 };A 必须为联系一方,另一方 B 合并到 A。确认直接合并,没有暂存的确认态。
POST /admin/api/people/{id}/aliases { "alias": "阿林", "expected_revision": 2 },使用主体修订号;返回稳定别名 ID。
DELETE /admin/api/people/{id}/aliases/{alias_id} { "expected_revision": 3 };移除别名及折叠证据,并阻止学习自动加回这个别名。管理员显式添加可以恢复。

成功操作与变更在同一写事务保存到 /admin/api/operations。修订过期、重复确认或操作已合并主体返回 409;对象不存在为 404,输入错误为 400。合并拒绝 self/scene、扮演者与其角色、从属人物与其祖先之间的合并。

B 的账号、有效别名、记忆涉及人和说话人转到 A,B 的名字成为 A 的别名(与 A 同名时不重复添加);从属于 B 的人物改从属于 A。记忆正文、修订号、相信程度、保留强度和向量不变,也不因身份合并去重现存记忆。B 和历史消息作者保留用于追溯,新消息的 B 账号解析到 A。合并记录保存平台账号键、记忆/别名/联系/从属主体 ID,以及折叠目标 ID,不保存记忆或消息正文。

同一对主体的联系折叠时,否认优先;状态相同时保留目标方已有联系的相信程度,不取最大分。扮演保持方向,折叠时保留每个原场景。折叠的旧联系和重复别名保留为证据档案,仍保护证据消息;人物页读取有效联系时汇总这些证据。手动删掉的别名在合并后仍不会自动回来;确认身份本身明确授权把 B 的显示名添加为 A 的别名。

prepare 和 search 为每条最终返回的记忆追加 subject_annotations,涉及者按 about 和 speaker_subject_id 判断:

{
  "possible_same_as": [
    {"link_id": 1, "belief": 70, "subjects": [{"id": "A", "name": "小林"}, {"id": "B", "name": "林同学"}]}
  ],
  "roleplay": [
    {"link_id": 2, "actor": {"id": "A", "name": "小林"}, "character": {"id": "C", "name": "船长"}, "worlds": ["海岛游戏"], "belief": 90, "fictional": true}
  ]
}

场景未知时 worlds 中保留 null,不假称现实。被否认的联系不出现在标注中;标注不返回证据原文,也不将扮演者与角色的经历相互归属。标注在名额、记忆 token 预算及召回判断完成后追加,因此不占用原来的记忆选取预算,不改变顺序、reason、选取名额或判断请求。宿主应将这些附加元数据计入自身的完整上下文预算。学习选材不增加这些字段。

记忆管理列表支持可选布尔 pinned:省略为全部,true 为已置顶,false 为未置顶;分页与 total 使用相同筛选。记忆列表页的置顶筛选使用这个参数。

学习 v7 已接入同一个最终主体解析接口:新快照排除占位和折叠别名,把历史消息/引用作者映射为最终主体;提交事务内在去重之前重新解析说话人、涉及人、别名所属人、联系双方和新从属主体的 parent。两端映射为同一人的 same_as 丢弃,别名若在合并后等于保留主体的显示名也丢弃。回复 R13 与学习共用数字/否定词比较,中文数字(如周三/周五)不同的记忆不会被近似去重。

M2 生命周期与每日维护(后端)

保留强度限制在 0—100:有效记忆低于 F(默认 20)进入遗忘并记下时间;遗忘记忆达到 H(默认 35)恢复并清除遗忘时间;两阈值之间保持原状。管理员手动遗忘把强度降至至多 F−1 并取消置顶,恢复把强度提至至少 H,不重新置顶。置顶不自动恢复,也不受维护的自动变化:不衰减、不受依据失效扣减、不自动删除。重要度改变增加修订号;置顶、状态与保留强度调整不增加修订号,但所有人工操作仍核对 expected_revision 并留操作记录。

默认每天按实例 timezone 的 03:00 维护;本节的 M2 确定性阶段不调用模型,随后执行下节的 M3 梦境整理。接收、学习、准备和反馈照常。启动时超过 24 小时没有完成运行,才在随后所有入口安静满 10 分钟时补跑一次;安静时间从启动或最近接收时间中较晚者计算。中断的运行优先续跑,阶段游标、检查计数和项目变更在同一小事务中提交,已完成衰减不重做;续跑中新进入遗忘的时间记为实际执行时间,保留期不从旧请求时间起算。一次运行固定设置和对象上界,新设置对以后接受的运行生效,反馈与人工操作立即读取最新值。报告只为实际变更及失败保存逐项 ID;检查数量按阶段汇总,冲突/跳过按原因计数,无变更项目不逐条留记录。

衰减只对有效、未置顶的记忆按实际执行次数计算:重要度 <40 每次减 1;40—69 的每条记忆累计三次符合条件的维护后减 1;≥70 不衰减。遗忘期间不衰减、不增加衰减计数,自动删除计时照常;未置顶者仍按新设置的 H 检查恢复,保留强度不变。中等重要度计数只在符合条件且修订校验成功时累加,暂时置顶、遗忘或改到其他区间保留余数;停机、错过的日子不补算,手动运行也计一次。维护会按阈值转换状态、删除遗忘满 180 天的记忆,并对实例时区前一天放弃的批次复用“重新学习”,每批一生只自动重试一次;拒绝批次不自动重试,缺口仍仅在成功事务中清除。

依据记忆进入遗忘或删除时保存依赖事件;维护对 sources.kind=memory 的每对(依赖记忆、依据记忆)只扣一次,默认 −10,不改相信程度。依赖对象置顶时保留待处理事件,取消置顶后由后续维护处理。依据在维护前恢复也不丢事件,恢复不返还,循环依赖不会反复扣减。模型判断独立证据与纠正后的重新整理留到 M3。

消息按接收时间超过 30 天且已学习/已放弃/内容拒绝/已过滤才可清理,未学习消息保留。记忆来源、goal_sources、主体别名/联系证据、立即学习位置继续保护;批次三段只在 waiting/running 时保护,succeeded/abandoned/refused 的批次和试用回复去重键不保护消息,回复本身按相同规则清理。批次仍保留原消息 ID,详情对缺失消息显示占位;目标段缺失时不能重新学习,管理请求返回 409 并说明“已清理”,自动重试跳过,记忆缺口保留。消息 ID 使用 AUTOINCREMENT,清理最大 ID 后也不会串到后来收到的消息。

普通删除仍保留历史和来源。彻底清除必须显式传 confirm=true,删除修订、标签、涉及人及自身来源关系;来源消息使用同一套引用保护规则,只有没有其他引用时才删除,响应列出删除及受保护的消息 ID/原因。彻底清除还会清空能通过记忆 ID、已删除消息 ID、来源批次或整理任务关联到的模型原始/修正输出与正文快照;含正文的字段整段置空,不按文字搜索替换。结构化 ID、时间、类别、用量继续保留,purge_redactions 记录“因彻底清除而清空”、记录主键和可识别的数字引用;调用或学习在清除后才完成时,数据库触发器继续阻止已关联的原文写回。数据库保留无正文、无向量的 deleted 占位行,管理列表/详情不再展示,确保 SQLite 也不会复用最大 ID;其他记忆的依据引用继续指向这个已清除对象。按旧内容新建使用所选修订的正文/判断,生成新 ID,按 30+重要度×0.4 初始化强度,不继承置顶、遗忘时间或向量;沿用当前 about、tags 和现存来源(包括来源记忆修订号),因为旧修订并未完整快照这些关系,不伪造历史来源。

清空范围包括批次尝试的 raw_output/repair_output/error、批次结果中的弃项和规整记录、关联 model_calls.error_summary、召回请求中的查询文本、整理材料/结论/调用输出/建议和维护明细,以及相关 persona/入口画像的历史版本、候选和尝试的正文、材料、检查和输出。含正文的字段整段清空;其他记忆上的整理标注只清 text/evidence,保留类别和有效状态,不把它标成已撤销。来源消息仍受保护时,只通过结果列出被清除记忆的批次关联输出;消息实际删除时,才沿 history/target/future 三段关联全部批次。未被其他消息引用的媒体对象,其理解文本和错误字段也会清空;媒体文件按既有孤立文件清理规则处理。带有已识别正文键且能关联的旧管理记录,其 details_json 整段清空,数字引用保存在清空标记中。

当前 persona/入口画像按句子或条目删除:依据包含被清除记忆、其派生链,或实际删除的来源消息时,在同一事务内生成作者为 purge 的新版本并直接生效;其余句子和依据原样保留,只删不加、不调用模型。管理员手写、或管理员改过措辞后 basis 为空的句子也原样保留,无法仅凭正文判断关联,需管理员人工核对。剩余句子携带的初始背景副本也会清空,避免辅助元数据留下原文。全部删空时,新版本为空,needs_regeneration 标为 true,提示“需要重新生成”;persona 将此状态视为到期,自动重新生成的结果仍按当前发布设置处理(默认需人工确认)。清除新版本沿用旧 persona 的 self snapshot,被清除记忆仍计入变化;新版本生效时 persona 的 pending 候选变为 superseded,该入口画像的 candidate 变为 rejected,保留 reason=superseded 与 superseded_by。persona 不允许回滚到已标记清空或正文为空的版本。旧版本仍整段清空;若只有未引用的采样材料受影响,则新版本保留全部句子,删除句子数为 0。仅被清除记忆属于当前背景时才清空背景设置;退役的旧初始设定不影响当前背景,独立生成的初始设定版本不继承旧版本的清空标记。

单条清除在一个事务内完成;批量清除在只读快照中按关联行数预算选块(目标每块不超过 400 行,且最多 8 条记忆;修订历史、来源与人物摘要也计入)。写事务内重新计算当时的关联集合并使用同一实现,避免预估之后的新写入漏清,块间释放写锁。单条记忆自身超预算时仍保持原子清除;行数预算是短事务目标,不是对任意正文大小或并发新增行数的耗时硬保证。每块提交后更新已完成 ID 回执;冲突或中断的响应包含已提交 ID 和 backup_notice,重新预览剩余范围即可续跑;同一完成令牌重复提交不会再次清除。关联 ID 由写入触发器维护到带索引的 purge_record_refs,清除时按本次记忆、消息、批次和在途任务查找,不扫描所有历史 JSON。清除前的快照按修订号识别,清除后只引用占位 ID 的新 persona、依赖复核、标注和扣分明细保持有效;宿主继续传入 known_memory_ids 不会清空新召回查询。召回关联采用实际返回项或判断中的 ID。persona 整理调用仅沿 purpose LIKE 'persona_%' 关联,不扩大到同轮其他用途;persona 维护条目按 details 中的 attempt_id/version_id 关联,不把 object_id 的 run_id 回退值当尝试 ID。入口画像调用在写入时固定关联该轮正在运行的尝试,之后的完成更新保留这条关联,不连带同轮其他入口。

单条详情的 purge_preview 和批量预览返回将受影响的 persona/入口画像版本数、将删除的当前句子数、当前 persona/全部入口画像中保留的无依据句数 retained_unbased_sentences(也计入没有关联 ID 的当前版本,供人工核对),以及 backup_notice;清除结果返回 redaction_counts、redaction_reason 和新版本 ID。读取投影提供 redacted/redaction_reason,供界面区分被清空和模型未输出。确认框可使用 backup_notice.message:“定时备份、数据库升级备份(.bak)和已导出的文件里仍有副本;定时备份最多保留 N 份,数据库升级备份由维护保留最近 3 份。” N 读取当前 scheduled_backup.keep(默认 7);配置无效时返回 null 并说明无法确定。彻底清除不会改写这些备份和导出文件。

关联边界:旧数据已丢失来源关系、没有真实记忆 ID 的输出只能沿仍可识别的来源批次清理;学习材料里的临时 M 编号没有持久化真实 ID 对照,因此其他批次仅因检索或保留的历史消息而见过该记忆、但没有结果 ID 关联的输出不能可靠定位,在途学习同样受此限制。没有关联 ID 的历史/外部写入错误摘要、未返回任何相关记忆的查询、宿主状态报告以及独立对象里另行写入的相同文字,也不能按原文猜测关联。仍受其他引用保护的消息、其他独立记忆和目标正文保持原有语义。本功能是当前数据库字段的逻辑清除,不承诺擦除 SQLite 空闲页、WAL、磁盘快照或服务商保留的副本。

升级时一次性建立关联索引;仅存在已清除记忆时回填清空记录,并保留清除后产生的正常占位引用。以前已失去关联的历史副本受上述限制;历史入口画像调用仅在时间区间唯一匹配尝试时建立关联,没有可辨识区间或区间重叠的旧调用无法可靠归到某个入口。029 同时回填 persona 维护条目与候选淘汰,并增加 memory_revisions(memory_id) 索引;该迁移尚未发布,因此合并到 029 而不另加迁移。tests/test_purge_review.py 的结构测试检查全部 purge_* 触发器/视图和关键索引查询计划;以后重建相关表的迁移必须保留这些对象并通过该测试。曾在开发中应用旧编号清除迁移的测试库须重建,不能将旧测试库当成正式升级路径。

人物详情页和「入口与学习」的入口卡片提供 批量遗忘/删除。人物范围按说话人或 about 涉及人匹配;入口范围仅按直接来源消息所在入口匹配。默认排除置顶,先预览有效/遗忘/已删除数量、置顶数和最多 5 条截断示例,再确认操作;彻底清除另需勾选不可撤销说明并再次确认。遗忘/删除跳过已删除对象,彻底清除包含已删除对象;已彻底清除的占位对象不参加操作。以上沿用单条语义与来源保护规则,派生记忆继续由已有依赖事件/待复核规则处理。

批量接口只在管理 API 提供,同样检查管理员会话、CSRF、Host、Origin 与 JSON Content-Type:

  • POST /admin/api/memories/bulk/preview:subject_id 或 entry_id 二选一,include_pinned 默认 false,action 为 forget/delete/purge。返回 counts、total、applicable_count、pinned_count(已选中)、excluded_pinned_count、examples、snapshot_token 与 expires_at。
  • POST /admin/api/memories/bulk/apply:snapshot_token、与预览相同的 action,彻底清除额外要求 confirm: true。返回 status、count、memory_ids、skipped_count 及来源消息删除/保护明细。

预览令牌绑定 ID、修订、生命周期、置顶、保留强度、人物/来源关系及生命周期设置,保存在当前进程内,15 分钟过期,最多保留 32 份;服务重启、过期或被后续预览淘汰时需重新预览。执行开始前核对整个选择,之后每批最多 50 条,在短事务内逐条核对修订,最终再核对范围。发生变化返回 409 bulk_preview_stale 和 result 中的已完成数量/ID:第一批提交前冲突不更改记忆,批次间冲突保留已提交部分,要求重新预览。进程重启导致令牌丢失时 result 为 null,应先查操作记录。执行中的重复请求不重复写入;完成后在令牌有效期内重试返回同一结果。每次批量执行只写一条 memory_bulk_<action> 操作记录,保存范围、操作、数量和 ID 列表,并随每批提交更新进度;不保存正文或预览令牌。异常退出时已提交批次的记录仍在,状态可能停留在 running,需核对记录后重新预览。此功能不需要新迁移,不增加模型调用。

以下接口沿用管理员会话、CSRF 与 Host 校验;记忆写请求都带 expected_revision:

/admin/api 下的方法与路径 参数/用途
PATCH /memories/{id}/lifecycle pinned、importance、retention 至少一项
POST /memories/{id}/forget、/restore 手动遗忘/恢复
POST /memories/{id}/purge confirm=true;不可撤销
POST /memories/{id}/recreate source_revision;返回 201 与新对象
GET /memories/upcoming-deletion 即将删除及已到期未处理的非置顶对象;默认窗口 14 天,关闭自动删除时为空
POST /maintenance JSON {},202 返回 run_id;已有未完成运行时复用它
GET /maintenance、/maintenance/{run_id} 分页运行列表/完整逐项报告
GET /operations time_from、time_to、action、actor、object_type、object_id;limit/offset 分页
PATCH /settings/lifecycle 部分更新生命周期设置;GET /settings 的 lifecycle 返回完整值

列表默认每页 30 条、最多 100;操作记录日期按实例时区解释,结束日期含整天。管理员写操作、宿主每次立即学习/反馈、每个批次结果和每次维护摘要进入操作记录;宿主接收消息不逐条记。记录只含 ID、计数、分数、设置和结果类别,不含消息正文、凭据或 Cookie。

生命周期设置:forget_threshold=20、restore_threshold=35(1≤F<H≤100);feedback_increment=8、confirmation_increment=5、decay_amount=1、dependency_penalty=10(幅度 0—100);auto_delete_enabled=true、auto_delete_days=180、upcoming_delete_days=14、message_retention_days=30(天数 1—36500);maintenance_time="03:00"(严格 HH:MM)、abandoned_retry_enabled=true。学习再次确认已接入 memory_ops.confirm_retention,同一批次每条记忆只增加一次,默认 +5;遗忘记忆达到 H 时恢复有效。

M3 梦境整理

一次每日维护依次执行:确定性衰减/状态转换、到期删除、依据失效扣减、消息清理、放弃批次重试;然后执行记忆模型整理、persona 定期更新、目标依据复核。手动触发与启动补跑走同一流程。每项用短事务提交,模型调用在事务外;记忆、persona 及目标写回前核对修订/版本和依据,冲突留到下一次。整理不改当前状态,不阻塞接收、学习、回复准备或目标操作。

合并沿用已选的 broad:确定性找候选并排除不兼容的说话人、立场、涉及人、时间、数字、否定,模型确认同一事实后才合并;置顶和最近正文修订为人工编辑的记忆不参与。合并判断逐条引用 A/B 各自独有的细节,片段必须是对应正文规整空白后的子串;缺失或无效时按不确定处理,不合并、不重试改写。只有一边有独有细节时强制保留该条,即使它更短或更晚;两边都没有独有细节时按原规则选已有的一条原文。正文原样保留,不生成第三种说法,并校验正文及来源;结果为有效状态、继承全部来源,相信程度不超过输入的最大值。被并入方用 deleted + merged_into 保留永久占位,原 ID 不复活、不召回、不接受反馈。同轮矛盾对不合并。

双方互有对方没有的细节时不自动合并,给管理员留下“合并待核对”建议,列出双方 ID、独有细节及来源摘录。底层沿用现有建议类别,管理页按 suggestion_type=merge_details 单独显示“合并待核对(双方互有细节,不代表事实矛盾)”;整理报告和记忆详情均列双方独有细节与记忆链接,可核对、确认或清除。采纳只确认建议,不执行合并;手动合并专用入口留待界面后续实现。建议不进入回复准备或查询返回,正文与相信程度保持不变。

依据 DECISIONS 2026-10-10,矛盾和依赖复核只生成模型建议及可撤销标注(争议、依据不足、被某条记忆替代),不改正文、相信程度或删除对象,不重复 M2 的保留强度扣减。材料附双方来源摘录,每条最多取最早一条与最近三条,各最多 300 token。建议只在管理员整理报告和记忆详情中显示,附来源供核对;可采纳为“已确认”或清除,重复结论不重加,之后正文修订显示“标注后已修改”。未确认和已确认的建议都不进入回复准备或查询返回。置顶及人工编辑的记忆也可收到建议。

persona 在记忆整理后调用既有到期判断:自上一版至少五条自我记忆新增/变化,或满七天且有变化;“待更新”的依据计入变化。只在生成候选、逐句检查和版本/依据校验全部通过后发布,变化大时默认待管理员确认,仍遵循 persona 的 small_medium_auto / all_auto / all_manual 设置。未到期、缺模型、用途暂停、每日 token 限额或预算不足都有报告状态,原版保持可用。目标复核随后逐目标检查内部依据的当前状态,添加/结束依据标注,保留目标正文和状态;过期沿用目标模块按截止时间的只读标识,不自动完成或放弃。

入口画像、记忆(合并/矛盾/依赖)、人物摘要和 persona 分别使用自己的每轮调用上限,互不占用;HTTP 重试和 JSON 修正均逐次计入所属步骤。每日 token 限额、用途暂停和停止信号优先于各步上限。某一步耗尽额度只停止该步,后续步骤仍可运行,但仍须通过共同的停止条件。记忆任务按重要度降序、变化时间降序、ID 升序处理,未完成留到下次。persona 和人物摘要开始生成前须在自己的额度内留出生成、检查各一次;生成重试/修正仍须为检查保留一次,预算中途不足或检查失败不会发布部分候选。目标复核不调用模型,预算耗尽或模型暂停仍能执行。无 gateway 的离线调用跳过模型阶段,确定性维护和目标复核照常。中断后已完成项目不重做;已完成 persona 尝试可补回报告,不重新生成,未完成尝试标为中断后留待下一次整理。

管理员接口沿用会话、CSRF 和 Host 校验,前端接入点:

接口/字段 含义
GET /admin/api/settings 的 consolidation 返回以下设置字段
PATCH /admin/api/settings/consolidation 部分更新,未提供字段保持原值;写 consolidation_settings_saved 操作记录
maintenance_time 默认 03:00,严格 HH:MM;与 lifecycle.maintenance_time 共用存储,两条设置接口保持同步
max_calls 记忆步骤每轮上限,默认 300,整数 0—1000;0 只停止记忆步骤的模型调用
entry_profile_max_calls 入口画像每轮上限,默认 60,整数 0—1000
person_summary_max_calls 人物摘要每轮上限,默认 40,整数 0—1000
persona_max_calls persona 每轮上限,默认 30,整数 0—1000
merge_enabled / conflict_enabled / dependency_enabled 合并/矛盾建议/模型依赖复核开关,默认全部 true;不关闭 M2 确定性维护
persona_enabled / goal_review_enabled 定期 persona/目标依据复核开关,默认全部 true
person_summary_enabled 人物摘要整理开关,默认 true;只影响后续整理运行,不关闭已发布摘要的宿主读取
POST /admin/api/maintenance {},202 返回 run_id;已有未完成运行时返回同一个 ID
GET /admin/api/maintenance、/maintenance/{run_id} 分页列表及完整报告

一次整理在接受请求时固定设置、时区与对象上界;四项调用上限均写入 consolidation_runs.settings_json,改时间、预算或开关只影响之后接受的运行,续跑使用原快照,并按持久调用账本统计各步已用次数(包括中断时已准入的调用),不重置、不重复扣计。旧设置/快照缺少新字段时按上述默认值解释,旧快照原文不改。persona 的发布方式仍从既有 /settings/persona 修改,并在该次 persona 尝试被接受时固定。

新数据库的记忆上限为 300;升级时,仅当已保存的 max_calls 为整数 50,且操作记录中从未出现 consolidation_settings_saved,迁移才将其改为 300,其余字段不变。管理员保存过整理设置(包括只改开关、显式保存 50)则保留原值。首次设置不写整理设置,数据库导入/备份恢复保留设置及操作记录;直接 SQL 或内部 Store.set_setting 写入且未留管理员记录的 50 无法与历史默认值区分,也会升级。

入口画像默认 60:公开 EF-15 六个入口在同一时间点的合计,14 轮共 62 次(含一次 JSON 修正),单轮 P95=9;上线实例已有 12 个群入口,入口数还会增加,原先按小样本取两倍得到的 18 可能让部分入口每天轮不到。规划者据此将上限设为 60,给更多入口留出额度。

persona 默认 30:现有维护集成测试 的单轮 P95=3,不能代表分句依据校验的调用量。规划者汇总的尚未合并的 #61 在公开 persona_v1/v2 上分别为 133 次/23 个生成候选、265 次/30 个生成候选,平均约 5.8/8.8 次(含检查与复查);原上限 6 会使该实现合并后的 persona 再次因预算被跳过,故预留到 30。

人物摘要默认 40 保持不变:依据 EF-02 两次公开集完整运行的 16、16 次及 PS1 已有十人定期步骤测试的 20 次,线性 P95=19.6,两倍向上取整为 40。上述数值是每轮调用上限,不代表常规用量;HTTP 重试和 JSON 修正均计入所属步骤。公开小样本不能视为上线流量的 P95。管理员可按负载调整;0 停止对应步骤的模型调用,其他步骤及目标复核照常。

每轮规划仍至多扫描 128 个新种子。待处理 pair(pending / classified / decided)超过内部上限 2000 时,按既有重要度降序、变化时间降序、ID 升序保留前 2000 项,其余记为终态 backlog_trimmed,不生成 consolidation_receipts,不改变记忆。相同指纹不反复入队;任一侧记忆以后发生语义变化、扫描指纹改变时可重新规划。依赖任务不参与修剪。维护报告 items 中同名 outcome/reason 的 details.count 记录该轮修剪数量,details.limit 为 2000。

步骤准入会写入 items 的 outcome=model_step_budget:details.step 为 entry_profile / memory / person_summary / persona,details.max_calls 为该步快照上限,details.calls_used 为持久账本已用数;停止时 reason 记录 call_budget / usage_limit / paused / interrupted 等原因。未到期、关闭或没有任务的步骤可无预算项。旧报告的 summary.model_calls 仍为四步合计,consolidation.skip_reason 仍表示记忆整理阶段原因;判断其他步骤请读取各自条目。

报告的 items 列实际变化、失败和步骤预算:合并与继承来源、带摘录的矛盾/依赖模型建议、persona 发布/待确认/拒绝、目标依据变更及失败原因。summary.skipped.reasons 汇总跳过,summary.model_calls 汇总调用数、已报告输入/输出 token、耗时及未知用量调用数;consolidation 给出本次设置快照、延期项数及停止原因。persona 给出 status(published / pending / rejected / not_due / skipped / failed / conflict,运行中为 running)、reason、due、尝试与版本 ID,候选结果附正文、检查结果和变化程度;goal_review 给出是否开启、检查数或关闭原因。未到期和无变化成功检查不逐条留日志;来源报告中的建议不等于已确认事实。

每日维护还分批清理运行数据,每批最多处理 128 行并单独提交:model_calls 保留 90 天;recalls 及其 recall_items、host_recalls 保留 30 天;batch_attempts 和 consolidation_calls 保留记录,只清除超过 90 天的原始/修正正文字段。数据目录中的迁移前 iris.db.<时间>.bak 保留最近 3 份。未满保留期及边界时刻的数据不清理。支持中断后续跑;有资格自动重试的批次目标消息受清理保护。停机超过 24 小时后的补跑若在当天预定时间后开始,占用当天维护时段,避免同日再次定时衰减。

数据库启动时拒绝比当前代码更新或不兼容的迁移历史,须升级代码或恢复迁移前 .bak。写连接启用检查点刷盘 checkpoint_fullfsync,日志尺寸目标为 64 MiB;不启用每次提交的 fullfsync。

入口画像

当前形式:主题化的画像记忆(2026-10-14 起,#112、#114、#113)

  • 每条画像就是一条记忆:kind 为「入口画像」,子类为常聊话题、群内说法(可附含义)或明确群规/禁忌;about 为该入口的入口主体(subjects.kind=entry)。原话作为 sources,不写进正文。和其他记忆一样有修订、确认、生命周期、人工保护和彻底清除。画像记忆只在所属入口可见;普通召回、学习检索、人物要点、人物摘要、persona 和目标材料默认都排除它。
  • 每日增量观察:每个入口在角色时区的每日维护时刻处理已结束日期的新消息,也可以“立即运行”处理当日剩余消息。每次请求只读游标之后的新消息(有上限,过多时分块),加上已有画像主题的简短目录。模型只输出观察:确认已有主题、提出新候选(话题、说法、群规)、建议修正或撤下,并标出依据消息;鼓励把同一类的多次讨论归纳为稳定主题(提示词 entry_profile_observe_v3)。
  • 代码累计证据,决定是否展示:依据作为 sources 累积,独立性按确定性规则计算(转引、复述、接龙、刷屏、同一件事的跨日后续都不算新讨论)。达到门槛才送检查:话题至少两次独立讨论且跨两个本地日期;说法至少两次独立使用(保守地合并自然变体);群规需要一次面向全群的明确公告(个人请求、一次性安排、外部规定都不算)。代码只硬拦能确定判断的情形:具体实体(专名、作品名、地点、型号、数字、日期)必须出现在依据中;明显的个人请求、一次性安排、外部规定不能成为群规;回复或引用链、同一发起人对同一对象的跟进合为同一讨论。其余语义判断交给逐条检查。
  • 逐条检查:候选刚达到门槛、或需要修正时,只带这一条和它自己的依据检查一次(提示词 entry_profile_admit_v3):核对依据、性别代词、引文隐私、含义依据、局部做法不能写成群体习惯、不写容易过时的描述。检查只能删减或缩短依据,不改写正文;失败只影响这一条。
  • 稳定性靠反复出现:候选 importance 30、准入后提为 50(仍走普通衰减);话题和说法准入时,保留强度补足到 importance 50 的初始值并重置衰减(#117;更高的保留强度不降低,同一已准入正文的复查和重放不重复补足);不再被确认的主题按生命周期淡出,被确认会增加保留强度。同一讨论只加一次。
  • 预算与恢复:观察和检查各自 180 秒(含 HTTP 重试和一次 JSON 修正),在事务外调用;观察、检查分别持久保存,重启可续跑,不重复加分。使用独立的 entry_profile_max_calls(默认 60,含 HTTP 重试和 JSON 修正),与其他步骤共用每日 token 限额。
  • 宿主:prepare.entry_profile 的 {text, version, generated_at, possibly_stale} 不变,兼容新增 memory_ids(与 text 中展示的条目一一对应)。version 是每个入口的单调整数,只在可展示的画像集合变化时加一,读取不改变它。依据失效的条目直接不返回。
  • 管理:入口画像面板按条显示画像记忆(子类、正文、依据原话、作者、准入状态、独立天数/次数、保留强度、最近确认时间、修订历史)和宿主投影预览。逐条编辑、删除、恢复、人工新建走通用记忆接口(修订号校验)。旧的整版编辑、回滚、确认、拒绝接口返回 410,旧版本保留为只读历史。全自动,没有“待确认候选”。
  • 迁移(032、033):管理员手写的群规逐条转为人工创建的画像记忆(无法分类的显示为“管理员说明”);旧的模型条目不直接导入,从原消息按新方式重建。
  • 评测:evals/entry_profile_eval/memory_runner.py 按消息时间逐日重放(daily_plus_final_flush),按 scoring_v3.md 判分。门槛:模型条目有依据 ≥90%、禁止内容 0;覆盖作诊断。

以下为 2026-10-14 之前的文档式 v2 说明,保留供查阅旧版本历史。

旧形式:文档式 v2(只读历史)

入口画像 v2 为 group/live 单个入口的“常聊话题+群内说法”中文短列表,可选加入明确说出的群规/禁忌;private 不生成。每个模型条目附 1—3 条原话,常聊话题至少两条。去掉氛围、说话方式和习惯类概括;个人信息与隐私、个人评价、性别推断、账号 ID、其他入口内容及给模型或宿主的指令仍禁止,引用原话也不豁免。上一版只帮助比较变化,不能作为事实依据。提示词为 entry_profile_generate_v2、entry_profile_check_v2;v1 文件和历史版本保留可读,下一次符合条件的整理生成 v2。示例(不向宿主输出消息编号):

常聊话题:试印纸张与渗墨(“换薄纸试印了”;“这次纸背又渗墨了”)。
群内说法:翻面——反看纸背面检查渗墨(“翻面就是看看纸背有没有渗墨”)。

包括保留的管理员手写条目在内,新生成版本最多 6 条、500 字、500 个估算 token,没有最低字数;估算使用现有 estimate_tokens。画像分区独立封顶,不挤占 prepare 相关记忆的 1500 token 预算。label 最多 40 字,含义最多 80 字,单条原话最多 100 字;超限拒绝该条或整版,不截断引语、删掉手写内容或凑足字数。说法含义仅可由本条原话或明确绑定的记忆支持;检查发现仅含义无依据时删含义,保留有依据的说法与引语。

entry_profile.py 确定性选择本入口最近 14 天的实际发言(message/self_output,不含行动结果、事件、未来消息),按角色时区分天,各天平分 6000 token 预算;每一天依次取最早、最晚和区间中点,摘录最多约 300 token,连同元数据计入预算。有效、在本入口可见且有本入口直接来源的记忆按重要度降序、ID 升序选取,合计 2000 token;每条最多附最早一条及最近三条本入口原话。材料使用 S/M 编号,入库换为消息 ID 与指纹,或记忆 ID、修订号、来源指纹。

梦境整理的入口画像步骤使用独立的 entry_profile_max_calls(默认 60,含 HTTP 重试和 JSON 修正),与其他步骤共用每日 token 限额。首版要求窗口内至少 50 条消息;更新要求自上一版以来至少 50 条新消息,或自生成时间经过至少 168 小时且有新消息。依据被清理、修改、删除、遗忘或可见范围变化时只读计算 possibly_stale 和 stale_basis,下一次整理重新生成;全部生成路径每入口每个角色本地日期最多一次。手动重新生成可跳过更新数量/七天条件,首版门槛、每日上限、开关和用途暂停仍有效。停用不删除历史。

生成和逐条检查各共享 120 秒总预算(含调用内重试),在数据库事务外执行,沿用原用途和整理调用上限。代码将空白连续段规整为一个空格,除此之外不改大小写、标点或字词;引语必须是材料中本入口消息正文或其 quote.content 的连续子串,也可来自入选记忆附带的本入口原始消息。材料摘录不包含人工添加的截断提示。只引用记忆编号而无消息原话不能通过。明确的账号号段、账号标签、邮箱、平台提及语法以及已知不透明账号 ID 作确定性过滤,其余禁止内容由检查调用判断。

常聊话题还要求至少两条不同消息、跨两个角色本地日期;完全相同的正文及其截短副本不增加独立原话数;转引原话可作解释依据,但缺少原始发生日期,不计入话题重复出现的两条正文。跨日只是保守下限,同一活动跨日延续不等于反复讨论,仍由检查调用核对。检查输入逐条只附绑定消息与记忆,不能借未引用前文。任一原话核对失败则删去整条;代码和模型累计删去超过原始模型条目的 50% 时整版拒绝,恰好 50% 可以通过,管理员手写条目不计入分母。删后重新核对长度,不补写;没有内容也拒绝。原条目、引用统计、删条原因和删含义原因都保留。管理员手写内容由代码原样保留,有禁止内容的警告仍待人工确认;手写内容删改视为大变化并待确认。

根据 2026-10-11 的上线决定,入口画像默认开启,默认 check_auto:检查通过即发布,包括大变化;质量继续迭代,管理员可按入口改为 all_manual(全部人工确认)或关闭。检查失败保留当前已发布版。新的候选或管理员编辑使旧候选成为 rejected,检查结果记 superseded。生成写回的短事务复核当前版本、设置修订、消息指纹、记忆修订和可见性;冲突不覆盖。失败和中断保存尝试记录;恢复不会重跑已完成或中断的模型调用,缺失的整理报告项可从记录补回。

不新增迁移,结构化条目复用 sentences_json:保留 text/basis/author,增加 kind/label/meaning/quotes/format_version,检查和材料也标 format_version=2。因此现有管理页仍能显示逐条正文、原话和依据;旧版不重写。三张表分别保存 entry_profile_settings(入口开关、发布方式、设置修订及当前版本指针)、entry_profile_versions(入口内递增版本、父版本、正文、逐句依据、完整检查、变化程度、生成/发布时间、作者、来源和材料快照)、entry_profile_attempts(整理运行、日期、状态、原始输出和调用记录)。版本状态只有 candidate / published / rejected,作者为 model / admin;旧的已发布版仍是 published,is_current 区分当前版。回滚只针对曾经发布的版本,复制为新的版本,不改写历史;过时依据仍会标出。依据 ID 不用外键保留原始消息,清理不会被画像阻塞。

入口与学习页的每个入口都有“入口画像”面板(群聊/直播可用,私聊显示不适用)。面板支持当前正文、生成时间、待确认计数、历史版本、逐句依据与原话、检查和删句记录、任意两版对比,以及编辑、回滚、重新生成、确认/拒绝和画像设置。新增或改写的管理员句子标记为手写并受保护;未改条目整体保留原依据,引语中的标点不会将它拆成新的手写句。v2 按“常聊话题/群内说法/明确群规”分项展示名称、可选含义和引语,注明正文/转引归属、原作者与消息编号;详情展示引用核对总数、逐条结果、模型检查、删条和删含义记录。结构化编辑不设最低字数,最多 6 条、运行设置中的 max_chars(默认 500)字及 500 个估算 token,分类、引语、标点和换行计入预算。可增删条目或改名称、含义、引语;引语必须来自本入口现存消息的连续原话,作者由服务端核对。未改条目保留原依据与手写标记;改动条目标记为管理员手写,清空模型依据,新版写入沿用版本冲突检查和操作记录。v1 标注“旧版摘要”,仍按原 150—500 字规则编辑。编辑发生 409 冲突时保留草稿,核对最新版本后再处理。窄屏使用单列对比。

prepare.entry_profile 只返回本入口启用且已发布的 {text, version, generated_at, possibly_stale},否则为 null;search 不返回。令牌范围外的入口不允许 prepare;新增分区不改变 memories、近期消息、状态或目标,不在回复准备时生成。宿主使用约定见 接口 v1。

数据层函数:

函数 行为
current_profile(store, entry_id) 当前已发布版、possibly_stale、stale_basis;没有时为 null
list_versions(store, entry_id, limit=50, offset=0) / get_version(store, entry_id, version) 分页版本列表/完整详情(含逐句 basis、author、checks)
version_diff(store, entry_id, before, after) 按句比较 insert/delete/replace/equal,附前后句及依据
profile_settings / set_settings(..., expected_version, enabled, publish_mode) 读/写单入口设置;未给出的字段保持原值
admin_edit(..., content, expected_version) 长度检查后直接发布;未变句保留依据,新增/改写句标 admin
rollback(..., version, expected_version) 复制已发布版并发布新版本
confirm_candidate / reject_candidate 接收候选 version 和 expected_version;确认前复核依据
EntryProfileEngine.update / .regenerate 参数 entry_id、expected_version;返回 status、reason、version、attempt_id、due
profile_due / select_material 可注入 clock 的到期判断/材料快照
profile_context(conn, entry_id) 回复准备的只读 {text, version, generated_at, possibly_stale};关闭或无画像时 null

所有写函数要求 expected_version 为当前已发布版本号,无版本为 0;冲突抛 EntryProfileConflict,在途竞争可抛 EntryProfileBusy。所有操作写 admin_operations,不在操作记录里重复存正文。兼容字段 sentences 在 v2 表示条目;每个模型检查结果的 index 对应 checks.checked_sentences,被删句见 checks.deleted_sentences,实际模型检查输入见 checks.model_input。运行设置 runtime_settings.entry_profile 存默认值;整理快照开关为 consolidation.entry_profile_enabled。

管理接口统一位于 /admin/api/entries/{entry_id}/profile,沿用管理员会话、CSRF、JSON Content-Type 和操作记录;版本号只在本入口有效,路径里的 version 不用全局行 ID。

方法和相对路径 请求/返回
GET / 当前和待确认版本摘要、pending_count、入口设置、possibly_stale、stale_basis、latest_attempt;无当前版为 null
GET /versions?limit=30&offset=0 {items,total,limit,offset},倒序历史;is_current 区分当前与历史已发布版
GET /versions/{version} 正文、逐句 basis/admin_written、依据摘录和当前记忆状态、检查及删句记录;不返回整份聊天材料和原始模型输出
GET /diff?before_version=N&after_version=M 任意两版的逐句差异,附前后正文和依据
PUT / {expected_version,content},150—500 字;直接发布管理员编辑
PATCH /settings {expected_version,enabled?,publish_mode?};至少一个设置字段。开关立即生效,发布方式只影响之后的候选
POST /versions/{version}/confirm、/reject、/rollback {expected_version};reject 可附 reason。确认前再次核对依据,回滚新建当前版本
POST /regenerate {expected_version};202,{accepted:true,attempt} 和任务 Location
GET /attempts/{attempt_id} 持久任务状态、阶段 generating/checking/finished、结果版本与安全原因码;不能跨入口查询

所有写请求中的 expected_version 指当前已发布版本号,无当前版为 0。版本、候选、设置或依据冲突返回 409;参数错误 400,不存在 404。重新生成在短事务中预约后立即返回,模型在线程池执行,不占请求线程、不持数据库事务。服务同时最多接收一个管理员生成任务,重复提交返回 409;与整理通过同入口的持久预约和每日上限互斥。任务失败、暂停、每日上限或消息不足会留下可查询的结果,accepted 不等于已发布。页面轮询不会重新提交;服务正常关闭等待在途任务,重启将无主任务标为 failed/interrupted,不会暗中重跑。

评测运行器只读显式 --corpus,每案例用隔离数据库和 as_of 时钟,直接载入消息与记忆,不经过学习。输入 JSON 为 format_version: 1 和 cases 数组;每案例含 id、entry: {id, kind, name?}、带时区的 as_of、messages: [{id, at, text, sender?, kind?, entry_id?}],可选 timezone、other_entries、memories: [{content, sources: [消息id], importance?, stance?, lifecycle?}]、initial_profile(管理员手写)、must_cover、forbidden。也直接支持已冻结的 groups 格式(entry_kind、now、quote):每个目标入口的独立数据库都先装入所有入口,消息 ID 加入口前缀、引用保留原作者,共享账号不合并入口;只取目标入口材料。标注不进入生成或检查请求。运行器不搜索语料、不复用其他运行,全部材料放仓库外:

uv run python -m evals.entry_profile_eval run --corpus <已冻结的显式文件> --out <外部运行目录>
# 机制试跑可加 --fake-responses <JSON响应数组>,不读取模型配置、不访问模型
uv run python -m evals.entry_profile_eval score --materials <材料目录> --judgments <第一轮> --judgments <第二轮> --judge-model <执行者模型名> --out <外部报告目录>

导出 PR #7 约定的 format_version、manifest.json、round-template.json、scoring.md、cases/*.json、run.json,指纹绑定源码、v2 提示词、全部输入、模型非敏感配置、适用性清单及输出;新的评分说明为 evals/entry_profile_eval/scoring_v2.md,v1 评分说明保留。判分轮把 round-template 复制为 manifest,按清单文件名保存逐条结果(JSON 键沿用 sentence_results);严格核对数组长度、索引、布尔值及指纹。双判支持取 AND,违规取并集,要点取 AND,禁止项取 OR,列全部分歧。已发布/待确认版本计算有依据条目率 ≥90%、禁止内容 0,要点覆盖率仅诊断;被拒版本单列,手写句不占模型依据比例但仍检查禁止内容。引用核对通过率单列为全部生成引语中的连续原话匹配数/引语数,含被删条目;无引语时为 null,不代表语义支持率。另报拒绝比例、删条比例、超时及调用用量。

冻结 entry_profile_v1.json 及标注不改。coverage_v2.json 按文件 SHA-256 与要点原文精确绑定,提议将 40 项中的 9 项列为“新形式不适用”,诊断覆盖率按其余 31 项计算;逐条原索引、原文及理由在材料与报告中保留,状态为待规划者确认,详见 EP2 报告。其他显式语料默认全部适用;可通过 --coverage-policy <显式 JSON> 使用规划者确认的清单,指纹或标注不符即失败,标注不进入模型请求。

EP2 本回合只做假模型机制验证,未运行真实模型或质量双判;不能宣称公开 dev 或隐藏门槛通过。真实模型迭代等规划者完成服务商对照后再运行,隐藏验收由规划者另行判定。v1 三轮历史结果仍见 评测汇总。

人物摘要

人物摘要从关于某人的全局 active 记忆生成最多 5 条、合计不超过 200 字/250 token 的中文列表:与我的关系(至多一条)、稳定事实、偏好与习惯、近期重要事项。近期事项附有依据的时间范围;每条绑定 1—3 条记忆。不读取原始消息正文。示例:稳定事实:正在学习木工。。关系需要多日互动支持,单条偏好只能写具体场景;不推断性别,不输出敏感/保密事项、无依据的个人评价、他人的事情或模型/宿主指令。

材料按重要度、保留强度、新近程度和 ID 排序,预算 3000 token。除 Visibility.memory(id) is None 外,还要求全部直接来源和派生祖先的来源入口声明为 shared;entry_only/entries 即使恰好覆盖当前所有入口仍排除。读取宿主投影时再次检查声明及请求入口的当前可见性,受限依据或被 CF 清空的整版不返回,管理只读投影标可能过时。其他依据修订/删除/遗忘、主体变化、近期事项到期也标可能过时;不可用或到期条目先移除,剩余为空则不返回。

默认开启、检查通过即发布,可按人物关闭或改全部人工确认。代码先核对引用,再由检查调用逐条判断依据、泛化、违规和时间范围,删去超过一半的模型条目即整版作废,保留上一版。模型调用均在事务外,写回核对版本、主体/设置修订、依据修订/生命周期和可见性;管理员手写内容保留,未改条目继续携带原依据。历史版本可读,编辑/回滚会新建版本;操作写入 admin_operations。

整理接入要求至少 8 条相关全局 active 记忆:首版,或至少 5 条相关记忆新增/变化,或满 14 天且有变化;已过时版本可提前满足重生成条件,处理顺序仍按最近活跃排序。依据已删除/遗忘/彻底清除或近期事项到期的条目在返回前移除,剩余为空不返回。近期事项未给明确截止日时,默认从依据的最新事件时间(无则创建时间)起 30 天到期。被拒或确定性失败(无效输出、删条过多、长度超限等)后,自动整理等相关记忆出现新变化才再尝试;超时、限流、服务端错误等模型故障和 interrupted 可在后续整理重试,仍受模型健康与预算限制。管理员重生成绕过这一退避和日常更新阈值;普通确认不算内容变化。默认每次至多 10 人,最近活跃者优先,使用独立的 person_summary_max_calls(默认 40)和共同的每日 token 上限。主体合并后,被并入方版本作废,存续方下次重生成。手动生成也受初始数量、开关和模型预算约束。

PS2 接入 prepare 的 people 分区、管理接口和整理流程;界面由 UX 后续实现。people_limit 为人数上限,默认 3、最大 3、0 关闭;按显式参与者顺序优先,否则最近发言顺序。participant_names 按主体 ID 或显式 participants 中的名字/别名提供本次显示名,主体 ID 键优先,仅影响 people.name。每人正文 250 token,整个数组独立限 1000 个估算 token:给 3 人的正文预留 750,剩余约 250 用于通常长度的名字、主体 ID、时间和字段;长显示名时按实际序列化结果整人跳过,不截断限定语。详情见 宿主 v1 兼容新增说明。新增分区在召回写事务结束后使用独立只读快照,先筛有当前已发布版的参与者,再复核依据;已有分区、排序、学习材料和召回判断输入保持不变。一次人物分区读取共用按需构建的名字表;名字形式参与者复用召回解析的主体 ID,再按当前公开别名来源复核。人物分区内部异常时返回 people: [],hints 附固定 people_unavailable 提示“人物摘要暂不可用。”;宿主运行记录复用该请求的单条 prepare 事件,使用固定 result=error 分类,不写异常正文或额外事件。试用回复会把 prepare 的 people 与 entry_profile 一起放入模型输入;冻结的 trial_reply_v1.md 未改。

单条和批量彻底清除已在同一写事务调用 purge_memory_references(conn, memory_ids):它整段清空关联的所有摘要版本、材料和模型输出,保留结构字段并标“因彻底清除而清空”;人物摘要与记忆一同提交或回滚,清除数量包含在结果中。

人物摘要管理路由前缀为 /admin/api/people/{subject_id}/summary,沿用管理员会话、CSRF、JSON Content-Type 和 Host 校验:

方法/相对路径 用途
GET / 当前版、候选、开关、编辑上限、过时原因、最新生成任务
GET /versions?limit=30&offset=0、GET /versions/{version} 分页历史、逐条依据与检查详情;不返回完整选材或模型原始输出
GET /diff?before_version=1&after_version=2 任意两版的条目对比
PUT / {expected_version,content};允许短正文,最多 5 条/200 字/250 token
PATCH /settings {expected_version,enabled?,publish_mode?},发布方式 check_auto/all_manual
POST /versions/{version}/confirm、/reject、/rollback 都带 expected_version;拒绝可附 reason;回滚新建版本
POST /regenerate {expected_version},202 返回 accepted、attempt 和 Location;事务内预约,事务外生成
GET /attempts/{id} 持久任务回执,含 state、stage、原因、结果版本及时间;可刷新轮询

所有写操作核对当前已发布版本号 expected_version(尚无发布版为 0)并记操作记录;版本、依据或设置冲突返回 409,已有在途管理生成任务返回 409。服务关闭先等待后台任务完成,再关闭网关和数据库;重启将中断任务记为失败。手写或未改条目保留规则沿用 PS1,清空版不可回滚复活。

整理在记忆合并、矛盾处理和依赖复核全部结束后调用 update_summaries_for_run,使用独立的 person_summary_max_calls,保留其他步骤的相对顺序;关闭上述记忆步骤时仍可运行人物摘要。人物发现每轮共用一次名字表,并预计算每个主体独有的 label。每运行的设置快照含 person_summary_enabled;主体上限沿用 runtime_settings.person_summary.max_subjects_per_run(默认 10,范围 1—100)。生成/检查、JSON 修正及网关内部重试计入人物摘要的独立上限;每次生成准入预留一个检查名额,共享 chat 每日 token 上限。单个人物错误写入记录后继续其他人物。管理员正在重生成时,为本轮该人物记录一次 skipped/generation_in_progress,继续其他人物并计入人数上限;不会让维护停在整理阶段或反复写运行记录。步骤异常用固定错误码写入报告,再继续其他维护步骤。维护报告 items 中 outcome=person_summary,details 含 subject_id、status、version、attempt_id;步骤异常不含主体,status=failed。不重复调用已完成尝试,停止信号可中断后续工作。

给 UX:人物页增加当前摘要、分类/时间范围、逐条记忆依据、检查/删条记录、过时与清空原因、候选确认/拒绝、历史/对比、编辑/回滚、重生成任务轮询和每人开关/发布方式。编辑不设 150 字下限,按接口 edit_limits 提示上限;409 要刷新版本再提交,清空旧版禁用回滚。设置页增加 person_summary_enabled;整理报告按 outcome=person_summary 显示每人结果并链接到对应版本。generation_in_progress 显示“管理员生成中,本轮已跳过”;模型暂时失败允许重试,确定性拒绝等待新依据,但管理员仍可主动重新生成。interrupted 显示为可重试,不要求先改记忆;重生成仍受最少 8 条、开关、版本冲突及模型预算限制。前端本 PR 不修改。

新提示词为 person_summary_generate_v1/person_summary_check_v1。运行器只加载显式 dev 文件,提供隔离库、外部判分材料、双判取不利和独立可见性泄漏检查;GD 格式及命令见 人物摘要评测说明,第一阶段接口约定和验证见 PS1 报告。本轮仅用假模型,真实质量评测待规划者通知。

PS2 验证(2026-10-12,基线 main 0754b16)

新增行为先补失败测试再实现。定向检查 218 项通过;Python 3.13.15 的 uv run pytest 全量 2727 passed(633.15 秒),随后隔离 Python 3.12.14 的 uv run --locked --isolated --python 3.12 pytest 全量 2727 passed(487.32 秒);各有 4 条现有依赖警告。uv build 成功生成 wheel/sdist,wheel 中本次修改的模块与测试过的源码一致。

全部公开学习语料 v1—v5 的 116 案例/272 批假模型请求逐字一致(请求摘要两边同为 9988666d3634cdde81ded0d1a540cf92cbda5d30b65bf8ae5a85301274861092)。七份公开召回语料共 228 查询,全文/混合/带判断三种模式共 684 份返回零差异,216 个判断输入一致;比较仅排除新增 people、随机 recall_id 和耗时字段。入口画像五种流程的提示词、调用参数、版本正文、依据和检查结果一致,原入口画像与宿主契约测试未修改且全部通过。新增契约测试覆盖 people、范围外入口、受限依据、关闭参数、显示名、合并主体、独立预算,以及管理会话/CSRF/版本冲突和整理预算/错误隔离。

R10 用同机固定合成库按 ABABABABAB 交替,各 5 轮;A 为 main,B 为本分支。每组预热 5 次、采样 60 次;直接 prepare 与带令牌 ASGI HTTP 逐次串行计时,保留本地判断和鉴权成本,预计算查询向量并使用即时假判断,不含外部模型网络。两边关闭后台调度器,未提高令牌限流;请求节奏等待不计时。每库 100 个人物主体、100 入口(50 个 entry_only),全部记忆附来源 roots,向量为默认 2048 维 float32;同库有 3 份已发布摘要,每人 5 条、每条 3 个依据引用,共 45 个引用。B 的每次直接/HTTP 返回均核对确有 3 人摘要。下面是五轮 P95 的中位数,单位 ms:

规模/查询 直接 main 直接 PS2 增量 HTTP main HTTP PS2 增量
5 千/不点名 27.84 29.51 +1.67 32.55 34.92 +2.37
5 千/点名 39.84 41.48 +1.64 44.19 50.42 +6.23
5 万/不点名 126.49 129.50 +3.01 137.82 137.42 -0.39
5 万/点名 149.00 144.89 -4.11 156.42 157.85 +1.43

四组直接调用和 HTTP 均满足增量 ≤ max(10 ms, main 的 5%);本次各组门槛均为 10 ms。HTTP 最大增量 6.23 ms,直接调用最大增量 3.01 ms;负差值只视为测量波动。首轮 B 的 5 万不点名 HTTP P95 为 205.43 ms,原样保留,没有因波动删样本或重跑挑选。

设备 Apple M4(10 核/16 GiB),macOS 26.6.2,Python 3.13.15、NumPy 2.5.3、SQLite 3.53.1。测试、构建和其他对照均已结束后才启动 R10;进程/分组及每 10 次采样前检测其他 Python/构建进程的 CPU 占用,高负载时在样本外等待。本轮因同机其他任务共执行 162 次 10 秒等待(分组内 144 次、轮次间 18 次)。各分组前后系统 load average 的 1/5/15 分钟范围分别为 2.17—3.69/2.53—3.39/2.75—3.46,中位数为 2.61/2.77/2.94;分组末端进程 RSS 为 109.66—628.59 MiB,不是峰值。桌面背景负载的波动保留在墙钟结果中,未做 CPU 压测。

全部输入库、原始请求/返回、计时与负载、源码指纹、测试日志及外部脚本保存在 /Users/cassia/Local/Code/iris-eval-artifacts/PS2-20261012/。主计时脚本为 r10_ab.py,结果为 r10/summary.json 和每轮 result.json;学习、召回、入口画像对照分别在 learning-all/、recall-comparison/、entry-comparison/。没有读取人物摘要 dev 或隐藏集,本轮不作真实模型质量结论。

PS2 审查修复复验(2026-10-12,R10 基线 main 4167a54;最终兼容性基线 main 25e3688)

管理员重生成期间,整理为该人保存一次 skipped / generation_in_progress 回执,并继续其他人物;该回执计入 N,重复执行已完成维护不会新增运行事件。自动整理仅对被拒和确定性失败启用“等待新依据”,模型暂时故障及 interrupted 可重试,管理员重生成绕过退避。人物发现每轮共用名字表并预计算独有 label;prepare 的多个摘要共用按需构建的名字表,名字参数复用已解析 ID,但仍按当前公开别名来源复核。人物摘要在记忆整理判断与写回完成后使用剩余预算。

人物分区内部异常时,返回空 people 和固定提示,宿主 HTTP 中间件在该请求原有的单条运行记录中写 result=error,不新增重复事件或保存异常内容。本轮 24 项新增用例覆盖 Busy 并发/回放、两阶段暂时错误、管理员重试、名字表及解析复用、受限别名变更、部分条目删除/遗忘/默认 30 天到期、名字作为 participant_names 键,整理判断和应用后的预算顺序,以及名字匹配的最长标签与词边界等价性;运行记录测试另核对每请求只有一条事件。

R10 完成后正常 merge 了 main 的 #98;该提交只更新目标去重默认门槛及对应文档/测试,本次人物摘要与 prepare 修改的源码 diff 摘要未变。最终源码验证:Python 3.13.15 全量 2805 passed(472.02 秒),随后隔离 Python 3.12.14 全量 2805 passed(473.03 秒);均为 4 条现有依赖警告。uv build 成功。宿主契约与入口画像原有测试全部通过;入口画像源码/提示词/测试对 main 零改动。最终假模型对照:116 个案例、272 批学习请求逐字一致;七份召回语料 228 查询、三种模式共 684 份返回零差异,216 个判断输入一致。学习请求摘要仍为 9988666d3634cdde81ded0d1a540cf92cbda5d30b65bf8ae5a85301274861092。

补充 R10 首次完整对照的 HTTP 增量为 +10.117 ms/+10.880 ms,略超 10 ms 门槛(直接调用增量 +9.511 ms/+8.117 ms);该轮全部原始结果保留。随后将人物摘要专用名字匹配按中文首字分组,保持组内最长标签优先及原词边界;独有 label 改为按需计算,宿主路径不分配整理专用的表。以下是相同固定输入库上的优化后完整复测。

补充 R10 使用 5000 条合成记忆、5000 个人物主体(另有 self/scene)、100 个入口。所有记忆 speaker=self,memory_subjects 为空;3 份已发布摘要共 45 个依据引用,只通过正文提及名字关联到人物。沿用上表的同机 ABABABABAB 各 5 轮、每组预热 5 次/采样 60 次、串行直接 prepare 和带令牌 ASGI HTTP 方法;点名组另以名字传入三位 participants。两边使用同一固定库、即时假判断及预计算向量,候选每次均断言返回 3 人。以下为五轮 P95 的中位数,单位 ms:

场景 直接 main 直接修复版 增量 HTTP main HTTP 修复版 增量
不点名/近期参与者 62.90 70.87 +7.96 68.32 76.42 +8.10
点名/以名字传参与者 70.25 78.76 +8.51 74.27 82.56 +8.29

四项增量均小于 10 ms,满足 ≤max(10 ms, main P95×5%) 的门槛。计时与本会话的测试、构建及请求对照分开进行,不做 CPU 压测。设备为 Apple M4/10 核/16 GiB,macOS 26.6.2、Python 3.13.15、SQLite 3.53.1;1/5/15 分钟系统负载范围为 1.61—2.87/2.03—2.56/2.25—2.50,中位数为 1.95/2.19/2.35,组末进程 RSS 为 136.38—252.38 MiB。检测到其他 Python/构建进程高 CPU 时在样本外等待;工作进程共记录 78 次等待(每次 10 秒,主循环额外等待 0 次),均不计入请求耗时。每套源码均保留全部五轮及异常波动,没有丢弃离群轮次;首次未达标后,在优化源码的基础上完整重测。

完整源码指纹、日志、固定输入库、原始请求/返回、计时与负载在 /Users/cassia/Local/Code/iris-eval-artifacts/PS2-review-20261012/:最终对照在 post-optimization-compat/;首次完整 R10 在 r10/,优化后的脚本、结果与各轮明细在 r10-optimized/。两次输入库 SHA-256 一致,凭据在 r10-optimized/input-sha256.json。合并 main、收紧单条运行记录和名字匹配优化前的中断/历史测试日志单独保留,不计入上述最终通过结果。#97 只随 main 合并,本次未另改评测运行器;未读取人物摘要 dev/隐藏集,未调用真实模型。

模型故障与状态

学习首次请求、调用内重试和 JSON 修正共享 180 秒总预算;其他生成请求为 120 秒,embedding 为 30 秒,召回查询 embedding 仍限 2 秒且不重试,评测判分为 240 秒。召回判断另有包含排队的 10 秒总预算、仅一次尝试。其他调用内最多再试两次;有有效 Retry-After 时按秒数或 HTTP 日期等待,否则分别以 2、4 秒为基数,加上 0 到基数之间的均匀随机抖动。等待和后续请求不得超过同一个总预算。批次重试间隔为 1/5/15 分钟,共四次尝试。

对话和 embedding 分别维护状态:normal、temporarily_unavailable、invalid_key、configuration_error、account_problem;每日上限造成学习暂停时,对话用途显示 usage_limit。连续三次可重试的网络/超时/限流/5xx 请求错误会暂停该用途(包含调用内重试的失败),触发暂停的批次失败不扣尝试次数。探测使用固定短请求,间隔为 1、2、4、8、10 分钟,之后保持 10 分钟;成功即恢复。分类优先识别结构化 error.code,其次兼容已知 error.type,最后按 HTTP 状态兜底。401 是密钥无效,404 及 InvalidParameter/MissingParameter 是配置错误,只在配置实际改变后恢复;403 权限、欠费、订阅及 QuotaExceeded/SetLimitExceeded 属账户问题,不作调用内短间隔重试,保留定时探测和内部立即重试。AccountRateLimitExceeded、ServerOverloaded、RequestBurstTooFast、500、传输错误、超时及审核服务故障 ContentSecurityDetectionError 可重试。方舟 SensitiveContentDetected 家族、输入/输出文本审核和风控命中,以及 finish_reason=content_filter,直接进入内容拒绝终态,不修正 JSON、不写记忆;原 MiniMax 敏感字段与 base_resp 识别保留。码表来源见方舟官方文档。

服务会重新读取当前模型配置来源,修改模型配置(含推理档位)后对新工作生效。管理设置接口调用 Gateway.replace_config(kind, ModelConfig(...))、Gateway.retry_now(kind),凭据只保存到 secrets.json;外部文件模式保持只读。ModelHealth.set_daily_token_limit(整数或 None) 设置每日 token 上限,默认不限;按角色 timezone 的次日零点或调高上限恢复。额度根据服务商已报告用量计算,在途请求可能越过上限,未报告 token 的调用不能计量。接收和召回始终照常。

embedding 暂停时网关直接返回可降级错误,回复准备和查询使用全文,并附 model_paused 提示。缺少向量的记忆照常进入全文索引,恢复后后台按修订号补算。embedding 维度参与配置变化检测、恢复探测和旧向量补算;改变模型或维度后仍须重新标定召回。GET /api/v1/status 提供 model_health、各入口 current_batch/latest_batch、memory_gap_count、今日/本周 usage、learning_latency_24h(P50/P95/最大耗时/超时数)和 scheduler.running;兼容保留最近调用 models 和积压 backlog。

迁移 006 为 model_calls 增加 reasoning_effort、reasoning_present、reasoning_chars;历史记录保持 NULL。推理字符数按 Unicode 字符计,已返回但非文本的推理字段记为未知;正文解析只用 message.content,不存 reasoning_content/reasoning 全文。reasoning_tokens 取 usage.completion_tokens_details.reasoning_tokens,缺失记 NULL,不从字符数估算;completion 用量已包含推理,不能再次相加。状态额外提供当前 chat_reasoning_effort 及逐次学习诊断。用量合计只覆盖已报告值,不能把未知调用当成零消耗。

兼容 MiniMax 等服务商时,正文开头(允许前导空白)的一个完整 <think>…</think> 块会在用途校验和 JSON 解析前剥离;正文中间、第二个块或未闭合块不剥离。剥离内容只累计到推理出现标志和字符数,不保存全文,也不进入 JSON 修正请求。batch_attempts 等原始输出保存剥离后的正文;关联的 model_calls.leading_think_stripped 标明是否剥离(迁移 027,历史为 NULL)。学习及召回评测报告保留这些诊断,源码指纹区分适配前后的结果;GLM 独立推理字段的处理保持原样。

运行日志同时输出控制台和数据库所在目录的 logs/iris.log,每 2 MB 滚动,保留 3 份旧文件;只记录状态、标识和错误类别,默认不记录消息正文或 API key。

宿主 prepare/search 的来源条目按令牌范围投影:范围外的 entry_id、entry_name、occurred_at 为 null,保留来源存在与消息 ID;管理员来源详情不变,不改变召回正文或顺序。

运行记录

上线后的只读检查使用迁移 024_runtime_journal.sql 的两张元数据表。服务每次启动分配随机 instance_id;正常退出前等待已接受的后台工作结束,留下最终积压快照和停止记录。启动后每 300 秒由独立线程记录心跳,调度器卡住也不会阻止该线程采样。没有正常停止记录的退出,可结合心跳中断、下一次启动和进程监管日志推断。心跳表示进程记录线程活跃,不等于外部 HTTP 探测成功;启动记录对应服务生命周期开始,不是端口已经绑定的证明。

runtime_events 是追加表,无正文或自由格式 JSON:

字段 含义
id 单调递增事件 ID;同一时刻按 ID 排序
instance_id 32 位随机十六进制 ID;离线操作为 NULL,不能当作在线运行证据
occurred_at UTC、带偏移的 ISO 8601 记录时刻
event 下表的固定事件名
entry_id prepare 的入口 ID;鉴权/路由前被拒绝时可能为 NULL
model_kind, configured 健康用途及是否已有非空端点和模型(0/1);不记录端点、模型名称或配置摘要
previous_state, state, reason 固定状态与固定原因,不保存异常原文
run_id, phase, scheduled_at 维护运行 ID、阶段及预定 UTC 时间;phase 为 maintenance/consolidation/persona
duration_ms, result 单次执行的总耗时(单调时钟、毫秒)及 prepare 分类
plan_id, notification_id 被取消/跳过的提醒计划 ID,以及替代它的通知 ID(如有)

除 id、occurred_at、event 外,列只在对应事件有意义时填写,其余为 NULL。

event 取值和解释
process_started/process_stopped 生命周期开始/正常停止;不补造历史启动、崩溃或停止时间
heartbeat 启动、每 5 分钟和正常停止前各一次;与其所有入口快照同一短写事务提交
model_state model_kind 为 chat、embedding、recall_judge、goal_dedup_judge、image_understanding;整理、persona 等沿用它们实际使用的 chat 健康状态。状态为 normal、configuration_error、invalid_key、account_problem、temporarily_unavailable、rate_limited、usage_limit。可选用途 configured=0 表示尚未配置,不应当成等待自动恢复的故障。
maintenance_scheduled 提前记录下一个每日时刻;运行繁忙时仍观察计划。按角色时区计算后存 UTC。重启可能重复观察同一时刻,按 scheduled_at 合并计划,不把它们当多次执行。
maintenance_started/maintenance_completed 一次维护调用或整理/persona 阶段的开始与完成;run_id 对应 maintenance_runs.id。重启续跑会产生新的开始,耗时按各次调用单列;persona 阶段完成表示计算结束,是否仍待人工确认看既有 maintenance_persona.status。
maintenance_skipped/maintenance_failed 跳过/中断或失败及固定原因。shutdown/interrupted 表示可继续的工作,本次结束不代表整日完成;schedule_changed 只撤销原先尚未到时的计划。
reminder_resolved 只补计划 cancelled/skipped 的变化时间。plan_id 关联 goal_reminder_plans.id;合并过期阶段时 notification_id 指向实际替代通知。
prepare 通过 Host 与令牌范围检查后,宿主 POST prepare 的服务端处理总时间,含排队、正文校验、召回、判断、提示和 JSON 序列化;不含先行鉴权、网络传输和记录写入本身。分类 normal/judge_degraded/fulltext_degraded/error。错误含鉴权通过后的正文校验和未捕获异常,不保存错误详情。Host/令牌/入口范围/令牌限流拒绝及未就绪请求不写 prepare 记录。全文降级与判断降级同时发生时,分类优先 fulltext_degraded。

健康原因:startup(本实例初始观察)、network(网络/超时/可重试服务端故障)、rate_limit、invalid_key、configuration、account、daily_limit、daily_reset、limit_changed、manual_retry、configuration_change、probe_success、response_received、cooldown_elapsed。冷却到期和每日额度是计算状态,记录的是下一次观察到转换的时刻(调度轮询或调用时),不是虚构的精确服务商恢复时刻。配置变化即使仍为 normal 也留记录;跨实例使用 startup 作为初始状态,不将它误配为一次自动恢复。

维护原因白名单:schedule_changed、shutdown、interrupted、internal_error、disabled、unconfigured、model_items_disabled、call_budget、usage_limit、paused、not_due、no_changes、already_running、revision_conflict、invalid_output、other。提醒原因:goal_changed、goal_closed、configuration_change、superseded、overdue_disabled、already_reminded。无法映射的已有维护诊断只记 other。

runtime_backlog 用 (heartbeat_id, entry_id) 作主键,外键 heartbeat_id → runtime_events.id;包括空入口,删除心跳时级联清理:

字段 含义
pending_messages messages.learning_state IN ('pending','batched') 的未学消息总数
waiting_batches, running_batches 对应 batches.state 的批次数
oldest_wait_seconds 最老未学消息的 received_at 到采样时的秒数(非 occurred_at);没有未学消息时为 NULL,无法解析接收时间时亦可能为 NULL
memory_gaps 此时 memory_gaps 中该入口的缺口数

积压在一个只读快照中按索引汇总,采样时间写入心跳 occurred_at;随后单独提交事件与快照,不持有业务写锁扫描消息,不读取正文。记录使用现有单写连接,锁等待和 SQLite busy 等待各最多 50 ms;记录失败只写固定日志 runtime journal write failed,清理失败为 runtime journal cleanup failed,继续业务。新记录保留 30 天,每日维护完成时每批最多删除 1000 条过期事件及其积压快照;停服、维护中断或清理失败会延迟清理。既有业务表的保留策略不变。

提醒已有证据不重复记:goal_reminder_plans.created_at/scheduled_at 是计划建立/预定时间;已发布计划的通知可按 goal_id + scheduled_at 与 notifications 对应,目标合并另按现有目标合并关系追溯。notifications.published_at/taken_at/cancelled_at 是发布、首次取走及取消时间;取走不代表平台送达。新表只补过去无法确定时间的计划取消/跳过,早期没有记录的终态仍是未知。判断降级的独立明细继续读取 recalls.request_json.judgment,不以其判断耗时冒充 prepare 总耗时。

事件流另在 logs/iris.log 记录固定格式 host_events instance=<实例 ID> reason=<分类> connections=<当前连接数> reminders_pushed=<累计帧数>:连接建立/结束及活跃时每 60 秒一次。分类为 connected、disconnected、periodic、shutdown、revoked、idle_timeout、max_duration、storage_unavailable。只含随机实例 ID、固定分类和计数,不记录提醒正文、宿主名称或令牌,也不增加数据库迁移。日志沿用既有轮转保留策略;当前值由 /api/v1/events/status 提供。累计数含重放且只表示已交给 ASGI 响应链,不能替代 notifications.taken_at 或连接器的平台发送记录。

连接器 tools/live_review/ 可据此补充判定(需连接器线适配本 schema):

  • 服务连续性:按实例读取开始、心跳、停止,检查窗口覆盖与心跳缺口,并结合监管/宿主成功记录。缺失、记录失败或超过保留期不能计为正常。
  • 入口卡住/积压清空:逐入口比较 5 分钟快照、最老等待时间以及既有 admin_operations.batch_result/batch_attempts 的成功进度;持续一小时无进展需标出。最终清空须有窗口末附近的零积压快照或该时刻一致读证据,不能拿当前状态代替历史结束时刻。
  • 自动恢复:同一实例、同一模型用途配对暂停到 normal,区分 probe_success/cooldown_elapsed/daily_reset 与 manual_retry/configuration_change/limit_changed;再核对恢复后的积压。未配对、跨实例、缺日志不判通过。
  • 每日整理:将事先记录的每日时刻与 maintenance_started 和完成事件、既有冻结 maintenance_runs 关联;300 秒延迟、缺开始/完成、阶段跳过和失败各自列出。手动/catchup 不能替代缺失的 scheduled 运行,缺未来计划也不能凭无错误宣称未漏跑。
  • 提醒:结合计划、补充取消/跳过事件和既有通知时间,检查 300 秒发布/取走延迟及到期未发布;及时取消与合并通知单列,不能把被取走当作送达。

评测

uv run iris eval learning --split dev --judge-runs 1
uv run iris eval learning --judge-runs 2
uv run iris eval learning --judge-mode external --out <运行目录>
uv run iris eval e2e --judge-mode external --out <运行目录>
uv run iris eval e2e --judge-mode external --script E001 --script E011 --out <流程检查目录>
uv run iris eval e2e-score --materials <材料目录> --judgments <第一轮目录> --judgments <第二轮目录> --judge-model <执行者模型名称> --out <报告目录>
uv run iris eval persona --corpus evals/persona_v1.json --judge-mode external --out <运行目录>
uv run iris eval persona-score --materials <材料目录> --judgments <第一轮目录> --judgments <第二轮目录> --judge-model <执行者模型名称> --out <报告目录>
uv run iris eval recall
uv run iris eval recall --split dev --calibrate
# 如需重新比较 1024/2048 维,再加 --compare-embeddings
uv run iris eval recall --corpus <外部 JSON 或 JSONL> --out <外部目录>
uv run python evals/benchmark_retrieval.py --default-config

默认公开学习集为 learning_v1—v5,共 116 段;v5 的 12 段手写 dev 在 905f955 冻结,覆盖活动协调、游戏过程与关键结果、跨批改述、亲友近况和转达通知。报告按 v1—v5 分组,同时列出 v1+v3+v4 和 v1+v3+v4+v5 合计、近似重复与再次确认。语料格式、完整外部明细、标定方法和报告见 evals/README.md。学习和召回的最终验收仍需规划者运行隐藏集。

图片学习评测使用 iris eval learning --corpus evals/learning_image_v1.jsonl --judge-mode external --out <仓库外目录>。消息可带 media: [{"file":"learning_image_v1/im001_ticket.png","content_type":"image/png"}],文件相对 evals/media/;只接受该目录内的普通图片文件,拒绝绝对路径、上级路径和符号链接。评测按原顺序上传附件并引用消息,原图字节哈希、chat 的 vision 和 native_image_learning 一同进入检查点指纹。外部判分材料附带原图并校验哈希,两个独立判分会话须根据原图与完整对话评分,不能以生成的描述替代原图。图片集不支持内置纯文本模型判分。原图路径在学习完成后补齐描述,批次耗时不含这一步;描述路径在学习前生成描述,耗时包含等待。命令行默认使用关闭原图学习的描述路径。Python 入口 run_learning_eval(..., concurrency=1, native_image_learning=True) 可显式启用原图对照(仍要求 chat 的 vision=true);省略开关默认为 false,运行记录和检查点指纹保留该值。并发默认仍为 4。

当前回复检索默认 trigram+2048 维 float32,向量下限 0.35、相对比例 0.75、向量/全文权重 1:1,查询加“为这个问题检索能回答它的个人记忆:”前缀;全文覆盖率 0.75,长片段最大文档频率 2。无向量时使用独立标定的 jieba 全文降级,覆盖率为 0,长片段文档频率通路关闭。新数据库写入这些默认值,已有设置保留。学习材料保持独立的 PR #4 设置。

trigram 查询中的一字/两字实词会补查已有 jieba 索引;只询问已知姓名时,也能按涉及人、说话人及正文提及取回候选。长短词结果按全查询词项覆盖率筛选,稀有长词片段也可按整库文档频率入选,之后合并去重,人物与类型等过滤继续生效,混合检索和全文降级均覆盖短词。

召回评测默认运行七份公开集(含 recall_no_answer_v1),共 243 条固定记忆、228 条查询。default_judged 使用正式 prepare 判断路径;原全文/混合对照关闭判断。报告单列判断降级次数和占比;有降级的运行不能作为正常路径结果。GLM 阶段全部按 dev 使用,保留原 split 作历史分组;第四轮新增 16 条手写短词查询,先单独冻结;第五轮继续按原规则在 122 条 dev 上重新标定。选参、逐项对照和已知问题见 evals/README.md。本次对话查询默认值由规划者批准为守则例外:conversation_v2 relevant 精确率下降 0.0682,conversation_v3 无答案误返由 1/4 增至 3/4,见 第二轮报告。召回判断按 PR #25 已批准方案固定,不重新选参。

点名检查仅读取检索和人物要点候选的正文。性能脚本加 --default-config 覆盖当前默认的 5 千/5 万条、点名/不点名四组;不带该参数还会比较维度和 dtype,并在合成测试库中关闭向量截断。性能方法和本次报告入口见 evals/README.md。学习影响按 2026-10-06 决定改为全部公开学习语料的确定性请求比较;第一阶段真实学习对照已作废。

学习与端到端评测默认仍调用对话模型判分,只作预览;正式评测使用外部执行者判分。端到端外部模式导出每检查点一份材料,不调用判分模型。材料目录含 manifest.json、scoring.md、cases/*.json、run.json 和 round-template.json;每轮把模板复制为 manifest.json,按清单写 facts/forbidden 判分。e2e-score 离线严格校验后生成原格式 JSON 报告,一次/两次 --judgments 分别为单判/双判,事实覆盖取 AND、禁止说法取 OR,U04 单列。完整文件约定及独立判分要求见 evals/README.md。小样本单判只用于流程检查,不作门槛结论。

端到端公开集现为 11 个手写脚本,包括群聊学习、重启后私聊提问的 E011。prepare 的查询文本只省略或使用提问原文;评测器另行校验近期原始消息没有跨入口。跨入口来源格式见 评测说明。

评测启动子进程时,临时 TOML 只记录端点、模型和 api_key_env 变量名,密钥仅通过该子进程环境传递;不会修改调用者环境。评测的配置来源仍为 test-models.toml/IRIS_TEST_MODELS,serve 的配置优先级见上文。需要时也可在模型配置组中用 api_key_env 引用已设置的环境变量,不能与 api_key 同时填写;变量缺失会明确报错。

GLM 服务商默认 max 推理可能耗尽 180 秒学习预算;依据 DECISIONS.md 2026-10-08「GLM 默认推理档位改为 high」,设置页的方舟预设及开发配置使用 high,未指定档位的外部配置仍由服务商决定。学习仍共享 180 秒预算。学习提示词默认 v7,先筛长期价值,再核对完整证据、人物归属、别名声明和时间一致性;补充转述视角、扮演双方主体、周一周界和跨批更新要求,v5、v6 保留。学习精确率问题列为已知问题,延后到上线后优化,M2 不再为它迭代提示词。learning_v8.md 已冻结、未启用,留作上线后的精度优化起点;冻结信息及本轮保留的确定性修复见收尾说明。参与者编号紧接完整已知姓名、带空格或括号时均解析回同一主体;编号与姓名不匹配则弃项,不按拼接后的名字新建主体。正文和标签中的括号显示名只展开一次;未使用的 null 关系字段可省略,实际关系的唯一性与证据要求不变。学习再次遇到同一命题时确认原记忆,包含遗忘中的记忆;每批仅增加一次保留强度(默认 +5),达到恢复阈值 H 后恢复有效,已删除对象不复活。数值或否定序列不同的命题不作近似重复合并。推理档位和方舟错误码适配已合入;M1 的评测门槛在定稿代码上以公开样本和规划者隐藏集的独立双判达到,M1 已完成(DECISIONS.md 2026-10-08);公开样本的单判及流程试用仍不能作为门槛结论。

M4 备份:在线导出与离线导入

服务运行时,管理员可通过 POST /admin/api/backups/export 下载备份,JSON 体默认为 {};显式传入 {"include_secrets":true} 才包含数据目录内的模型密钥。请求沿用管理员会话、JSON Content-Type 和 X-Iris-CSRF,宿主令牌不能代替管理员会话。响应为 ZIP 下载,Cache-Control: no-store;含密钥时文件名带 -with-secrets,X-Iris-Backup-Includes-Secrets 为 true,清单和导出记录也明确标记。GET /admin/api/backups?limit=30 返回最近导出记录(最多 100 条)、是否含密钥及离线导入说明。前端入口由 M4 界面线接入。

每日维护完成后自动执行一次定时备份,默认开启、写入 <数据目录>/backups/、保留最近 7 份且不含模型密钥。补跑、正常维护和手动维护共用按角色时区计算的每日名额;同日重复调用或重启不会重复导出。失败、空间不足也占用当天尝试,下一天维护后再试;关闭时不占名额。维护中断时不导出,完成状态提交后才开始在线快照,备份失败不撤销或阻止其他维护阶段。

通过 runtime_settings 的 scheduled_backup JSON 设置(缺省字段使用默认值;本条没有设置页开关):

{"enabled":true,"keep":7,"directory":null}

enabled 必须为布尔值,keep 必须为正整数;directory=null 使用默认目录,也可填绝对路径或相对于数据目录的路径,不能位于媒体目录中。设置无效会留下固定失败原因。定时备份复用手动在线导出的快照、媒体校验、0600 文件权限和不覆盖发布机制。文件名为 iris-scheduled-<实例标识>-<本地日期>-<备份ID>.zip,清单附 scheduled_backup 的 owner、day。仅在新备份成功后,清理当前配置目录中同时匹配本实例命名规则、备份 ID 和清单标记的旧定时备份;不跟随符号链接,不清理手动导出、其他实例文件、迁移前 .bak 或以前配置的其他目录。

导出前估算目标卷所需空间:数据库与媒体总量的两倍,加上至少 64 MiB(或总量 5%,取较大值)的余量,用于未压缩暂存、ZIP 和并发变化;不足时跳过并记录 insufficient_space,保留旧备份。实际导出仍可能因后续磁盘变化失败,按同一记录返回固定原因。

GET /admin/api/backups 的最近导出记录同时返回定时尝试;沿用 purpose 并新增明确的 trigger:manual(手动)、pre_import(导入前)、scheduled(定时),旧记录按原用途补齐。界面线可据此显示“定时”。新增元数据为 status(running / succeeded / failed / skipped)、reason、started_at、finished_at、duration_ms;原有 created_at、size、file_count 等保留。定时备份每次尝试一条记录,完成后更新结果;失败时大小为 0,成功时为实际 ZIP 字节数。中断后再次检查会标记 failed / interrupted,耗时未知为 null,不补造成功。定时失败原因只使用 invalid_settings、insufficient_space、invalid_output、output_exists、media_changed、media_integrity、unsupported_migration、manifest_too_large、export_failed、interrupted;不写异常原文、路径或密钥。若导出成功但旧备份清理失败,保留新文件,status 仍为 succeeded,另记 retention_reason=cleanup_failed;正常时该字段为 null。不增加运行记录事件。

停服后也可使用命令行;这些命令不加载测试模型配置、不调用模型:

uv run iris --data-dir /path/to/iris-data backup export --out /path/to/backup.zip
# 需要将本地模型密钥一起备份时,显式加 --include-secrets
uv run iris --data-dir /path/to/new-data backup import /path/to/backup.zip
# 覆盖已有实例:先停止服务及其他配置命令,再显式确认
uv run iris --data-dir /path/to/iris-data backup import /path/to/backup.zip --confirm-overwrite
# 覆盖前的私有自动备份默认保留旧本地模型密钥,结果返回归档位置

导出包格式版本为 1:manifest.json(UTF-8)列出备份 ID、创建时间、迁移版本及完整迁移序列、是否含模型密钥、是否包含媒体目录,以及各文件的字节数和 SHA-256;内容为 iris.db、已有的 media/ 文件,以及选择包含时的 secrets.json。不包含日志、旧备份、部署配置、外部 TOML 或环境变量中的密钥。媒体路径沿用 media/<sha256>,数据库快照同时保留媒体对象、理解文本和消息引用;导出与导入均校验登记的哈希、大小与文件一致。只要求数据库快照登记的媒体文件在复制期间存在且保持不变;忽略 .upload-* 临时文件和快照后新增的内容寻址文件。未标记清理的登记文件缺失、损坏或在导出期间改变时整次导出失败,可重试;不会发布缺损包。每日维护已明确清理的字节不进入 files,清单新增 purged_media 对象,以原 SHA-256 为键、带时区的清理时间为值,例如 {"原哈希":"2026-10-17T00:00:00+00:00"};没有清理记录时为 {}。导入严格核对它与数据库的清理标记,拒绝遗漏、伪造标记或同时携带已清理字节的包。旧备份可省略此字段,按未清理规则校验。清理意图尚未完成时暂不能备份,维护续跑完成后可重试。

数据库来自独立只读连接上的 SQLite 在线备份,固定同一已提交快照,复制数据库和媒体时不占用接收或学习使用的写锁;发布前短暂取得写锁,复核媒体登记和文件身份,阻止清理/重新上传与最终发布竞态。模型配置锁只覆盖快照建立和可选的密钥捕获。CLI 输出和服务端下载临时文件权限为 0600,浏览器保存后的权限由浏览器决定;同名输出文件不会被覆盖。导出和导入操作记录只存标识、版本、数量与密钥包含标志,不存文件内容或本机路径。备份仍包含原始消息、记忆、管理员密码摘要和宿主令牌摘要,应按实例数据私密保存;选择包含的模型密钥在 ZIP 内不加密。

默认无密钥的备份会清除副本中的模型密钥引用,保留端点、模型名等非敏感配置,原实例不变。导入这种备份也不会沿用目标目录原有的密钥文件;需要重新配置模型密钥,或继续使用原来的外部配置方式。宿主令牌摘要、撤销状态和入口范围原样恢复,令牌明文不会重新生成。

导入仅提供离线命令,选择该方式是为了在整体替换数据库、媒体和可选密钥时没有运行中的连接继续访问旧数据。程序持有服务锁和配置锁;先校验清单、每个文件的完整性、数据库完整性与外键、迁移序列,再在私有临时目录依次迁移旧版本。兼容方向为新版本可读取旧备份;旧版本拒绝含新迁移的备份,恢复前须先升级。新增媒体清理迁移也沿用这一规则,旧程序会先返回 unsupported_migration。较新或不兼容的版本、截断包、路径越界和符号链接条目会被拒绝。覆盖前自动生成 数据目录名.pre-import-<随机ID>.zip,放在数据目录旁并在命令结果中返回路径;私有自动备份权限为 0600,默认保留旧 secrets.json 中的全部本地模型密钥(包括不再被当前配置引用的旧项)。命令 JSON 的 previous_backup 返回归档路径,含旧密钥时 previous_secrets_archive 同时明确返回该位置。--backup-include-secrets 保持兼容;手动导出仍默认不含密钥。自动备份失败不会覆盖数据。

校验和迁移成功后,通过 macOS/Linux 的原子目录交换一次发布完整数据;服务锁在交换前后保持同一文件身份。失败或进程在交换点中断时,目标目录只会是完整旧实例或完整新实例。其他文件(如日志)保留;崩溃可能留下私有 .iris-import-* 临时目录,可在确认恢复结果后删除。此导入实现要求数据目录可在同一文件系统原子交换,不支持直接替换挂载点或 Windows;当前验证平台为 macOS。恢复后按原方式启动服务,未完成批次由既有重启恢复流程处理。

M4 媒体与图片理解(后端)

媒体保存在数据目录的 media/,按 SHA-256 共享文件;每次保存返回独立媒体对象,不覆盖同文件其他对象的宿主说明。单文件默认最多 10 MiB(10 × 1024 × 1024 字节),检查 MIME 和文件头。图片白名单为 PNG、JPEG、GIF、WebP(image/png、image/jpeg、image/gif、image/webp),不接受 SVG、HTML 或任意文件类型。音频支持 MP3、WAV、Ogg、FLAC,视频支持 MP4、WebM;仅保存附件,不调用音视频理解模型。无宿主说明时分别使用 [音频,未理解]、[视频,未理解]。文件头校验不等同于完整解码,损坏图片的模型失败仍退回占位。

在 test-models.toml 中由用户新增 [image_understanding],填写 base_url、model、api_key(或省略 api_key,填写 api_key_env 所指的环境变量名);可选 reasoning_effort,只在所选模型支持时设置。不需要 dimensions。独立配置优先;未单独配置且 chat 的 vision=true 时,沿用 chat 的端点、密钥、模型和显式推理档位,否则保持未理解占位。健康状态始终按图片理解用途独立。脱敏设置中的 models.image_understanding.inherited 及服务状态中的 model_health.image_understanding.inherited 标明是否沿用对话模型。请求使用 OpenAI 兼容 /chat/completions 的 image_url data URL,见方舟图片输入契约。真实视觉模型冒烟只应使用已配置且声明支持图片的模型。

本地模型配置、导入、热重载和脱敏设置投影自动包含 image_understanding。管理接口 PUT /admin/api/settings/models/image_understanding、同路径的 POST /test、POST /retry 沿用管理员会话和 CSRF;enabled=false 清除独立配置,此时 chat 支持图片则恢复继承,否则停用。测试连接使用固定 64×64 PNG、总预算 10 秒;探测图片满足方舟输入尺寸下限。设置页已提供独立的图片理解模型组。

仅当对话模型声明 vision=true 且运行设置 native_image_learning=true 时,学习直接读取本批目标消息的原图,按消息和附件顺序插入 image_url data URL,正文以 [图片 N] 对应。原图保持上传字节,不缩放、不转码,也不附加该图的宿主或系统描述。历史段、后续段仍使用已有描述或占位,节省图片 token;任一条件不满足时继续使用原有描述路径。不含图片的学习请求逐字不变,提示词仍为 v7。

运行设置 native_image_learning 默认 false,已有数据库缺少该字段也按 false 处理。PATCH /admin/api/settings/limits 接受严格布尔值,GET /admin/api/settings 返回该字段;省略时保留现值,修改沿用设置操作记录,下一批学习和后续后台调度读取新值,当前界面未增加控件。图片理解用途沿用对话模型的规则仍只取决于 vision,不受此开关影响。当前默认 v7 的图片对照未通过事实召回门槛,暂不默认启用,待 v9 上重新对照后由规划者判定。

每批原图上限默认 8 张;PATCH /admin/api/settings/limits 的 learning_image_limit 接受 0—32 的整数,0 表示全部回退,省略则保留现值。设置读取返回该字段,修改沿用管理员会话、CSRF 和设置操作记录,当前界面未增加控件。单张沿用 10 MiB 限制;超出批次数量、文件过大或不可用时改用已有描述或占位,已拒绝的图片不发送。批次 result_json.media_attempts 逐次记录消息 ID、图片 ID/哈希、附件位置及实际路径(原图、描述、占位或被材料预算排除),不保存图片字节或 base64。图片内容可只以所在消息作为证据;截图署名不成为发图人的自身声明,既有说话人和转述归属校验保持不变。

启用原图学习时不等待图片描述。仅在上述两个条件均满足时,服务调度器另设一个后台描述任务,最多一个在途任务、每轮最多处理 8 个对象,沿用图片用途健康状态、每日用量限制和媒体租约;重启后接续未完成且租约已过期的对象,停机等待正在调用的描述收尾。生成的描述供回复准备、检索和管理页使用。原图学习的 JSON 修正仍附同一批原图,避免修正时丢失证据;学习与修正共用原有的 180 秒总预算。调用及批次记录只保存正文与诊断,响应中完整回显的原图 data URL/base64 在保存前被移除。

未启用原图时,处理顺序仍是:保存媒体 → 消息引用 media_ids → 按原节奏冻结批次 → 领取批次为 running → 补充图片理解 → 读取学习快照、组成材料 → 学习。上传和接收消息不等待模型。整批图片(目标优先,再历史、后续)共享最多 120 秒,包含排队、网络和调用内重试;随后学习仍有独立的 180 秒预算。含媒体消息在冻结时预留原有的每消息 1500 token 上限。图片理解并发最多 2、排队最多 8,独立线程池和健康状态;不等待其他批次已在理解的同一对象,本次使用其已有占位。图片较慢时端到端延迟可能超过实时入口的一分钟目标。

描述路径优先使用宿主理解文本(UTF-8,最多 32 KB);其余图片在配置可用时逐个理解。一般网络、超时、429、5xx 和无效响应最多再试两次,间隔 2、8 秒,服务商的 Retry-After 优先,始终受整批预算限制。失败或未配置用 [图片,未理解] 继续学习;图片失败不触发学习批次重试。明确安全拒绝固定为“敏感信息无法访问”,标记为被拒绝,同文件保留期间不再自动理解;识别方舟图片安全错误码,不将安全检测服务故障误当内容拒绝。已结束的理解尝试不会因为历史段重复出现或批次重新学习而自动重做;未配置、用途已暂停或排队不足而未完成的对象,原图模式由后台继续处理,其他模式在以后组成材料时可再尝试。

描述与占位材料在对应消息正文后追加一行,媒体按消息引用顺序编号,例如:

媒体(数据)=[{"序号":1,"类型":"图片","来源":"宿主提供","被拒绝":false,"理解文本":"窗边有猫。"},{"序号":2,"类型":"图片","来源":"被拒绝","被拒绝":true,"理解文本":"敏感信息无法访问"}]

来源固定为“宿主提供/本系统理解/被拒绝/未理解”;文本使用 JSON 转义。描述路径的正文与媒体文本合并后沿用单条 1500 token 截断;原图路径的正文与回退描述同样共用这份文本预算,耗尽后使用占位,并在图片路径记录中注明描述被截断或省略。原图标记和图片部分保留,原始正文和理解文本完整保存;材料超过原总预算时沿用去除上下文的规则。无媒体消息材料逐字不变,学习检索仍使用原正文和原选材规则,召回算法与返回材料未增加媒体字段。

媒体原文件使用独立的 media_retention_days,默认 7,严格整数 1—36500。GET /admin/api/settings 在顶层返回它,PATCH /admin/api/settings/limits 可修改;省略字段保留原值,沿用管理员会话、CSRF 和设置操作记录。该字段不在 lifecycle 中,设置页由 UX 线跟进。消息仍按 message_retention_days(默认 30 天)保留。

仅每日维护按文件登记的创建时间清理超过保留期的字节,每批最多 128 个候选,沿用维护的截止 ID、游标和重启续跑。理解文本、来源、哈希、大小、类型、创建时间和消息附件关系均保留;文件不可用时沿用原有 404、描述或未理解占位。任一共享对象所在消息仍有记忆来源或其他消息保护引用(含等待/运行中的批次)时,文件受保护;有效理解租约和本进程在途理解调用也受保护,即使调用已超过租约时间。实际删除前在写事务内再次核对。清理意图先持久化,删除或提交中断后可续跑。

同一哈希重新上传会写回字节、清除清理标记,旧媒体对象也可重新引用;只有对象 ID、没有字节的引用会被拒绝,须先重新上传。重新上传保留原登记创建时间,不重置按原创建时间计算的到期日。未绑定消息的上传仍有 1 天引用窗口;最后一条消息引用消失后也有 1 天宽限,之后每日维护可删除无引用文件及媒体对象。操作记录不算引用;有理解租约或在途调用时暂不删除。崩溃遗留的临时/未登记文件仍在一天后清理。

宿主上传 POST /api/v1/media 和消息 media_ids 已接通,见宿主接入文档。保存函数约定见 ARCHITECTURE.md 的媒体一节。

管理员媒体接口(管理员会话;写请求同时带 X-Iris-CSRF,遵守现有 Origin 与 JSON Content-Type 检查):

接口 返回与用途
POST /admin/api/media 201,上传试用图片,JSON 为 {"content_type":"image/png","data_base64":"标准 Base64"};返回下面的媒体投影
GET /admin/api/media/{media_id}/file 200,原文件字节;管理员可读取所有入口及内部媒体,宿主 Bearer 令牌不能替代管理员会话
POST /admin/api/trial/entries/{entry_id}/messages 既有 201 回执;增加可选 media_ids,最多 100 个、不重复,按数组顺序引用图片
GET /admin/api/trial/entries/{entry_id} 每条 messages[] 增加 media[],轮询可读取最新理解结果
GET /admin/api/batches/{batch_id} 三段现存消息均增加 media[];已清理的消息沿用 missing 占位对象
GET /admin/api/memories/{memory_id} 来源消息和前后文均增加 media[];目标来源前后文沿用同一媒体投影
GET /admin/api/status 新增 usage.by_purpose.image_understanding.today/week,以及 timeouts_seconds.image_understanding=120

先上传每张图片,再把返回的 id 放进试用消息的 media_ids。content 可以为 "",但正文和图片不能同时为空;保留原 speaker_id、dedupe_key。新消息的媒体无效时整条消息回滚;重复去重键返回原消息,不覆盖原正文或图片。管理员上传只接受 PNG/JPEG/GIF/WebP,解码后最多 10 MiB,完整 JSON 最多 14 MiB(也限制实际接收的分块正文);超限 413,非法类型、文件头、Base64 或字段为 400。上传不接受文件路径、URL、客户端文件名或伪造理解文本;新对象以未理解状态进入原有学习流程。未发送的上传沿用一天清理宽限。独立管理员上传不会向宿主令牌授予媒体引用权限。

media[] 包含 id、kind、content_type、size_bytes、understanding_text、understanding_source、understanding_source_label、completed_at、file_url。来源代码为 host/system/refused/unprocessed,中文标签为“宿主提供/本系统理解/被拒绝/未理解”。completed_at 是宿主说明接收或本系统理解尝试完成的时间,未完成为 null;一般失败也可有完成时间,须结合来源代码区分成功。读取不触发理解、学习、召回或保留强度变化。无媒体消息返回空数组。管理投影展示所有入口媒体,不受宿主可见范围筛选;文件系统路径和处理租约不返回。

file_url 可用于同源图片预览;提供原图,由界面缩放。响应使用登记的图片/音频/视频 MIME,Cache-Control: no-store、X-Content-Type-Options: nosniff、Cross-Origin-Resource-Policy: same-origin,不公开缓存。读取只接受媒体对象 ID,从数据库解析内容哈希,固定目录和文件描述符,拒绝符号链接,复核大小、哈希与文件头;不存在、已清理或文件不可用均为 404,不回显磁盘路径,也不重建目录。

图片用量按角色时区统计今日和本周(周一开始),结束于查询时刻。每个分桶包含 calls、prompt_tokens、completion_tokens、reasoning_tokens、tokens(输入+输出)、calls_without_usage、failures、refusals、failure_rate、duration_ms(总耗时)、p50_ms、p95_ms、max_ms、timeouts。统计图片用途的实际调用记录,包含调用内重试、设置连接测试和恢复探测;旧记录按图片 purpose 兼容归类。失败包含全部非成功调用,拒绝是其中 content_rejection 的子集;无调用时延迟和失败率为 null,缺失 token 不作推算。图片调用已计入原有总用量,界面不要再次相加。

M4 入口的记忆可见范围(VS)

入口默认 shared(全部共享),可改为 entry_only(本入口)或 entries(指定入口列表,始终包含本入口)。管理员通过已有会话与 CSRF 调用:

PATCH /admin/api/entries/入口ID/visibility
Content-Type: application/json
X-Iris-CSRF: <当前会话的 CSRF 值>

{"visibility":"entries","visible_in":["另一个入口ID"]}

入口必须已经存在;名单完整替换,不能引用未知入口。改动写入管理操作记录。GET /admin/api/entries 和入口 /settings 返回 visibility、visible_in;记忆详情返回 visibility.shared 与计算后的 visibility.visible_in,空列表且 shared=false 表示仅管理员可见。

记忆范围是全部来源消息的入口范围与派生依据范围的交集;目标还与所属入口范围取交集。无来源目标按所属入口计算,无所属入口的目标全局可见。计算使用当前设置,收紧、放宽、撤换名单对旧对象和派生链立即生效,不必重新学习。原始消息仍只返回本次入口的近期窗口。

回复准备、人物要点、点名检索、深度召回、目标分区及其附带引用均按查询入口过滤。底层 Retrieval.search(entry_id=...) 支持指定入口;省略入口时仅返回全局可见的记忆和目标,不按宿主令牌范围放宽。HTTP POST /api/v1/memories/search 已接通该入口参数,令牌无权访问指定入口时返回 403 entry_forbidden。令牌范围另行限制目标分区;本节取代前文接入过渡说明中的待接线状态。不同实际范围的对象不做召回去重、学习再次确认、目标去重或整理合并;整理的矛盾与依赖材料也不跨范围。persona 的依据仅限全局可见的自我记忆,已发布句子的依据被改为私有时,该句立即停止进入宿主和学习材料,等待后续更新。

隐私 dev 检查与回归命令(完整输出放在仓库外):

uv run python evals/visibility_eval/run.py --out <外部目录> --judge --http-search
uv run python evals/visibility_eval/run.py --corpus <JSON路径> --out <外部目录> --offline
uv run python evals/visibility_eval/compare_learning.py --baseline <主线源码目录> --candidate . --out <外部目录>
uv run python evals/visibility_eval/compare_recall.py --baseline <主线源码目录> --candidate . --out <外部目录>
uv run python evals/visibility_eval/benchmark.py --out <外部目录>

隐私运行使用默认召回配置;--http-search 让所有 search 查询经带真实宿主令牌的 ASGI HTTP 接口,prepare 保留语料的精确近期窗口。HTTP 错误会令整次运行无效。--embedding-cache <已完成的公开向量缓存> 可复用 embedding,召回判断仍重新调用。--offline 检查默认全文降级路径,不加载模型配置。--judge 开启真实召回判断,判断降级或查询未完成会将整次运行标为无效。门槛只检查 forbidden 和跨范围合并;expected / expected_goals 只作为过度过滤诊断。公开集零泄漏不等于 M4 隐藏验收通过。

许可证

iris-core 使用 GNU Affero General Public License v3.0 only(AGPL-3.0-only),与 iris-connectors 及 AstrBot 插件一致。项目仅选择第 3 版;LICENSE 中的通用应用示例不表示本项目选择 “or later”。第三方依赖仍遵循各自的许可证,分发时须保留其版权与许可声明。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages