feat: chat uploads, vision turn isolation, and MindSpace agent improvements

Add chat file/image upload UX, attachment proxying, vision thumbnails, and per-turn image scoping so agents only use the current upload. Extend MindSpace asset context, billing token state, OA/scenario verify scripts, and related runtime config.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-11 00:23:01 +08:00
parent e3063ea806
commit 32fb2cdeaf
51 changed files with 4588 additions and 186 deletions
@@ -0,0 +1,526 @@
# AI Mind 与 Memind 整合架构分析文档
日期:2026-07-10
状态:架构分析与实施方案;未改代码、未碰生产、未读写用户数据
适用范围:Memind H5、服务号聊天、MindSpace、Memory V2、AI Mind 画像与 persona 能力
## 1. 核心结论
建议把 AI Mind 接成 Memind 的“画像与人格洞察旁路服务”,而不是接成新的聊天主链路、主记忆体或生产执行系统。
推荐边界:
- Memind 继续负责入口、账号、权限、会话、H5、服务号、MindSpace、页面发布、工具执行、计费和生产链路。
- Memory V2 继续负责 Memind 当前记忆入口,保持 pgvector resolve、legacy fallback 等现有体系。
- AI Mind 负责用户画像、性格分析、沟通偏好、长期目标、life_stream 分析、persona profile 和轻量上下文建议。
- Bridge Layer 负责身份映射、授权、限流、缓存、降级、审计和数据分级。
一句话方案:
```text
AI Mind 做“懂用户的人格画像脑”,Memind 继续做“入口、权限、会话、工具和生产执行系统”。
```
## 2. 当前担心与设计目标
当前主要担心是:AI Mind 一旦接入,会不会影响 Memind 已经稳定运行的 H5、服务号、MindSpace、Memory V2、pgvector、legacy 记忆和 goosed 会话体系。
因此设计目标不是“深度替换”,而是“可关闭的旁路增强”:
- 不替换现有聊天回复链路。
- 不替换现有 Memory V2 主链路。
- 不让 AI Mind 直接触发工具、写文件、发布页面或修改用户数据。
- 不让 AI Mind 成为 H5、服务号、MindSpace 的强依赖。
- AI Mind 不可用时,Memind 原功能必须正常。
- 所有接入能力必须用户级可开关、可灰度、可审计、可回滚。
## 3. 推荐总体架构
```text
用户
|
v
Memind H5 / 服务号 / MindSpace
|
| 现有主链路
v
Memind Chat / Session / Goose / Memory V2
|-- pgvector: 聊天语义召回
|-- legacy conversation-memory: 写入与回退
|-- mem0/letta/neo4j 等: 可用但不应默认接管主链路
|
v
正常回复、页面生成、工具执行、发布
旁路增强链路
Memind 重要摘要事件
|
v
AI Mind Bridge Layer
|-- 身份映射
|-- Token 管理
|-- 数据分级
|-- 调用限流
|-- 超时降级
|-- 审计日志
|
v
AI Mind
|-- user profile
|-- persona profile
|-- life_stream
|-- personality / preference analysis
|-- lightweight persona context
|
v
Memind 只读取摘要级画像上下文
```
## 4. 能力分层
### 4.1 Memind 保留的能力
Memind 不应让渡以下能力:
- 用户登录、权限、用户空间归属。
- H5 和服务号消息入口。
- goosed / agent session 创建、恢复、路由和工具策略。
- MindSpace 页面生成、保存、发布、公开链接。
- 生产数据写入和生产发布流程。
- 当前 Memory V2 的主配置和回退路径。
- 计费、额度、服务隔离和审计。
### 4.2 AI Mind 提供的能力
AI Mind 适合提供:
- 用户画像:长期偏好、沟通风格、兴趣、常见目标。
- 性格分析:表达方式、决策风格、风险偏好。
- 轻量 persona context:给 Memind 聊天提供简短、可审计的上下文提示。
- life_stream 分析:对重要摘要事件做长期沉淀。
- profile evidence:为画像结论保留证据来源。
- 可选 persona chat:只在用户主动进入人格/数字分身模式时使用。
### 4.3 Bridge Layer 的职责
Bridge Layer 是风险控制点,不应只是一个 HTTP client。
它需要负责:
- `memind_user_id``ai_mind_user_id/persona_id` 的绑定。
- external token 的服务端保存、加密、轮换和禁用。
- scopes 控制,例如 `profile_read``life_stream_write``persona_chat`
- 每次调用的超时、重试、熔断和 fail-open。
- 数据投递前的摘要、脱敏和隐私分级。
- 审计日志:谁、何时、把什么摘要投给了 AI Mind,聊天注入了什么画像。
- 缓存 AI Mind profile,避免每条消息都实时请求。
## 5. 身份映射设计
身份映射是最大风险点。Memind 与 AI Mind 不应共享数据库表,也不应直接复用彼此的主键。
建议使用独立绑定关系:
```text
memind_ai_mind_bindings
```
建议字段:
- `id`
- `memind_user_id`
- `ai_mind_user_id`
- `default_persona_id`
- `persona_external_token`
- `external_user_id`
- `enabled`
- `scopes`
- `created_at`
- `updated_at`
- `last_profile_sync_at`
- `last_life_stream_event_at`
设计约束:
- `memind_user_id` 对应 Memind 的 `h5_users.id`
- `ai_mind_user_id` 对应 AI Mind 用户。
- `default_persona_id` 对应 AI Mind 默认 persona。
- `external_user_id` 建议使用稳定格式:`memind:<h5_user_id>`
- 前端永远不拿 external token。
- 禁止用邮箱、openid、昵称等非稳定字段做主映射。
- 所有调用前必须校验 binding 是否启用、scope 是否允许。
## 6. 数据流设计
### 6.1 Profile 读取流
目标:Memind 读取 AI Mind 的用户画像摘要,但不影响主聊天。
```text
Memind 用户进入 H5 / 服务号
-> Bridge 检查用户是否绑定 AI Mind
-> 若已绑定且 profile_read 允许
-> 拉取或读取缓存的 AI Mind profile summary
-> 返回 Memind 内部可用的轻量 Profile Context
```
建议 Profile Context 结构:
```json
{
"enabled": true,
"source": "ai_mind",
"confidence": 0.82,
"summary": "用户偏好直接、执行导向的建议,关注生产稳定性和边界控制。",
"communication_style": "先结论后细节,明确风险与边界",
"long_term_goals": ["稳定接入记忆系统", "保护现有生产链路"],
"do_not_assume": ["不要自动发布", "不要修改生产数据"],
"updated_at": "2026-07-10T00:00:00Z"
}
```
注入聊天时必须限制长度,例如 300 到 800 字,并且只作为“理解用户偏好”的提示,不允许覆盖系统策略。
### 6.2 Life Stream 投递流
目标:让 AI Mind 逐步学习用户,但不把所有原始聊天无脑同步过去。
```text
Memind 产生高价值事件
-> 本地先生成摘要
-> 标记 source / privacy_class / event_type / user_id / session_id
-> Bridge 校验 scope 与隐私等级
-> 异步投递 AI Mind life_stream
-> 投递失败只记录,不影响用户体验
```
适合投递的事件:
- 用户主动保存的重要资料摘要。
- MindSpace 页面发布摘要。
- 用户明确表达的长期偏好或目标。
- 服务号中明显有长期价值的摘要。
- 用户主动要求“记住”的内容。
不建议投递的内容:
- 完整原始聊天流水。
- 临时任务细节。
- 一次性验证码、密钥、隐私内容。
- 未经过摘要和分级的服务号原文。
- 页面生成过程中的内部工具日志。
### 6.3 轻量聊天介入流
目标:让 AI Mind 影响语气和理解,不影响执行和主记忆。
```text
用户发起聊天
-> Memind 正常解析用户、会话、权限
-> Memory V2 正常 resolve 当前记忆
-> Bridge 获取短 Profile Context
-> 将 Profile Context 作为低优先级用户画像提示
-> 正常走 Memind / Goose 回复
```
硬约束:
- AI Mind context 不能覆盖系统提示。
- AI Mind context 不能增加工具权限。
- AI Mind context 不能改变发布、写文件、生产操作策略。
- AI Mind 超时或失败时直接跳过。
- 每次注入内容都应可审计。
### 6.4 Persona Chat 可选流
persona chat 不应默认替代普通聊天。
只在以下情况下使用:
- 用户进入“数字分身”或“人格陪伴”入口。
- 用户明确选择某个 persona。
- 内部灰度账号启用。
- 管理后台明确打开 `persona_chat` scope。
普通 H5 和服务号默认仍走 Memind 当前聊天链路。
## 7. 与 Memory V2 的关系
当前建议不是把 AI Mind 接到 Memory V2 主 backend。
推荐关系:
```text
legacy conversation-memory
-> 稳定记录和回退
pgvector
-> 当前聊天语义召回
mem0
-> 可作为候选记忆提取/压缩能力,但不负责 resolve
AI Mind
-> 用户画像、性格、人生流、persona profile
-> 通过 Bridge 产出轻量 Profile Context
```
AI Mind 的 Profile Context 可以作为聊天上下文的一小段附加信息,但不应直接替代:
- `memoryV2.resolve(...)`
- `conversationMemoryService`
- pgvector 检索
- legacy 写入与 compact
这样可以避免三类问题:
- 记忆来源混乱。
- 召回结果不可解释。
- 回滚时不知道关闭哪个系统。
## 8. 分阶段实施方案
### 阶段 0:只读画像验证
目标:验证 AI Mind 画像质量,不影响任何用户回复。
动作:
- 定义 AI Mind Profile Context 数据格式。
- 定义绑定关系和 scope 语义。
- 对内部测试用户建立绑定。
- Memind 后台只读拉取 AI Mind profile。
- 不注入聊天,不投递生产用户数据。
验收:
- 能正确绑定用户。
- 能拉取画像摘要。
- 画像不会串用户。
- AI Mind 不可用时 Memind 无感。
### 阶段 1:异步 life_stream 旁路
目标:让 AI Mind 开始基于摘要事件生成画像。
动作:
- 只对内部测试用户启用。
- 只投递摘要,不投递完整原文。
- 事件必须带 `source``event_type``privacy_class``external_user_id`
- 失败只记录,不影响主链路。
验收:
- 投递不会阻塞 H5/服务号。
- AI Mind 能生成可解释 profile evidence。
- 可以按用户关闭投递。
- 可以追踪每条投递来源。
### 阶段 2:轻量聊天画像注入
目标:让 AI Mind 轻微影响聊天风格和上下文理解。
动作:
- 仅内部灰度。
- 每次聊天最多注入 300 到 800 字 Profile Context。
- 注入内容只包含偏好、风格、长期目标、注意事项。
- 不允许包含工具指令、权限指令、发布指令。
- 每次注入写审计记录。
验收:
- 回复质量提升可感知。
- 不影响工具权限和生产边界。
- AI Mind 失败时自动降级。
- 可以按用户、按入口关闭。
### 阶段 3Persona 模式
目标:提供独立的人格/数字分身体验。
动作:
- 新增独立入口或显式模式。
- 调用 AI Mind persona external chat。
- 与普通聊天明确区分。
- 不默认接管服务号和 H5 普通聊天。
验收:
- persona 回复与普通 Memind 回复边界清楚。
- 用户知道自己进入了人格模式。
- 可以退出 persona 模式回到普通聊天。
- persona 不触发 Memind 工具执行。
### 阶段 4:更深融合前评审
只有当前三阶段稳定后,才讨论更深融合。
评审内容:
- 是否允许 AI Mind 画像进入 Memory V2 的某个只读 backend。
- 是否允许 AI Mind profile 参与 chat intent router。
- 是否允许 MindSpace 页面摘要成为 AI Mind knowledge。
- 是否需要用户可视化管理“AI Mind 记住了什么”。
- 是否需要数据删除、纠错、导出流程。
## 9. 开关与灰度建议
建议所有能力都具备独立开关。
```text
AI_MIND_ENABLED=0/1
AI_MIND_PROFILE_READ_ENABLED=0/1
AI_MIND_CHAT_INJECTION_ENABLED=0/1
AI_MIND_LIFE_STREAM_WRITE_ENABLED=0/1
AI_MIND_PERSONA_CHAT_ENABLED=0/1
AI_MIND_FAIL_OPEN=1
AI_MIND_TIMEOUT_MS=1500
AI_MIND_MAX_CONTEXT_CHARS=800
```
用户级 scope 建议:
```json
{
"profile_read": true,
"profile_inject": false,
"life_stream_write": false,
"persona_chat": false
}
```
灰度顺序:
1. 本地开发环境。
2. 内部测试用户。
3. 103 内部账号。
4. 少量真实用户 opt-in。
5. 正式用户分批启用。
任何阶段都必须能一键关闭 AI Mind,不影响 Memind 主功能。
## 10. 风险清单
### 10.1 用户串号
风险:错误绑定导致 A 用户画像注入 B 用户聊天。
缓解:
- 独立绑定表。
- 所有调用带 `external_user_id=memind:<h5_user_id>`
- 调用前校验登录用户与 binding。
- 审计 profile 来源。
### 10.2 记忆污染
风险:临时信息、错误信息、敏感内容进入 AI Mind 长期画像。
缓解:
- 先摘要,再投递。
- 明确 privacy_class。
- 只投递高价值事件。
- 支持用户删除、纠错、关闭。
### 10.3 主链路被拖慢
风险:AI Mind 响应慢导致 H5 或服务号回复变慢。
缓解:
- 画像缓存。
- 短超时。
- fail-open。
- life_stream 异步投递。
### 10.4 权限污染
风险:AI Mind 输出被误当成系统指令,影响工具或生产操作。
缓解:
- Profile Context 只作为低优先级用户画像。
- 禁止 AI Mind 返回工具权限。
- 禁止 AI Mind context 覆盖系统提示。
- 明确上下文模板边界。
### 10.5 隐私与合规
风险:用户不知道哪些内容被用于画像。
缓解:
- 用户授权开关。
- 可查看、可删除、可纠错。
- 敏感内容默认不投递。
- 审计记录可追踪。
## 11. 验收指标
技术指标:
- AI Mind 故障时 Memind 主链路正常。
- profile 拉取 P95 小于目标阈值,超时自动跳过。
- life_stream 投递失败不影响用户请求。
- 注入上下文长度受控。
- 所有调用可按用户追踪。
产品指标:
- 用户感觉回复更懂自己。
- 沟通风格更稳定。
- 不出现明显串号、幻觉画像、过度推断。
- 服务号和 H5 的普通使用不受影响。
安全指标:
- external token 不出现在前端。
- 生产操作权限不受 AI Mind 影响。
- 用户可以关闭 AI Mind 增强。
- 删除/纠错路径明确。
## 12. 不建议方案
不建议:
- 直接合并 Memind 和 AI Mind 项目。
- 直接共享数据库表。
- 直接把 AI Mind 接成 Memory V2 主 backend。
- 直接替换 pgvector 或 legacy conversation-memory。
- 默认让 AI Mind 接管 H5/服务号普通聊天。
- 每条服务号消息都同步完整原文。
- 让 AI Mind 直接触发工具、发布页面或写生产数据。
- 没有审计和用户级开关就面向全量生产用户启用。
## 13. 推荐下一步
在不写代码前,建议先完成这些设计决策:
1. 明确 AI Mind 的第一阶段只做 profile read,还是同时做 life_stream write。
2. 明确默认 persona 是“用户数字分身”还是“陪伴型助手人格”。
3. 明确哪些事件可以投递 AI Mind,哪些永远不投递。
4. 明确用户是否需要显式授权。
5. 明确 Profile Context 的最大长度和字段。
6. 明确审计日志中记录哪些信息。
7. 明确 103 灰度账号和回滚开关。
8. 明确 AI Mind 故障时的 UI 表现。
建议第一版实施只做:
```text
AI Mind profile read + 内部测试用户绑定 + 画像摘要展示
```
确认画像质量和身份绑定可靠后,再进入:
```text
摘要级 life_stream 投递 + 轻量聊天画像注入
```
这条路线最稳:既能让 AI Mind 开始产生价值,又不会撬动 Memind 现有生产功能体系。
+1
View File
@@ -18,6 +18,7 @@
- 自动跟随 provider/模型,换模型不需重调 token 单价。
- 仅当上游回传 `accumulatedCost` 时生效;缺失则**回退**原 flat token 路径,不破坏现有计费。
- 若 goose Finish / session 均未带 cost,且 `H5_COST_ESTIMATE_FROM_TOKENS=1`(成本模式默认开启),Portal 会按 DeepSeek 中继粗估价(`billing-token-state.mjs`)补齐 `accumulatedCost`,再走 `× margin`
- 生产启用:`.env``H5_USE_BACKEND_COST=1` + `H5_MARGIN_MULTIPLIER=<目标毛利>`
> ⚠️ 上线前需确认:一帧真实 SSE 的 `token_state` 是否带 `accumulated_cost`goose `sessions.db` 有该列,但要确认 SSE Finish 事件也序列化了它)。确认前 multiplier 改动是安全的(无 cost 即回退)。
@@ -0,0 +1,703 @@
# TKMind Search Capability 插拔式接入架构与开发计划
日期: 2026-07-10
状态: 设计评估完成,等待用户明确指令后再开始开发
## 0. 文档目的
本文定义如何在现有 Memind Portal + Goose Runtime 架构中,以插拔方式增加以下四项外部信息能力:
- `search.web`: 公共网页搜索。
- `search.news`: 新闻和时效信息搜索。
- `search.code`: GitHub 等公共代码与开源项目搜索。
- `search.read`: 根据 URL 读取并清洗网页正文。
本文只描述架构、边界、协议和后续开发计划。本阶段不修改业务代码、不启动新服务、不调用生产 Provider、不改变生产配置,也不执行发布。
第一阶段不考虑 Skill Store、第三方开发者发布、商业化、Credit 扣费、收益分成和第三方代码托管。
## 1. 结论
现有项目已经具备适合 Search Capability 的扩展入口,不需要重构聊天和会话主链路。
推荐目标:
```text
H5 / Web / WeChat
|
v
现有 Agent Run Gateway
|
v
现有 tkmind-proxy
|
v
Goose
|
| 仅在功能开关和用户能力同时允许时注册
v
tkmind-search MCP Extension
|
v
Search Hub
- request validation
- fixed provider routing
- result normalization
- citation construction
- URL safety
- timeout / limits
|
+--> SearXNG: web / news
+--> GitHub API: code
+--> Reader Adapter: read
+--> optional commercial provider adapters
```
核心决策:
1. 不替换现有 Goose `platform/web` 扩展。
2. 不覆盖现有 `web` Skill。
3. 不覆盖现有本地代码搜索 `search` Skill。
4. 新增独立扩展名 `tkmind-search`,默认关闭。
5. Search Hub 不进入 Portal HTTP 主请求链路。
6. Search 失败只能影响当前工具调用,不能导致会话、SSE 或 Agent Run 基础设施失效。
7. 第一阶段使用固定 Provider 路由,不引入 LLM Router 和动态评分。
## 2. 现有项目基础
### 2.1 已有能力注入机制
`capabilities.mjs` 已经通过 `buildAgentExtensionPolicy()` 生成 Goose `extension_overrides`
当前 `web` capability 会注入:
```json
{
"type": "platform",
"name": "web",
"available_tools": ["web_search", "fetch_url"]
}
```
这证明现有架构已经把“用户能力授权”和“Goose 实际工具集合”连接起来。新 Search 不应另建一条绕过该策略的入口。
### 2.2 已有禁用和会话对账机制
`policies.mjs` 会在 `network_egress=deny` 时移除联网能力。
`session-reconcile.mjs` 会在恢复会话时:
- 获取当前会话扩展。
- 移除不再允许的扩展。
- 添加缺失的扩展。
- 在扩展集合变化时重启 Goose session runtime。
因此,新 Search Capability 可以复用当前权限和会话对账体系,形成可验证的关闭语义。
### 2.3 已有搜索相关 Skill
当前仓库中两个名称必须保留原义:
- `skills/web`: Goose 内置 `web_search``fetch_url` 的使用说明。
- `skills/search`: 使用 ripgrep 搜索当前工作区代码和文件,不是公网代码搜索。
为避免命名冲突,新能力内部使用:
- Goose extension: `tkmind-search`
- Capability grant: `search_external`
- Search protocol capability: `search.web` / `search.news` / `search.code` / `search.read`
- 可选用户 Skill 文档名: `search-external`
### 2.4 现有路由基础
当前 `chat-intent-router.mjs` 已将实时新闻、天气、行情等需要联网查询的请求判定为 `agent_orchestration`
第一阶段不扩大自动路由范围。测试和灰度优先通过显式选择 Search Skill 或专用测试账号进入 Agent,避免普通问答被误路由。
## 3. 硬性隔离边界
### 3.1 保持不变的模块
第一阶段禁止因 Search 接入而改变以下模块的职责或协议:
- H5 发送消息和订阅事件的顺序。
- `POST /agent/runs` 的请求语义。
- Agent Run 状态机。
- `tkmind-proxy` 作为 Portal 到 goosed 的唯一适配器边界。
- Session Broker ownership 和 goosed target mapping。
- Session SSE 和 Run SSE 协议。
- `chat-finish-sync` 的消息 merge 行为。
- Memory V2 的读取、写入和压缩行为。
- MindSpace 页面、文件和发布链路。
- UserDataSpace 数据访问。
- MySQL 用户、会话、订单和计费表。
- 现有 `platform/web` 和本地 `search` Skill。
### 3.2 Search Hub 允许做的事情
Search Hub 只负责:
- 校验搜索或读取请求。
- 根据 capability 使用固定规则选择 Provider。
- 调用公共搜索、GitHub 或 Reader Provider。
- 标准化结果。
- 去除明显重复 URL。
- 构建来源和引用对象。
- 执行超时、响应大小和 URL 安全限制。
- 返回结构化结果或统一错误。
### 3.3 Search Hub 禁止做的事情
Search Hub 不得:
- 创建、恢复或修改 Goose session。
- 写入 Agent Run 状态。
- 直接写 Memory V2。
- 直接写 MindSpace 页面或文件。
- 读写 UserDataSpace。
- 读取完整聊天记录。
- 直接访问用户业务 MySQL 表。
- 接管 Goose 最终回答。
- 把搜索结果自动保存为长期资产。
- 因 Provider 故障修改 Portal 或 Goose 服务状态。
## 4. 运行形态
### 4.1 第一阶段: 独立 stdio MCP
第一阶段建议增加一个独立进程运行的 MCP Extension:
```text
Goose session
-> stdio MCP: tkmind-search
-> Search Hub modules
-> outbound HTTPS
```
优点:
- 复用仓库已经验证过的 stdio MCP 接入方式。
- Search 代码不进入 Portal 进程请求处理路径。
- MCP 进程异常不会直接终止 Portal。
- 关闭扩展后 Goose 不启动对应进程。
- 不新增公网监听端口。
限制:
- 每个运行实例的缓存和健康状态不天然共享。
- 多会话下可能产生多个 MCP 子进程。
- Provider 限流第一阶段只能依赖 Provider 和轻量本地限制。
这些限制可以接受,因为第一阶段目标是安全验证四项能力,而不是建设大规模 Search 平台。
### 4.2 后续可选: 内部 Search Sidecar
当调用量需要共享缓存、全局限流和统一 Provider 熔断时,可以演进为:
```text
Goose
-> tkmind-search MCP bridge
-> private Search Hub sidecar
-> Providers
```
Sidecar 只能监听内部地址,并且不属于第一阶段范围。
## 5. Goose 工具设计
推荐使用一个扩展、两个工具,而不是四个独立插件。
### 5.1 `tkmind_search`
负责:
- `search.web`
- `search.news`
- `search.code`
请求示例:
```json
{
"capability": "search.web",
"query": "Goose Agent latest release",
"language": "zh-CN",
"region": "CN",
"freshness": {
"type": "days",
"value": 30
},
"limit": 10
}
```
### 5.2 `tkmind_read`
负责:
- `search.read`
请求示例:
```json
{
"url": "https://example.com/article",
"max_chars": 30000,
"include_metadata": true
}
```
`search.read` 不与搜索请求共用 `query` 字段。Reader 的输入、SSRF 风险和响应上限与搜索不同,应保持独立工具 Schema。
## 6. 统一返回协议
搜索结果最小结构:
```json
{
"request_id": "srch_example",
"status": "success",
"capability": "search.web",
"provider": "searxng",
"query": "Goose Agent latest release",
"results": [
{
"id": "result_001",
"type": "webpage",
"title": "Example title",
"url": "https://example.com/article",
"snippet": "Example snippet",
"published_at": null,
"retrieved_at": "2026-07-10T00:00:00Z",
"source_name": "Example",
"language": "en",
"provider_rank": 1,
"metadata": {}
}
],
"citations": [
{
"citation_id": "cite_001",
"result_id": "result_001",
"url": "https://example.com/article",
"title": "Example title"
}
],
"timing": {
"duration_ms": 800
}
}
```
Reader 结果最小结构:
```json
{
"request_id": "read_example",
"status": "success",
"capability": "search.read",
"url": "https://example.com/article",
"final_url": "https://example.com/article",
"title": "Example title",
"content": "Cleaned article text",
"content_type": "text/html",
"content_hash": "sha256:...",
"retrieved_at": "2026-07-10T00:00:00Z",
"trust_level": "untrusted_external_content",
"truncated": false
}
```
Provider 自带摘要只能作为结果字段,不能直接作为 TKMind 最终答案。最终回答继续由 Goose 生成。
## 7. Provider 方案
### 7.1 第一阶段推荐
| Capability | 默认 Provider | 说明 |
|---|---|---|
| `search.web` | SearXNG | 自建、固定配置、返回标准化网页结果 |
| `search.news` | SearXNG news category | 第一阶段复用同一搜索服务 |
| `search.code` | GitHub Search API | 搜索 repository、code、issue、release 等 |
| `search.read` | Readability Adapter 或受控 Reader | 读取 URL 并输出正文和元数据 |
SearXNG 的生产部署和对外服务方式需要在实施前完成许可证合规确认。
### 7.2 可选 Provider
Brave、Tavily、Jina Reader 可以后续作为 Provider Adapter 接入,但不应成为第一阶段启动条件。
第一阶段不做多 Provider 并行聚合,不做质量动态评分,也不做自动商业 Provider 降级。
## 8. 功能开关和授权
### 8.1 建议开关
```text
TKMIND_SEARCH_ENABLED=false
TKMIND_SEARCH_PROVIDER_SEARXNG_ENABLED=false
TKMIND_SEARCH_PROVIDER_GITHUB_ENABLED=false
TKMIND_SEARCH_READER_ENABLED=false
```
### 8.2 生效条件
普通用户只有同时满足以下条件才能获得新扩展:
```text
TKMIND_SEARCH_ENABLED = true
AND user capability search_external = true
AND network_egress = allow
```
任意条件不满足时,不得把 `tkmind-search` 加入 `extension_overrides`
### 8.3 关闭语义
`TKMIND_SEARCH_ENABLED=false` 时:
- 不注册 `tkmind-search` Extension。
- 不启动 Search MCP 子进程。
- 不读取 Provider Secret。
- 不产生 Search 外部网络请求。
- 不改变现有 `platform/web`
- 不影响现有 Skill、MCP、会话、SSE、Memory、MindSpace 和 UserDataSpace。
- 老会话恢复时,由现有 session reconcile 移除 `tkmind-search`
### 8.4 灰度原则
第一阶段只允许专用测试账号开启 `search_external`
禁止一开始修改默认用户能力,也禁止对所有用户替换现有 `web` capability。
## 9. 安全要求
### 9.1 URL 和 SSRF
Reader 必须拒绝:
- `localhost` 和回环地址。
- RFC1918 私网地址。
- Link-local 和云元数据地址。
- Docker、Colima、Kubernetes 和宿主机内部域名。
- 非 HTTP/HTTPS 协议。
- 解析后落入受限地址的域名。
每次重定向都必须重新执行 URL 和解析后 IP 校验。还需要限制:
- 最大重定向次数。
- 最大响应体积。
- 请求总超时。
- 允许的 MIME 类型。
- 压缩后解包体积。
### 9.2 Prompt Injection
所有 Reader 正文必须标记:
```json
{
"trust_level": "untrusted_external_content"
}
```
网页内容只能作为资料,不得改变系统指令、请求其他工具、读取用户数据或执行命令。
### 9.3 数据最小化
Provider 请求不得包含:
- 真实 `user_id`
- `tenant_id`
- 手机号、邮箱或用户名。
- 完整会话内容。
- Memory V2 内容。
- MindSpace 私有文件。
- UserDataSpace 数据。
Provider 只接收搜索参数、随机 request ID 和必要的 Provider 鉴权信息。
### 9.4 Secret
Provider Key 只能从运行环境或 Secret 引用读取,禁止写入仓库、Skill 文档、日志和 Goose 对话上下文。
## 10. 超时和错误隔离
第一阶段建议:
| 操作 | 超时上限 |
|---|---:|
| Web/News Search | 10 秒 |
| Code Search | 10 秒 |
| Reader 单页 | 10 秒 |
| MCP 工具总调用 | 12 秒 |
统一错误示例:
```json
{
"status": "error",
"error": {
"code": "PROVIDER_TIMEOUT",
"message": "Search provider timed out",
"retryable": true
}
}
```
Search MCP 不得主动重启 Portal 或 goosed,不得修改 Agent Run 数据。Provider 失败应作为普通工具错误返回,由 Goose 决定是否解释、重试一次或继续回答。
## 11. 可观测性
第一阶段只记录技术指标,不接用户扣费:
- request ID。
- capability。
- Provider 名称。
- 成功或错误码。
- 响应耗时。
- 结果数量。
- 是否触发 Reader。
- 是否截断。
默认不记录完整搜索词。调试环境需要查询内容时,应显式开启并做脱敏处理。
Search 日志应与 Portal 会话日志分离,避免 Provider 波动污染现有服务健康判断。
## 12. 对现有项目的预期影响
### 12.1 功能关闭时
预期影响为零:
- Goose 工具列表不变化。
- Portal 请求链路不变化。
- 会话和 SSE 不变化。
- 不新增网络请求。
- 不新增运行进程。
### 12.2 测试账号开启时
只影响该账号的 Goose 工具集合:
- Goose 可以调用新 Search MCP。
- 搜索轮次可能增加 1 到 12 秒延迟。
- 结构化结果会占用一定会话上下文 Token。
- Provider 故障只影响当前搜索工具调用。
### 12.3 需要避免的间接影响
- 不要扩大聊天 Router 规则导致普通问答大量进入 Agent。
- 不要让前端只根据 Skill grant 显示搜索入口,而忽略有效 capability 和 `network_egress`
- 不要让新旧搜索工具使用相同名称。
- 不要把 Search Provider 健康状态加入 Portal 总体存活条件。
- 不要在 Search 稳定前关闭现有 `platform/web`
## 13. 开发计划
所有开发阶段均等待用户后续明确指令。每个 Patch 必须可以单独测试和停止,不自动进入下一阶段。
### Patch 0: 基线确认
目标:
- 记录开发前分支和工作区状态。
- 确认现有 capability、session reconcile、Agent Run 和 SSE 测试基线。
- 确认当前 goosed 对自定义 stdio MCP 配置字段的实际兼容性。
产物:
- 基线测试结果。
- 不改业务行为的接入点确认。
停止条件:
- 现有关键测试未通过。
- goosed 当前版本无法可靠加载独立 stdio MCP。
### Patch 1: 协议和纯函数
目标:
- 定义 Search、Read、Result 和 Error Schema。
- 实现请求校验、URL canonicalization、结果标准化和错误映射纯函数。
- 不连接任何 Provider。
建议新增范围:
```text
search-capability/
schemas/
search-errors.mjs
request-validator.mjs
result-normalizer.mjs
citation-builder.mjs
```
验收:
- Schema 正反例单测。
- Provider 原始结果不能穿透到 Goose。
- 所有成功结果必须包含来源 URL。
### Patch 2: Search MCP 空运行时
目标:
- 新增独立 `tkmind-search` stdio MCP。
- 暴露 `tkmind_search``tkmind_read`
- Provider 未启用时返回 `CAPABILITY_DISABLED`
- 默认不开启、不注入任何用户会话。
验收:
- MCP initialize、tools/list、tools/call 单测。
- MCP 子进程异常不影响 Portal 测试。
- 功能关闭时 extension policy 与开发前完全一致。
### Patch 3: SearXNG Web/News Adapter
目标:
- 接入 `search.web``search.news`
- 使用固定 Provider 路由。
- 完成超时、结果上限和标准化。
验收:
- 使用本地 mock Provider 完成确定性测试。
- Provider 超时、空结果、无效 JSON 和 5xx 均返回统一错误。
- 新闻结果保留发布时间;未知发布时间不得伪造。
### Patch 4: GitHub Code Adapter
目标:
- 接入 `search.code`
- 与现有本地 `search` Skill 保持名称和行为隔离。
验收:
- Repository、Issue 和 Code 等结果映射为统一结构。
- API 限流返回可重试错误。
- 不把 GitHub Token 返回到日志或工具结果。
### Patch 5: Reader 和 URL Safety
目标:
- 接入 `search.read`
- 实现 URL Safety Validator。
- 增加重定向、MIME、体积和正文长度限制。
验收:
- 回环、私网、metadata、IPv6 特殊地址和重定向到私网均被拒绝。
- 非 HTML/允许类型被拒绝或明确标记。
- 正文统一标记为不可信外部内容。
- 超大页面被截断且返回 `truncated=true`
### Patch 6: Capability 和功能开关接入
目标:
- 增加默认关闭的 `search_external` capability。
- 增加全局和 Provider 功能开关。
-`buildAgentExtensionPolicy()` 中条件化注入 `tkmind-search`
- 复用 `network_egress` 和 session reconcile。
验收:
- 默认用户 extension policy 不变化。
- 只有三层条件全部满足时才注入扩展。
- 开关关闭后,恢复会话会移除扩展。
- 现有 `web``search` Skill 测试不变化。
### Patch 7: 测试账号本地端到端验证
目标:
- 只为专用测试账号开启能力。
- 验证 Search 工具调用、结果引用和失败隔离。
- 不改变默认用户能力。
验收场景:
1. 普通聊天不调用 Search。
2. Web 搜索返回来源。
3. News 搜索保留时间信息。
4. Code 搜索不调用本地 ripgrep Skill。
5. Reader 拒绝私网 URL。
6. Provider 超时后会话仍可继续。
7. Search 关闭后同一账号恢复为原工具集合。
8. Run SSE 和 Session SSE 的终态行为不变。
### Patch 8: 可选 UI 和窄范围路由
只有后端本地验证稳定后才评估:
- 是否增加独立“联网搜索”入口。
- 前端是否按有效 capability 而不是单纯 Skill grant 展示。
- 是否增加窄范围、可关闭的自动 Search 意图规则。
第一阶段可以完全不改 UI,通过显式测试请求验证后端能力。
## 14. 测试与回归范围
实际开发时至少需要覆盖:
- Search Schema 和 Adapter 单测。
- MCP 协议单测。
- Capability policy 单测。
- Session reconcile 增删扩展单测。
- `tkmind-proxy` 会话启动和恢复测试。
- Agent Run Gateway 回归测试。
- SSE taxonomy 和 Finish sync 回归测试。
- 现有 `web`、本地 `search` 和 chat skill 路由测试。
- URL Safety 专项测试。
如果任何改动触及 MindSpace 回归守卫路径,必须按照仓库 `AGENTS.md` 执行对应 verify;本方案原则上不要求触及这些路径。
## 15. 回退方案
本地或后续灰度发现问题时,按以下顺序回退:
1. 设置 `TKMIND_SEARCH_ENABLED=false`
2. 确认新会话不再包含 `tkmind-search`
3. 恢复测试账号会话,确认 reconcile 移除扩展。
4. 保留现有 `platform/web`,继续提供原搜索能力。
5. Search MCP 和 Provider 不进入 Portal 健康检查,不需要重启现有服务完成逻辑回退。
代码回退不得使用脏工作区或直接修改生产文件,仍须遵守仓库开发和发布规范。
## 16. 第一阶段明确不做
- Skill Store。
- 第三方开发者接入。
- 用户支付和 Search Credit。
- 开发者收益结算。
- Tavily Deep Research 编排。
- 多 Provider 并行聚合。
- AI Provider Router。
- LLM 全量重排。
- Platform Hosted 第三方代码。
- 企业私有搜索。
- 搜索结果自动写 Memory 或 MindSpace。
- 替换或删除现有 Goose `platform/web`
- 修改现有 Run SSE 和 Session SSE 协议。
## 17. 开发启动闸门
开始任何代码开发前,必须再次获得用户明确指令。
开发指令不自动授权:
- `git push`
- 合并 `main`
- 构建或发布 `103` runtime/artifact。
- 修改生产配置。
- 启动生产 Provider。
- 执行任何生产动作。
开发完成后必须先完成对应本地测试和 verify,再由用户决定下一步。