DeepSeek Harness: 服务隔离与作用域
最后更新:2026-08-31
在多 Agent、多会话的环境中,服务共享是福也是祸——全局服务方便但容易冲突,隔离服务安全但通信成本高。Cordis 的作用域系统在两者之间找到平衡:默认共享,按需隔离。
💡 提示:作用域的核心思想是"默认共享,按需隔离"——大多数服务全局共享即可,只有需要隔离的服务才配置 isolate。不隔离是常态,隔离是特例。
📋 前置知识:已完成 19-service.md,理解 Service 基类
1. 你将学到
- 全局作用域(Global Scope)
- 会话作用域(Session Scope)
- 请求作用域(Request Scope)
- isolate realm 配置
- 作用域与 Agent preset
- 多 Agent 环境下的服务隔离
2. 全局作用域(Global Scope)
(1) 默认行为
默认情况下,所有 Service 都是全局的——整个 DSH 实例只有一个实例:
TYPESCRIPT
export default class CacheService extends Service {
constructor(ctx: Context) {
super(ctx, 'cache') // 全局唯一
}
}
(2) 全局服务的特点
| 特点 | 说明 |
|---|---|
| 单例 | 整个进程只有一个实例 |
| 共享 | 所有会话和请求共享同一状态 |
| 无隔离 | 会话 A 修改的数据会话 B 能看到 |
(3) 适用场景
- 配置管理(全局配置只需一份)
- 连接池(共享连接比各自创建更高效)
- 日志服务(日志应统一收集)
- 模型适配器(API 调用可跨会话复用)
(4) ▶ 示例 4
TYPESCRIPT
// ❌ 全局缓存导致会话间数据泄漏
export default class CacheService extends Service {
private data = new Map<string, any>()
set(key: string, value: any) {
this.data.set(key, value)
}
}
// 会话 A: ctx.cache.set('temp', 'secret-data')
// 会话 B: ctx.cache.get('temp') → 'secret-data' (泄漏!)
3. 会话作用域(Session Scope)
(1) 概念
会话作用域为每个会话创建独立的服务实例:
TYPESCRIPT
export default class SessionCacheService extends Service {
static scope = 'session'
constructor(ctx: Context) {
super(ctx, 'session-cache')
}
}
(2) ▶ 示例 2
graph LR
S1[Session A 创建] --> C1[CacheService A 实例]
S2[Session B 创建] --> C2[CacheService B 实例]
S1 --> D1[Session A 销毁 → CacheService A 销毁]
S2 --> D2[Session B 销毁 → CacheService B 销毁]
(3) 作用域声明
TYPESCRIPT
export default class SessionCacheService extends Service {
static scope = 'session'
// 或
// static scope = Symbol('session')
}
(4) 适用场景
- 会话级缓存
- 会话级配置覆盖
- 会话级工具集定制
- 会话历史记录
(5) 会话间的隔离效果
TEXT
📖 仅展示
Session A:
ctx.sessionCache.set('key', 'value-A')
Session B:
ctx.sessionCache.get('key') → undefined (隔离)
Session A:
ctx.sessionCache.get('key') → 'value-A' (本会话可访问)
4. 请求作用域(Request Scope)
(1) 概念
请求作用域为每次工具调用或 LLM 请求创建独立实例:
TYPESCRIPT
export default class RequestContextService extends Service {
static scope = 'request'
constructor(ctx: Context) {
super(ctx, 'request-context')
}
}
(2) 请求作用域的生命周期
graph LR
R1[请求 1 开始] --> S1[Service 实例 1]
R2[请求 2 开始] --> S2[Service 实例 2]
R1 --> E1[请求 1 完成 → 实例 1 销毁]
R2 --> E2[请求 2 完成 → 实例 2 销毁]
(3) 适用场景
- 请求级上下文信息(用户 IP、请求 ID)
- 请求级权限检查
- 请求级性能计时
- 请求级日志聚合
(4) 三种作用域对比
| 维度 | 全局 | 会话 | 请求 |
|---|---|---|---|
| 实例数 | 1 | 每会话 1 | 每请求 1 |
| 生命周期 | 进程存活 | 会话存活 | 请求存活 |
| 状态共享 | 全局共享 | 会话内共享 | 请求内独享 |
| 内存占用 | 低 | 中 | 高 |
| 适用 | 配置/连接池 | 会话缓存/历史 | 权限/计时 |
5. isolate realm 配置
(1) realm 概念
realm 是 Cordis 的隔离域——在同一个 DSH 实例中创建独立的插件空间:
YAML
# cordis.yml
realms:
agent-a:
isolate: ['cache', 'tools']
plugins:
my-tool-a:
$insert: ./plugins/tool-a
agent-b:
isolate: ['cache', 'tools']
plugins:
my-tool-b:
$insert: ./plugins/tool-b
(2) isolate 字段
isolate 列表指定哪些服务在 realm 内独立实例化:
YAML
realms:
my-realm:
isolate:
- cache # cache 服务独立实例
- tools # tools 服务独立实例
# 未列出的服务仍然全局共享
(3) realm 内的服务实例
TEXT
📖 仅展示
全局: llm (共享)
realm-a: cache (独立), tools (独立)
realm-b: cache (独立), tools (独立)
realm-a 中的插件 → ctx.cache = realm-a 的 cache
realm-b 中的插件 → ctx.cache = realm-b 的 cache
两者互不影响
(4) realm 的使用场景
| 场景 | 说明 |
|---|---|
| 多 Agent | 不同 Agent 有不同的工具集和缓存 |
| 多租户 | 不同租户的服务互相隔离 |
| 测试 | 测试 realm 不影响生产 realm |
| A/B 测试 | 两个 realm 使用不同的服务实现 |
(5) realm 间通信
realm 之间默认隔离,但可以通过全局服务通信:
TYPESCRIPT
// 全局服务(不受 isolate 影响)
export default class EventBusService extends Service {
constructor(ctx: Context) {
super(ctx, 'event-bus') // 未列入 isolate → 全局共享
}
emit(event: string, data: any) { /* ... */ }
on(event: string, handler: Function) { /* ... */ }
}
// realm-a 中的插件
ctx.eventBus.emit('data-updated', { source: 'realm-a' })
// realm-b 中的插件
ctx.eventBus.on('data-updated', (data) => {
ctx.logger.info(`received from ${data.source}`)
})
6. 作用域与 Agent preset
(1) Agent preset 概念
Agent preset 是预定义的 Agent 配置,包含工具集、模型参数和作用域设置:
YAML
presets:
coder:
model: deepseek-coder
tools: [file_edit, shell, search]
mode: standard
reviewer:
model: deepseek-chat
tools: [file_edit, search]
mode: minimal
isolate: [cache]
(2) preset 与作用域的关系
每个 preset 可以指定 isolate 列表,为该 Agent 创建隔离的服务实例:
TEXT
📖 仅展示
coder Agent: 共享 cache, 独立 tools
reviewer Agent: 独立 cache, 共享 tools
(3) 多 Agent 场景
YAML
# cordis.yml
agents:
coder:
preset: coder
isolate: [tools]
reviewer:
preset: reviewer
isolate: [tools, cache]
(4) Agent 间协作
graph TB
subgraph Global
LLM[llm Service]
EVENT[event-bus Service]
end
subgraph Agent-Coder
CT[tools Service]
CC[cache Service]
end
subgraph Agent-Reviewer
RT[tools Service]
RC[cache Service]
end
CT --> LLM
RT --> LLM
CT --> EVENT
RT --> EVENT
7. 多 Agent 环境下的服务隔离
(1) 隔离策略选择
TEXT
📖 仅展示
完全共享: 所有服务全局 → 简单但可能冲突
完全隔离: 所有服务独立 → 安全但资源浪费
混合隔离: 核心服务共享 + 业务服务隔离 → 平衡方案
推荐混合隔离:
| 服务类型 | 隔离策略 | 原因 |
|---|---|---|
| llm | 共享 | API 调用可复用 |
| sessions | 共享 | 会话管理统一 |
| tools | 隔离 | 各 Agent 工具集不同 |
| cache | 隔离 | 各 Agent 缓存独立 |
| fs | 共享 | 文件系统只有一个 |
(2) ▶ 示例 2
YAML
agents:
frontend-dev:
preset: coder
isolate: [tools, cache]
tools:
- file_edit
- shell
- search
config:
cache:
maxSize: 100
backend-dev:
preset: coder
isolate: [tools, cache]
tools:
- file_edit
- shell
- search
- database
config:
cache:
maxSize: 200
(3) 资源消耗评估
| 隔离级别 | 内存 | CPU | 连接数 |
|---|---|---|---|
| 全局共享 | 1x | 1x | 1x |
| 2 Agent 隔离 | ~2x | ~1.5x | ~2x |
| 5 Agent 隔离 | ~5x | ~3x | ~5x |
(4) 隔离泄漏检测
TYPESCRIPT
// 监控服务实例数量
ctx.on('service/created', (name, instance) => {
ctx.logger.info(`service created: ${name}, total instances: ${countInstances(name)}`)
})
// 如果某个服务的实例数远超 Agent 数,可能有泄漏
❓ 常见问题
Q 默认不声明 scope,服务是全局的吗?
A 是的。不声明
static scope 的 Service 默认全局单例。Q 会话作用域的 Service 在会话结束后会被回收吗?
A 会。会话销毁时,该会话的 Service 实例自动清理,包括 ctx.effect 注册的资源。
Q realm 的 isolate 列表可以动态修改吗?
A 不可以。realm 配置在启动时确定,修改需要重启。
Q 一个 Service 能同时存在于多个作用域吗?
A 不能。一个 Service 要么全局,要么会话级,要么请求级。如果需要跨作用域共享数据,用全局事件总线。
Q 请求作用域的性能开销大吗?
A 每次请求创建和销毁 Service 有开销。只在确实需要请求级隔离时使用,否则用全局或会话作用域。
Q 如何调试作用域问题?
A 在 Service 构造函数中打印实例 ID:
typescript constructor(ctx: Context) { super(ctx, 'cache') console.log(`cache instance created: ${this.id}, scope: ${this.scope}`) } ---📖 小节
- 三种作用域:全局(默认)、会话(每会话独立)、请求(每请求独立)
- 全局服务适合配置/连接池,会话服务适合缓存/历史,请求服务适合权限/计时
- isolate realm 配置为特定空间创建独立服务实例
- Agent preset 与作用域结合,实现多 Agent 的工具集和缓存隔离
- 推荐混合隔离策略:核心服务共享 + 业务服务隔离
- 作用域选择影响内存和性能,按需隔离,不要过度
📝 作业
1. ⭐ 基础题:编写一个会话作用域的 SessionStateService,为每个会话维护独立的键值存储。启动两个会话,验证数据互不干扰。
2. ⭐⭐ 进阶题:配置一个 realm,将 cache 和 tools 服务隔离。在 realm 内外分别访问 cache,验证得到不同的实例。
3. ⭐⭐⭐ 挑战题:设计一个双 Agent 系统:coder 和 reviewer。coder 有 file_edit/shell 工具,reviewer 只有 file_edit/search 工具。两者共享 llm 和 sessions,但缓存和工具集各自隔离。验证:coder 的 shell 工具 reviewer 不可见,两者缓存互不影响。