feat: 拆分 worker 架构并增强 assistant 安全保护
- 新增 daemon/channel/assistant/function/scheduler/logging worker 架构和 JetStream 消息协议 - 修复邮件通知误入 assistant、迁移 checksum、日志 subject 和工具调用无限循环风险 - 新增 assistant 工具轮次熔断、工具结果截断和提示词软收敛规则 - 补充邮件推送关键字、可信发件人配置、升级日志和验证测试
This commit is contained in:
@@ -9,29 +9,22 @@ INSERT INTO ias_upgrade_logs (
|
||||
'0.2.27',
|
||||
'2026-07-08T00:00:00+08:00',
|
||||
'抽离数据库基础设施模块',
|
||||
'新增 ias-db 模块集中负责数据库连接和 SQLx 迁移执行,并将原 ias-service/migrations 整体迁移到 ias-db/migrations。同时新增操作日志查询接口与前端页面,邮件远程已读同步,以及多处改进。',
|
||||
'新增 ias-db 模块集中负责数据库连接和 SQLx 迁移执行,并将原 ias-service/migrations 整体迁移到 ias-db/migrations。',
|
||||
'[
|
||||
"新增 ias-db crate,集中负责 PostgreSQL 连接池创建、DATABASE_URL 读取和 SQLx 迁移执行。",
|
||||
"将原 ias-service/migrations 整体迁移到 ias-db/migrations,迁移嵌入和变更监听由 ias-db/build.rs 负责。",
|
||||
"ias-main 改为直接调用 ias_db::connect_db(),ias-service 只接收已建立的 PgPool 并运行服务,数据库生命周期边界更清晰。",
|
||||
"新增操作日志 HTTP 查询接口 GET /v1/logs 和 GET /v1/logs/{id},支持按级别、模块和关键词筛选,API 文档新增 logs 功能分组。",
|
||||
"新增操作日志 Web 前端页面,支持按级别、模块和关键词筛选查看日志,便于排障和审计。",
|
||||
"消息队列关键节点增加操作日志写入:LLM 响应、渠道消息接收、上下文记录错误、消息发送失败等场景自动记录到 ias_operation_logs。",
|
||||
"ias-mail 新增 mark_remote_message_seen 函数,通过 IMAP 设置邮件已读并验证服务器确认,抽取 connect_imap_session 复用连接逻辑。",
|
||||
"工作流配置加载改为文件级容错:ias-ai 新增 load_configs_with_warnings,返回 LoadedFlowConfigs 包含 warnings 信息,单个 YAML 配置损坏时不会导致所有 HTTP 能力为空。",
|
||||
"ias-context 工具调用说明中的能力引用路径从已废弃的 record_tool_call_instruction 更新为 query_api_docs + call_http_api,并新增单元测试验证。",
|
||||
"新增高德地图地理编码/逆地理编码 HTTP 能力配置文件 tools/amap_geocode.yaml。",
|
||||
"数据库无结构变化;已验证 cargo fmt --check 和 cargo check。"
|
||||
]'::jsonb,
|
||||
'{
|
||||
"ias-common": "0.1.2",
|
||||
"ias-ai": "0.1.14",
|
||||
"ias-ai": "0.1.13",
|
||||
"ias-wechat": "0.1.1",
|
||||
"ias-context": "0.1.6",
|
||||
"ias-http": "0.1.15",
|
||||
"ias-mail": "0.1.16",
|
||||
"ias-context": "0.1.5",
|
||||
"ias-http": "0.1.14",
|
||||
"ias-mail": "0.1.15",
|
||||
"ias-db": "0.1.0",
|
||||
"ias-service": "0.1.25",
|
||||
"ias-service": "0.1.24",
|
||||
"ias-main": "0.2.27"
|
||||
}'::jsonb
|
||||
) ON CONFLICT (version) DO UPDATE SET
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE ias_mail_accounts
|
||||
ADD COLUMN notification_subject_keywords TEXT[] NOT NULL DEFAULT '{}',
|
||||
ADD COLUMN trusted_senders TEXT[] NOT NULL DEFAULT '{}';
|
||||
@@ -0,0 +1,36 @@
|
||||
INSERT INTO ias_upgrade_logs (
|
||||
version,
|
||||
released_at,
|
||||
title,
|
||||
description,
|
||||
changes,
|
||||
modules
|
||||
) VALUES (
|
||||
'0.2.28',
|
||||
'2026-07-09T00:00:00+08:00',
|
||||
'邮件推送过滤和可信发件人渲染',
|
||||
'邮件账号新增标题关键字推送过滤和可信发件人列表,前端详情页按可信状态控制 HTML 邮件远程资源渲染。',
|
||||
'[
|
||||
"邮件账号新增推送标题关键字过滤配置:关键字列表为空时保持全部推送;配置后只有主题命中任一关键字的新邮件才发送微信提醒,邮件本身仍正常入库和刷新缓存。",
|
||||
"邮件账号新增可信发件人列表,邮件详情 API 返回 rendering.trusted_sender 和 rendering.remote_resources_allowed;前端详情页仅在发件人命中可信列表时允许 HTML 邮件加载远程图片资源。",
|
||||
"ias mail auth 和 ias mail edit 支持录入推送关键字和可信发件人;新增 ias mail settings <id> 可单独修改这两项邮箱设置。",
|
||||
"用户配置页面展示邮件账号的推送标题关键字和可信发件人列表,HTTP API 文档同步说明邮件详情渲染策略。",
|
||||
"数据库新增 ias_mail_accounts.notification_subject_keywords 和 ias_mail_accounts.trusted_senders 两个 TEXT[] 字段;已验证 cargo fmt --check、cargo check、cargo test -p ias-mail、cargo test -p ias-service、cargo test -p ias-http 和 bun run build。"
|
||||
]'::jsonb,
|
||||
'{
|
||||
"ias-common": "0.1.2",
|
||||
"ias-ai": "0.1.14",
|
||||
"ias-wechat": "0.1.1",
|
||||
"ias-context": "0.1.6",
|
||||
"ias-http": "0.1.16",
|
||||
"ias-mail": "0.1.17",
|
||||
"ias-db": "0.1.1",
|
||||
"ias-service": "0.1.26",
|
||||
"ias-main": "0.2.28"
|
||||
}'::jsonb
|
||||
) ON CONFLICT (version) DO UPDATE SET
|
||||
released_at = EXCLUDED.released_at,
|
||||
title = EXCLUDED.title,
|
||||
description = EXCLUDED.description,
|
||||
changes = EXCLUDED.changes,
|
||||
modules = EXCLUDED.modules;
|
||||
@@ -0,0 +1,57 @@
|
||||
CREATE TABLE IF NOT EXISTS ias_schedules (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
schedule_type VARCHAR(32) NOT NULL,
|
||||
expression TEXT NOT NULL,
|
||||
target_topic TEXT NOT NULL,
|
||||
payload JSONB NOT NULL DEFAULT '{}',
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
last_fired_at TIMESTAMPTZ,
|
||||
next_fire_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT ias_schedules_type_check CHECK (schedule_type IN ('cron', 'interval'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ias_schedules_due
|
||||
ON ias_schedules(next_fire_at)
|
||||
WHERE enabled = TRUE;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_ias_schedules_name_unique
|
||||
ON ias_schedules(name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ias_schedule_fires (
|
||||
fire_id TEXT PRIMARY KEY,
|
||||
schedule_id BIGINT NOT NULL REFERENCES ias_schedules(id) ON DELETE CASCADE,
|
||||
fire_time TIMESTAMPTZ NOT NULL,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'publishing',
|
||||
attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
published_at TIMESTAMPTZ,
|
||||
error TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ias_schedule_fires_schedule_time
|
||||
ON ias_schedule_fires(schedule_id, fire_time DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ias_schedule_fires_reclaim
|
||||
ON ias_schedule_fires(status, updated_at)
|
||||
WHERE status IN ('failed', 'publishing');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ias_assistant_turns (
|
||||
operation_key TEXT PRIMARY KEY,
|
||||
channel TEXT NOT NULL,
|
||||
account_id TEXT NOT NULL,
|
||||
from_user_id TEXT NOT NULL,
|
||||
turn_id TEXT NOT NULL,
|
||||
trace_id TEXT NOT NULL DEFAULT '',
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'active',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT ias_assistant_turns_status_check
|
||||
CHECK (status IN ('active', 'completed', 'cancelled'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ias_assistant_turns_status_updated
|
||||
ON ias_assistant_turns(status, updated_at DESC);
|
||||
@@ -0,0 +1,46 @@
|
||||
INSERT INTO ias_upgrade_logs (
|
||||
version,
|
||||
released_at,
|
||||
title,
|
||||
description,
|
||||
changes,
|
||||
modules
|
||||
) VALUES (
|
||||
'0.3.0',
|
||||
'2026-07-09T00:00:00+08:00',
|
||||
'进程拆分与 JetStream 数据面',
|
||||
'按架构拆分设计新增 daemon 控制面、独立 worker 和 async-nats JetStream 公共数据面,HTTP、调度、日志和本地 NATS 部署配置同步接入。',
|
||||
'[
|
||||
"新增 ias-daemon 控制面,负责 worker 进程管理、control socket、JetStream 初始化和状态汇总;依赖边界测试确保 daemon 不依赖 AI、上下文、微信或邮件业务 crate。",
|
||||
"ias-common 新增 MessageEnvelope、inbound/outbound/function/schedule/log/DLQ payload、固定 stream/subject 命名和 JetStreamBus 薄封装,统一使用 async-nats + JetStream durable pull consumer、explicit ack、有限并发消费、消费循环持续回收 handler 任务、handler 失败显式 NAK 和最后一次失败后显式 DLQ 转发;DLQ message id 使用原消息 id + UUID,避免同毫秒并发去重碰撞。",
|
||||
"新增 ias-channel、ias-assistant、ias-function、ias-scheduler 和 ias-logging worker;微信入站、assistant 回复、工具调用、邮件提醒、定时触发和结构化日志均通过 JetStream subject 路由;邮件提醒只发布微信 outbound,不会误进入 assistant inbound;WeChat Worker 启动后继续发送上线提醒和版本更新通知。",
|
||||
"CLI 新增 ias daemon run、ias worker assistant/wechat/mail/function/scheduler/logging 和 ias http --host;ias service start/stop/restart/status/logs 改为管理 daemon 控制面。",
|
||||
"HTTP 新增 /v1/daemon/status,daemon control socket 不可用时返回 degraded 状态,HTTP 可独立启动。",
|
||||
"数据库新增 ias_schedules、ias_schedule_fires 和 ias_assistant_turns 表;Scheduler Worker 通过 fire_id 主键保证同一触发只发布一次,失败 fire 可重试,publishing 超时后可回收;Assistant Worker 持久化 active/completed/cancelled turn 状态,worker 重启后 function result 可继续当前 turn。",
|
||||
"删除 ias-service::run_service、旧 ias-service supervisor、内存队列/control/runtime 单进程代码和旧微信 looper,避免新 daemon 与旧 supervisor 双实现并存;daemon 对初始启动失败或运行后退出的 worker 会在后续健康检查 tick 中按配置重试。",
|
||||
"新增 docker-compose.nats.yml,本地 NATS JetStream 使用 file store ./data/nats,客户端和监控端口默认只绑定 127.0.0.1。",
|
||||
"新增 worker 消息序列化/幂等测试、daemon cargo tree 依赖边界测试、max_deliver 语义测试和 JetStream ack/redelivery/DLQ 集成测试模板;/data 已加入 .gitignore,避免误提交 NATS file store;已验证 cargo fmt --check、cargo check 和 cargo test,真实 NATS JetStream 集成测试默认 ignored,需要启动 docker-compose.nats.yml 后手动运行。"
|
||||
]'::jsonb,
|
||||
'{
|
||||
"ias-common": "0.2.0",
|
||||
"ias-ai": "0.1.14",
|
||||
"ias-wechat": "0.1.1",
|
||||
"ias-context": "0.1.6",
|
||||
"ias-http": "0.1.17",
|
||||
"ias-mail": "0.1.17",
|
||||
"ias-db": "0.1.2",
|
||||
"ias-service": "0.1.27",
|
||||
"ias-daemon": "0.1.0",
|
||||
"ias-channel": "0.1.0",
|
||||
"ias-assistant": "0.1.0",
|
||||
"ias-function": "0.1.0",
|
||||
"ias-scheduler": "0.1.0",
|
||||
"ias-logging": "0.1.0",
|
||||
"ias-main": "0.3.0"
|
||||
}'::jsonb
|
||||
) ON CONFLICT (version) DO UPDATE SET
|
||||
released_at = EXCLUDED.released_at,
|
||||
title = EXCLUDED.title,
|
||||
description = EXCLUDED.description,
|
||||
changes = EXCLUDED.changes,
|
||||
modules = EXCLUDED.modules;
|
||||
@@ -0,0 +1,30 @@
|
||||
UPDATE ias_upgrade_logs
|
||||
SET
|
||||
released_at = '2026-07-08T00:00:00+08:00',
|
||||
title = '抽离数据库基础设施模块',
|
||||
description = '新增 ias-db 模块集中负责数据库连接和 SQLx 迁移执行,并将原 ias-service/migrations 整体迁移到 ias-db/migrations。同时新增操作日志查询接口与前端页面,邮件远程已读同步,以及多处改进。',
|
||||
changes = '[
|
||||
"新增 ias-db crate,集中负责 PostgreSQL 连接池创建、DATABASE_URL 读取和 SQLx 迁移执行。",
|
||||
"将原 ias-service/migrations 整体迁移到 ias-db/migrations,迁移嵌入和变更监听由 ias-db/build.rs 负责。",
|
||||
"ias-main 改为直接调用 ias_db::connect_db(),ias-service 只接收已建立的 PgPool 并运行服务,数据库生命周期边界更清晰。",
|
||||
"新增操作日志 HTTP 查询接口 GET /v1/logs 和 GET /v1/logs/{id},支持按级别、模块和关键词筛选,API 文档新增 logs 功能分组。",
|
||||
"新增操作日志 Web 前端页面,支持按级别、模块和关键词筛选查看日志,便于排障和审计。",
|
||||
"消息队列关键节点增加操作日志写入:LLM 响应、渠道消息接收、上下文记录错误、消息发送失败等场景自动记录到 ias_operation_logs。",
|
||||
"ias-mail 新增 mark_remote_message_seen 函数,通过 IMAP 设置邮件已读并验证服务器确认,抽取 connect_imap_session 复用连接逻辑。",
|
||||
"工作流配置加载改为文件级容错:ias-ai 新增 load_configs_with_warnings,返回 LoadedFlowConfigs 包含 warnings 信息,单个 YAML 配置损坏时不会导致所有 HTTP 能力为空。",
|
||||
"ias-context 工具调用说明中的能力引用路径从已废弃的 record_tool_call_instruction 更新为 query_api_docs + call_http_api,并新增单元测试验证。",
|
||||
"新增高德地图地理编码/逆地理编码 HTTP 能力配置文件 tools/amap_geocode.yaml。",
|
||||
"数据库无结构变化;已验证 cargo fmt --check 和 cargo check。"
|
||||
]'::jsonb,
|
||||
modules = '{
|
||||
"ias-common": "0.1.2",
|
||||
"ias-ai": "0.1.14",
|
||||
"ias-wechat": "0.1.1",
|
||||
"ias-context": "0.1.6",
|
||||
"ias-http": "0.1.15",
|
||||
"ias-mail": "0.1.16",
|
||||
"ias-db": "0.1.0",
|
||||
"ias-service": "0.1.25",
|
||||
"ias-main": "0.2.27"
|
||||
}'::jsonb
|
||||
WHERE version = '0.2.27';
|
||||
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE ias_assistant_turns
|
||||
ADD COLUMN IF NOT EXISTS tool_rounds INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS tool_calls INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS last_tool_at TIMESTAMPTZ;
|
||||
@@ -0,0 +1,8 @@
|
||||
UPDATE ias_upgrade_logs
|
||||
SET changes = changes || '[
|
||||
"Assistant Worker 新增 token 消耗保护:每个 turn 默认最多 8 轮工具往返、每轮最多 5 个工具调用,超过后自动熔断并结束当前 turn;工具结果返回模型前默认截断到 4000 字符,避免邮件 HTML 等大响应反复进入上下文;修复日志 subject 生成,logs.worker.assistant 可正确写入 IA_LOGS。"
|
||||
]'::jsonb
|
||||
WHERE version = '0.3.0'
|
||||
AND NOT changes @> '[
|
||||
"Assistant Worker 新增 token 消耗保护:每个 turn 默认最多 8 轮工具往返、每轮最多 5 个工具调用,超过后自动熔断并结束当前 turn;工具结果返回模型前默认截断到 4000 字符,避免邮件 HTML 等大响应反复进入上下文;修复日志 subject 生成,logs.worker.assistant 可正确写入 IA_LOGS。"
|
||||
]'::jsonb;
|
||||
@@ -0,0 +1,8 @@
|
||||
UPDATE ias_upgrade_logs
|
||||
SET changes = changes || '[
|
||||
"Assistant 系统提示词和入口工具描述新增软收敛规则:已有足够信息时直接回答,query_capability_detail 和 query_api_docs 不再作为默认前置步骤;宽泛邮件查询默认只汇总列表,禁止同一 turn 重复调用相同能力参数或重复读取同一邮件 ID;每次 LLM 请求都会注入当前工具预算状态,引导模型在接近上限前主动总结或请用户缩小范围。"
|
||||
]'::jsonb
|
||||
WHERE version = '0.3.0'
|
||||
AND NOT changes @> '[
|
||||
"Assistant 系统提示词和入口工具描述新增软收敛规则:已有足够信息时直接回答,query_capability_detail 和 query_api_docs 不再作为默认前置步骤;宽泛邮件查询默认只汇总列表,禁止同一 turn 重复调用相同能力参数或重复读取同一邮件 ID;每次 LLM 请求都会注入当前工具预算状态,引导模型在接近上限前主动总结或请用户缩小范围。"
|
||||
]'::jsonb;
|
||||
Reference in New Issue
Block a user