Integration Patterns
Agent 与 Federation AIService
理解 Agent、Downcity、City 和 AIService 在模型管理上的职责边界,以及集成时应该怎么用
Agent 与 Federation AIService
如果你在正常的 Downcity 项目里使用 Agent,模型通常不应该由 Agent 项目自己持有。
这时要把职责拆成四层:
Federation AIService:拥有模型目录、AIChannel、API key、模型路由Downcity:连接 City,读取模型目录,校验和解析modelIdAgent 项目:只声明execution.modelId@downcity/agentsession:真正执行 prompt
最重要的结论
在 Downcity 集成模式里:
- CLI 全局数据库保存的是
modelId - 不是 provider 配置
- 不是 API key
- 也不是本地模型实例
也就是说,下面这种心智模型才是对的:
global downcity.db: agent.execution.modelId
-> Downcity 读取并校验
-> City SDK 创建可执行 CityModel
-> Agent 直接调用 CityModel.doStream()
-> Federation AIService 路由到 AIChannel 与上游协议为什么要这样分层
这样做的好处是:
- 多个 Agent 可以复用同一套模型目录
- Channel key 不进入项目目录
- Channel 切换、模型增删、默认模型策略都可以在 City 侧统一管理
这也是 Downcity 当前真实实现的边界:
- Downcity 不拥有模型池
- Downcity 不解析 Channel、apiKey 或 base URL
- 模型目录唯一来源是 Federation AIService
Agent 项目里实际写什么
项目里真正的绑定点是:
{
"execution": {
"type": "api",
"modelId": "quality"
}
}也就是:
execution.type = "api"execution.modelId = "<some-id>"
City 侧如何提供模型
City 侧通常会这样注册模型:
import { createDeepSeek } from "@ai-sdk/deepseek";
import {
Federation,
AIService,
AIChannel,
read_required_env,
type AIChannelStreamInput,
type LanguageModelV3StreamResult,
} from "@downcity/federation";
const base = new Federation({ database });
class DeepSeekChannel extends AIChannel {
constructor() {
super({
id: "deepseek",
base_url: "https://api.deepseek.com/v1",
env_key: "DEEPSEEK_API_KEY",
ai_sdk_provider_id: "deepseek",
});
}
protected async stream(
input: AIChannelStreamInput,
): Promise<LanguageModelV3StreamResult> {
const deepseek = createDeepSeek({
apiKey: read_required_env(input, this.env_key ?? ""),
baseURL: this.base_url,
});
const model = deepseek(input.model.upstream_model);
return model.doStream(input.call);
}
}
const deepseek = new DeepSeekChannel();
const ai = new AIService();
ai.use(
deepseek.model({
id: "quality",
upstream_model: "deepseek-chat",
name: "DeepSeek Quality",
}),
);
base.use(ai);注册后,Federation AIService 就拥有了:
- 模型目录
- SDK 通路
- CityModel 原生 LanguageModelV3 通路
- OpenAI 兼容通路
Downcity 如何读取和校验模型
Downcity 启动 Agent 项目前,会去 Federation AIService 读取模型目录并校验 modelId。
当前真实实现大致是:
- 读取 City 连接配置
- 通过 User City 调
city.ai.catalog() - 检查
execution.modelId是否存在 - 从目录取得已实现
LanguageModelV3的CityModel
这也是为什么:
execution.modelId写错时,问题通常会在启动或首次执行阶段暴露- 而不是拖到对话跑了很多轮才发现
什么时候该用 session.set({ model })
应该用
适用于纯 SDK 嵌入模式:
- 你自己在 Node 应用里
new Agent(...) - 你自己持有模型实例
- 你不是通过 Downcity 去绑定 City 模型
不应该作为默认路径
在正常的 Downcity Agent 项目里,不应该把 session.set({ model }) 当默认用法。
这时应优先:
- 在 Federation AIService 注册模型
- 在项目里写
execution.modelId
一个完整的最短链路
1. City 注册模型
const ai = new AIService();
ai.use(channel.model({ id: "quality", upstream_model: "vendor-quality", name: "Quality" }));
base.use(ai);2. Downcity 连接 City
downcity federation use
downcity federation status3. Agent 项目绑定 modelId
{
"execution": {
"type": "api",
"modelId": "quality"
}
}4. 启动或重启 Agent
downcity agent restart .如果要远程访问 Agent
要区分两层:
- 模型能力:来自 Federation AIService
- session 能力:由本地 Agent 对外暴露
所以 RemoteAgent 连到的是:
- 本地 Agent 暴露出的 session API
不是直接去连 Federation AIService 模型本身。
常见误区
误区 1:Agent 项目里应该保存 Channel 配置
不是。项目里只保存 modelId。
误区 2:RemoteAgent 可以远程传 session.set({ model })
不是。远程 session 当前不支持客户端直接传本地模型实例。
误区 3:Downcity 自己维护模型目录
不是。Downcity 只读取和校验 Federation AIService 暴露出的模型目录。