Profiles(配置档案)
按 provider 和模型打包默认配置,让 harness 自动适配不同模型行为
不修改 create_deep_agent 调用点,就能针对特定模型微调行为为什么需要它
你有没有在切换 LLM 模型时,发现 harness 行为全乱了?
硬编码的困境
- 换了 GPT-5,回复太长——没有按模型调 system prompt
- Claude 不支持 execute 工具——每个调用点都要手动排除
- 不同模型需要不同温度——散落在代码各处
- 想针对某模型加中间件——只能改 create_deep_agent
- 团队各用各的模型——配置没法复用
Profile 的解法
- 注册一个 profile,自动按模型匹配配置
- system prompt 追加、工具排除、中间件调整一键搞定
- provider 级别 + model 级别两级继承
- 配置可序列化为 YAML/JSON,团队共享
- 作为插件分发,无需手动注册
认识 HarnessProfile
描述 create_deep_agent 在构造聊天模型之后应用的 prompt、工具、中间件调整
说白了,HarnessProfile 就是一张"当选用某个模型时该怎么做"的清单。它不会改模型本身,而是调整 harness 的行为——追加 prompt、隐藏工具、增删中间件、配置通用子 Agent。在 JS/TS 里,它就是一个用对象字面量描述这些调整的配置对象。
import {
HarnessProfile,
registerHarnessProfile,
} from "deepagents";
// 为 openai:gpt-5.4 注册一个专属 profile
registerHarnessProfile(
"openai:gpt-5.4",
new HarnessProfile({
systemPromptSuffix: "Respond in under 100 words.", // 在 system prompt 末尾追加,限制回复长度
excludedTools: ["execute"], // 从工具集中移除 execute,防止该模型执行代码
excludedMiddleware: ["SummarizationMiddleware"], // 按名称排除中间件,阻止自动摘要介入
generalPurposeSubagent: { enabled: false }, // 禁用通用子 Agent
}),
);
HarnessProfile 能做什么
- baseSystemPrompt:替换整个基础 system prompt
- systemPromptSuffix:在 prompt 末尾追加文本
- toolDescriptionOverrides:覆盖工具的描述文字
- excludedTools:从工具集中移除指定工具
- excludedMiddleware:从堆栈中剥离指定中间件
- extraMiddleware:追加额外的中间件
- generalPurposeSubagent:配置通用子 Agent
适用场景一览
- 某模型回复过长 → 追加
systemPromptSuffix - 某模型不支持某工具 → 用
excludedTools移除 - 某模型不需要摘要中间件 → 用
excludedMiddleware - 不想用通用子 Agent → 设置
generalPurposeSubagent.enabled = false - 模型理解工具描述有偏差 → 覆盖描述
中间件排除规则
excludedMiddleware 接受两种形式,但有些中间件不能动
排除中间件时,你可以用中间件类精确匹配,也可以用字符串名称模糊匹配。
形式一:字符串名称
按 AgentMiddleware.name 匹配,适合内置和公共中间件。
new HarnessProfile({
// 按中间件名称排除,适合内置和公共中间件
excludedMiddleware: ["SummarizationMiddleware"],
});
形式二:类引用
直接传入中间件类(构造器)进行精确匹配,适合运行时代码。
import { TelemetryMiddleware } from "my-pkg/middleware.js";
new HarnessProfile({
// 传入类本身(而非字符串),匹配精确类
excludedMiddleware: [TelemetryMiddleware],
});
- 在
excludedMiddleware中列出FilesystemMiddleware、SubAgentMiddleware或内部权限中间件会抛出Error——它们是必需的基础设施 - 只想向模型隐藏工具而不移除中间件?用
excludedTools代替 - 类引用会在解析时真正
import对应模块——仅在可信的本地代码或受信配置中使用 SubAgentMiddleware(以及task工具)仅在存在至少一个同步子 Agent 时才会被附加,因此此配置可以干净地将其排除- 异步子 Agent 不受影响
注册键格式
两种粒度——provider 级别覆盖所有模型,model 级别只针对特定模型
Provider 级别
裸 provider 名称如 "openai",应用于该 provider 的所有模型。
registerHarnessProfile(
"openai",
new HarnessProfile({
// 所有使用 OpenAI 模型的 Agent 都会追加这条指令
systemPromptSuffix: "Be concise.",
}),
);
所有 OpenAI 模型(gpt-5.4、gpt-4o 等)都会追加 "Be concise."
Model 级别
完全限定的 "provider:model" 键,仅应用于特定模型。
registerHarnessProfile(
"openai:gpt-5.4",
new HarnessProfile({
// 只有 gpt-5.4 会排除 execute 工具
excludedTools: ["execute"],
}),
);
只有 openai:gpt-5.4 会排除 execute 工具。
create_deep_agent 调用点进行。
合并语义
当多个 profile 叠加时,每个字段的合并规则不同
理解合并语义是避免"配置被意外覆盖"的关键。下面是每个字段的具体行为:
| 字段 | 合并行为 | 举例 |
|---|---|---|
baseSystemPrompt |
新值优先,否则继承 | provider 设 "A",model 设 "B" → 结果是 "B" |
systemPromptSuffix |
新值优先,否则继承 | provider 设 "简短",model 未设 → 继承 "简短" |
toolDescriptionOverrides |
对象按键合并,新值优先 | 两者都覆盖 "grep" → model 的描述胜出 |
excludedTools |
数组合并去重(并集) | provider 排 ["A"],model 排 ["B"] → 排 ["A", "B"] |
excludedMiddleware |
数组合并去重(并集) | 同上,排除列表只会越来越多 |
extraMiddleware |
按类合并,新类追加 | 同类实例替换,新类追加到尾部 |
generalPurposeSubagent |
按字段合并 | 未设置的字段继承上层 |
initKwargs(provider) |
对象按键合并;共享键上新值优先 | provider 设 temperature=0,model 设 temperature=0.7 → model 胜出 |
preInit(provider) |
函数链式执行:现有先运行,然后新运行 | 两个 preInit 都会执行,按注册顺序依次调用 |
initKwargsFactory(provider) |
工厂链式执行,每次 resolveModel 调用时合并其输出 |
多个工厂的输出按键合并,新值优先 |
Provider 配置档案
更窄的配套 API,只管模型构造参数,不影响 harness
ProviderProfile 和 HarnessProfile 是互补关系:前者管"怎么构造模型",后者管"构造之后怎么调整 harness"。大多数场景你只需要 HarnessProfile。
import {
ProviderProfile,
registerProviderProfile,
} from "deepagents";
// 为 OpenAI 设置默认温度为 0
registerProviderProfile(
"openai",
// initKwargs 会被转发给 initChatModel,这里设置默认温度
new ProviderProfile({ initKwargs: { temperature: 0 } }),
);
ProviderProfile 的三个字段
- initKwargs:静态初始化参数,转发给
initChatModel - preInit:构造前运行的副作用(如凭据验证)
- initKwargsFactory:运行时动态生成 kwargs(如从环境变量读 headers)
什么时候用 ProviderProfile
- 需要为 provider 设置默认
temperature、max_tokens等参数 - 需要在模型构造前验证 API Key
- 需要从环境变量动态注入配置
- 你在打包一个 provider 集成库
ProviderProfile 仅在你传递 provider:model 字符串时生效。如果你直接传预配置的聊天模型实例,ProviderProfile 不会被使用。
从配置文件加载
用 JSON(或 YAML)管理 profile,团队协作更方便
运行时 API(HarnessProfile)支持中间件实例、工厂等复杂对象,但配置文件只能存声明式数据。HarnessProfileConfig 就是为此而生的——它镜像了 HarnessProfile 的可序列化子集。
registerHarnessProfile 同时接受 HarnessProfile 和 HarnessProfileConfig 两种类型,因此配置文件支持的调用者不需要手动转换步骤——直接传入即可。
JSON 配置文件
// openai.json
{
"baseSystemPrompt": "You are helpful.",
"systemPromptSuffix": "Respond briefly.",
"excludedTools": ["execute", "grep"],
"excludedMiddleware": ["SummarizationMiddleware"],
"generalPurposeSubagent": { "enabled": false }
}
JS 加载代码
import { readFileSync } from "node:fs";
import {
HarnessProfileConfig,
registerHarnessProfile,
} from "deepagents";
// 读取 JSON 配置并注册
const raw = readFileSync("openai.json", "utf8");
registerHarnessProfile(
"openai",
// HarnessProfileConfig 直接接受普通对象(JSON.parse 的输出)
new HarnessProfileConfig(JSON.parse(raw)),
);
npm install yaml,把上面的 JSON.parse(raw) 换成 parse(raw)(来自 yaml 包)。配置文件里同样使用 camelCase 键名。
HarnessProfileConfig.fromHarnessProfile(...) 可以把运行时 profile 导出回声明式形式。但注意:非空的 extraMiddleware 和匿名(未命名)的中间件类无法序列化,导出时会抛 Error。
配置文件 vs 运行时
不是所有功能都能放进 JSON——搞清楚边界
HarnessProfileConfig(可序列化)
- prompt 文本(
baseSystemPrompt、systemPromptSuffix) - 工具描述覆盖(
toolDescriptionOverrides) - 排除的工具和中间件(字符串形式)
- 通用子 Agent 配置(
enabled、name等) - 可以用 JSON / YAML 存储
HarnessProfile(仅运行时)
- 中间件实例(
extraMiddleware) - 工厂函数(
initKwargsFactory) - 类形式的
excludedMiddleware条目 - 需要在 JS/TS 代码中注册
- 不能用纯 JSON 表示
extraMiddleware 实例或未命名的中间件类的 profile 时,fromHarnessProfile 会抛出 Error。设计 profile 时就要考虑:哪些内容放配置文件,哪些只能在代码中注册。
作为插件分发
导出 register 函数让使用者一行 import 自动注册
JS/TS 没有 Python 那种 importlib.metadata 入口点机制,deepagents 采用更直接的方式:把注册函数作为模块的副作用导出。加载顺序是:内置 → npm 包(被 import 时执行)→ 用户代码。
package.json 暴露入口
// my-deepagents-profiles/package.json
{
"name": "my-deepagents-profiles",
"version": "1.0.0",
"type": "module",
"exports": {
".": "./src/index.js",
"./register": "./src/register.js"
}
}
注册模块(src/register.js)
import {
HarnessProfile,
ProviderProfile,
registerHarnessProfile,
registerProviderProfile,
} from "deepagents";
// 用户 import 本模块时自动执行
registerHarnessProfile(
"my_provider",
new HarnessProfile({
// 指示该 provider 的模型批量并发执行独立工具调用
systemPromptSuffix:
"Batch independent tool calls in parallel.",
}),
);
registerProviderProfile(
"my_provider",
new ProviderProfile({
initKwargs: { temperature: 0 }, // 默认温度 0,输出更确定
}),
);
// 也可以命名导出,让用户按需手动调用
export function registerMyProvider() {
/* 上面两段注册逻辑 */
}
import "my-deepagents-profiles/register";(副作用导入即触发注册),或显式调用 import { registerMyProvider } from "my-deepagents-profiles"; registerMyProvider();。无需修改 createDeepAgent 调用点。
Prompt 拼装优先级
理解调用者、profile 和子 Agent 的 prompt 如何叠加
profile 的 prompt 设置和调用者的 prompt 设置各司其职:调用者在前,profile 在后,子 Agent 会重新解析。
主 Agent prompt 拼装顺序
- 调用者提供的
systemPrompt:始终在最前面 - Deep Agents 基础 prompt(或 profile 的
baseSystemPrompt替换) - Profile 的
systemPromptSuffix:始终在最后面
子 Agent prompt 规则
- 每个子 Agent 针对自身的模型重新运行 profile 解析
- 通用子 Agent 专用的 prompt 优先级高于
baseSystemPrompt - 相同的覆盖规则适用于声明式子 Agent 和自动子 Agent
provider:identifier → ② 仅 identifier(当 identifier 含 : 时) → ③ 仅 provider 回退。确保你的注册键覆盖了实际使用的模型标识。
动手练习
为你的项目设计一套 profile 配置方案
假设你的团队同时使用 OpenAI 和 Anthropic 的模型,请根据以下需求设计 profile:
需求:
1. 所有 OpenAI 模型:回复简洁(不超过 200 字)
2. openai:gpt-5.4 额外排除 execute 工具
3. 所有 Anthropic 模型:排除 grep 工具,追加 systemPromptSuffix "Think step by step."
4. anthropic:claude-sonnet-4-20250514 禁用通用子 Agent
任务 1:设计注册键
- 需要注册几个 profile?分别用什么键?
- 哪些字段在 provider 级别设置?哪些在 model 级别?
- 合并后每个模型实际拿到什么配置?
任务 2:选择加载方式
- 哪些配置适合放 JSON 文件?
- 哪些只能在 JS/TS 代码中注册?
- 如何把配置组织成可复用的模块?
参考答案
一种可行的 profile 配置方案——合并语义决定了最终效果
import {
HarnessProfile,
registerHarnessProfile,
} from "deepagents";
// Provider 级别:所有 OpenAI 模型
registerHarnessProfile(
"openai",
new HarnessProfile({
systemPromptSuffix: "Respond in under 200 words.", // 所有 OpenAI 模型追加此指令
}),
);
// Model 级别:gpt-5.4 在 provider 基础上追加排除
registerHarnessProfile(
"openai:gpt-5.4",
new HarnessProfile({
excludedTools: ["execute"], // 与 provider 级别的 excludedTools 取并集
}),
);
// Provider 级别:所有 Anthropic 模型
registerHarnessProfile(
"anthropic",
new HarnessProfile({
excludedTools: ["grep"], // 排除 grep 工具
systemPromptSuffix: "Think step by step.", // 引导模型逐步推理
}),
);
// Model 级别:claude-sonnet-4 禁用通用子 Agent
registerHarnessProfile(
"anthropic:claude-sonnet-4-20250514",
new HarnessProfile({
generalPurposeSubagent: { enabled: false },
}),
);
本章回顾
到这里,你已经掌握了 Profiles 的完整体系
你学到了什么
- HarnessProfile:在模型构造后调整 prompt、工具、中间件和子 Agent
- ProviderProfile:控制模型构造参数(温度、凭据、动态 kwargs)
- 注册键:provider 级别(全模型)和 model 级别(单模型)两级继承
- 合并语义:prompt 覆盖、集合并集、映射按键合并
- 配置文件:YAML/JSON 存可序列化部分,
HarnessProfileConfig负责转换 - 插件分发:通过入口点自动注册,分层叠加
后续章节预告
- Event Streaming:监听 Agent 执行过程中的实时事件
- Streaming:token 级流式输出和自定义数据流
- Frontend 集成:在 Web 前端展示 Agent 状态
- ACP 协议:Agent Communication Protocol 的使用
- Code 模式:面向代码生成的专用配置
延伸阅读
继续本课程
- 下一章:Event Streaming — 监听 Agent 执行的实时事件流
- Harness 概览 — 理解 profile 所在的完整 harness 架构
- Models — 配置模型 provider 和参数
相关主题
- Customization — 完整的 create_deep_agent 配置项
- Subagents — 通用子 Agent 的详细配置
- 认识 Deep Agents — 回到起点重温全局视角