@faapi/agent 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  - **LLM Provider 抽象**(Phase 3.2)—— 统一接口对接 OpenAI / Anthropic 等 provider
8
8
  - **ReAct 循环引擎**(Phase 3.3)—— LLM 输出 → 调用 tool / 递归 sub-agent → 把结果回灌给 LLM,循环直到最终回答
9
- - **Agent 类**(Phase 3.4)—— 按 `agent.name` 查找元数据、加载 handler、组装 agent.tools + defaultTools + sub-agent,提供 `run` / `stream` / `asTool`
9
+ - **Agent 类**(Phase 3.4)—— 按 `agent.name` 查找元数据、加载 handler、组装 agent.tools + sub-agent,提供 `run` / `stream` / `asTool`
10
10
  - **递归防护** —— `maxTurns` + `maxAgentDepth`(来自 `config.agent` 或 agent 自身 `config` 块)
11
11
 
12
12
  ## 与 faapi 核心的关系
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { LlmConfig, AgentMetadata, ToolMetadata, ToolModule, AgentModule, AgentToolDescriptor, FaapiPlugin } from '@faapi/faapi';
1
+ import { LlmConfig, AgentToolDescriptor, AgentCore, AgentMetadata, ToolMetadata, ToolModule, AgentModule, FaapiPlugin } from '@faapi/faapi';
2
2
 
3
3
  /**
4
4
  * LLM Provider 错误
@@ -20,7 +20,8 @@ declare class LLMProviderError extends Error {
20
20
  /**
21
21
  * 创建 OpenAI 兼容 LLMProvider
22
22
  *
23
- * @param config LLM 提供方配置(apiKey / model / baseURL / 透传字段)
23
+ * @param config LLM 提供方配置(apiKey / baseURL / models + provider 级透传字段,
24
+ * model 级字段在 `models[modelName]` 下覆盖 provider 级同名字段)
24
25
  * @returns LLMProvider 实例(含 complete + stream 方法)
25
26
  */
26
27
  declare function createOpenAIProvider(config: LlmConfig): LLMProvider;
@@ -170,12 +171,134 @@ interface LLMProvider {
170
171
  *
171
172
  * Phase 3.2 仅支持 `'openai'`,其他值抛错不静默降级(参考 AGENTS.md §6.3)。
172
173
  *
173
- * @param config LLM 提供方配置(来自 faapi.config.ts 的 `agent.llm`)
174
+ * @param config LLM 提供方配置(来自 faapi.config.ts 的 `agent.llms[key]`)
174
175
  * @returns LLMProvider 实例
175
176
  * @throws {Error} 当 `config.provider` 不是已支持的值
176
177
  */
177
178
  declare function createProvider(config: LlmConfig): LLMProvider;
178
179
 
180
+ /**
181
+ * 单次 agent.run() / agent.stream() 的结构化调用明细
182
+ *
183
+ * 默认开启(`enableTracing` 默认 `true`),业务方在生产主路径显式
184
+ * `enableTracing: false` 关闭以零开销运行。详见 [trace.md](./trace.md)。
185
+ */
186
+ interface AgentTrace {
187
+ /** agent 名 */
188
+ agentName: string;
189
+ /** 开始时间(performance.now() ms,相对进程启动,便于算相对耗时) */
190
+ startedAt: number;
191
+ /** 总耗时 ms(done 时填充,抛错时也填充) */
192
+ durationMs?: number;
193
+ /** 总轮数(与 ReactLoopResult.turns 一致) */
194
+ turns: number;
195
+ /** 累计 token 用量(与 ReactLoopResult.usage 一致) */
196
+ usage?: LLMUsage;
197
+ /** 最终停止原因(与 ReactLoopResult.stopReason 一致) */
198
+ stopReason?: LLMStopReason;
199
+ /** 最终 assistant 内容(与 ReactLoopResult.content 一致) */
200
+ content?: string;
201
+ /** agent 抛错时的错误消息(agent.run 抛错时填充) */
202
+ error?: string;
203
+ /** 按发生顺序的事件列表 */
204
+ events: AgentTraceEvent[];
205
+ }
206
+ /**
207
+ * trace 事件(discriminated union,按 `type` 区分)
208
+ *
209
+ * 一次 agent.run() 内的事件序列示例:
210
+ * llm_call(turn=1) → tool_call(turn=1) → llm_call(turn=2) → done
211
+ *
212
+ * sub-agent 调用作为 `subagent_call` 事件,内嵌 sub-trace(递归结构)。
213
+ */
214
+ type AgentTraceEvent = LlmCallEvent | ToolCallEvent | SubAgentCallEvent;
215
+ /**
216
+ * LLM 调用事件——每轮调 provider.complete() / provider.stream() 触发一次
217
+ */
218
+ interface LlmCallEvent {
219
+ type: 'llm_call';
220
+ /** 第几轮(从 1 开始) */
221
+ turn: number;
222
+ startedAt: number;
223
+ durationMs?: number;
224
+ /** 该轮调用的 model 名 */
225
+ model: string;
226
+ /** 该轮发给 LLM 的输入消息快照(浅拷贝数组,消息对象引用共享) */
227
+ inputMessages: LLMMessage[];
228
+ /** 该轮 LLM 返回的 assistant 消息(含 toolCalls 若有) */
229
+ response: LLMMessage;
230
+ /** 该轮的停止原因 */
231
+ stopReason: LLMStopReason;
232
+ /** 该轮的 token 用量(provider 不返回时 undefined) */
233
+ usage?: LLMUsage;
234
+ }
235
+ /**
236
+ * 常规 tool 调用事件——executeTool 返回 unknown 时触发
237
+ */
238
+ interface ToolCallEvent {
239
+ type: 'tool_call';
240
+ turn: number;
241
+ startedAt: number;
242
+ durationMs?: number;
243
+ /** LLM 分配的 tool_call_id(与 messages 里 tool 消息的 toolCallId 一致) */
244
+ toolCallId: string;
245
+ /** tool 名 */
246
+ name: string;
247
+ /** tool 参数(已 JSON.parse 的对象) */
248
+ arguments: Record<string, unknown>;
249
+ /** tool 执行结果(stringifyResult 后的字符串,与 messages 里 tool 消息一致) */
250
+ result: string;
251
+ /** tool 抛错时填充(result 为 stringifyError 后的字符串) */
252
+ error?: string;
253
+ }
254
+ /**
255
+ * sub-agent 调用事件——executeTool 返回 TracingToolResult 时触发
256
+ *
257
+ * sub-agent 的 trace 嵌入 `trace` 字段(递归结构),业务方可还原完整调用树。
258
+ */
259
+ interface SubAgentCallEvent {
260
+ type: 'subagent_call';
261
+ turn: number;
262
+ startedAt: number;
263
+ durationMs?: number;
264
+ toolCallId: string;
265
+ /** 被调用的 sub-agent 名 */
266
+ agentName: string;
267
+ /** 喂给 sub-agent 的输入(args JSON.stringify 后) */
268
+ input: string;
269
+ /** sub-agent 自己的 trace(递归) */
270
+ trace: AgentTrace;
271
+ /** sub-agent 返回的最终内容(sub-trace.content 的副本,便于不展开 sub-trace 也能读到结果) */
272
+ result?: string;
273
+ /** sub-agent 抛错时填充 */
274
+ error?: string;
275
+ }
276
+ /**
277
+ * sub-agent 调用的特殊返回值——reactLoop 据此识别 sub-agent 调用并发出 subagent_call 事件
278
+ *
279
+ * [Agent.executeSubAgent](./agent.md) 在 `enableTracing=true` 时返回此类型;
280
+ * `enableTracing=false` 时返回 `unknown`(与常规 tool 一致)。
281
+ *
282
+ * `__trace` 是标记字段,避免与普通对象返回值冲突。reactLoop 通过
283
+ * `typeof result === 'object' && result !== null && result.__trace === true`
284
+ * 判断是否为 TracingToolResult。
285
+ */
286
+ interface TracingToolResult {
287
+ /** 标记字段(避免与普通对象返回值冲突) */
288
+ __trace: true;
289
+ /** sub-agent 返回的业务结果(stringifyResult 后会作为 tool 消息内容) */
290
+ result: unknown;
291
+ /** sub-agent 自己的 trace */
292
+ trace: AgentTrace;
293
+ }
294
+ /**
295
+ * 类型守卫:判断 ToolExecutor 返回值是否为 TracingToolResult
296
+ *
297
+ * reactLoop 用此函数区分 sub-agent 调用(发出 subagent_call 事件)
298
+ * 与常规 tool 调用(发出 tool_call 事件)。
299
+ */
300
+ declare function isTracingToolResult(value: unknown): value is TracingToolResult;
301
+
179
302
  /**
180
303
  * ReAct(Reasoning + Acting)循环引擎
181
304
  *
@@ -192,8 +315,11 @@ declare function createProvider(config: LlmConfig): LLMProvider;
192
315
  * - agent-as-tool(`agent.` 前缀)→ 递归调子 agent 的 reactLoop(含 `maxAgentDepth` 防护)
193
316
  *
194
317
  * 返回值可以是任意类型——非 string 会被 JSON.stringify 后回传 LLM。
318
+ *
319
+ * sub-agent 调用时,若 `enableTracing=true`,返回 [TracingToolResult](./trace.md)
320
+ * 携带 sub-agent 的 trace,reactLoop 据此发出 `subagent_call` 事件(嵌套递归 trace)。
195
321
  */
196
- type ToolExecutor = (name: string, args: Record<string, unknown>) => Promise<unknown>;
322
+ type ToolExecutor = (name: string, args: Record<string, unknown>) => Promise<unknown | TracingToolResult>;
197
323
  /**
198
324
  * reactLoop 配置
199
325
  *
@@ -216,6 +342,13 @@ interface ReactLoopConfig {
216
342
  temperature?: number;
217
343
  /** 最大生成 token 数 */
218
344
  maxTokens?: number;
345
+ /**
346
+ * 启用 tracing(默认 true)。开启时填充 `ReactLoopResult.trace` /
347
+ * `ReactLoopStreamChunk.traceEvent`,详见 [trace.md](./trace.md)。
348
+ *
349
+ * 业务方在生产主路径显式传 `false` 关闭以零开销运行。
350
+ */
351
+ enableTracing?: boolean;
219
352
  }
220
353
  /**
221
354
  * 非流式循环结果
@@ -231,6 +364,11 @@ interface ReactLoopResult {
231
364
  stopReason: LLMStopReason;
232
365
  /** 累计 token 用量(多轮累加,provider 不返回时为 `undefined`) */
233
366
  usage?: LLMUsage;
367
+ /**
368
+ * 结构化调用明细(`enableTracing=true` 时填充,否则 `undefined` 零开销)。
369
+ * 详见 [trace.md](./trace.md)。
370
+ */
371
+ trace?: AgentTrace;
234
372
  }
235
373
  /**
236
374
  * 流式循环的单个 chunk
@@ -239,6 +377,7 @@ interface ReactLoopResult {
239
377
  * - `deltaContent` — LLM 增量 token(多次 yield)
240
378
  * - `toolCall` — tool 开始执行
241
379
  * - `toolResult` — tool 执行完成
380
+ * - `traceEvent` — trace 事件(`enableTracing=true` 时增量推送,与上述字段互斥)
242
381
  * - `done` — 循环结束(只 yield 一次)
243
382
  */
244
383
  interface ReactLoopStreamChunk {
@@ -254,6 +393,11 @@ interface ReactLoopStreamChunk {
254
393
  name: string;
255
394
  result: string;
256
395
  };
396
+ /**
397
+ * trace 事件(`enableTracing=true` 时增量推送)。
398
+ * 与 deltaContent / toolCall / toolResult / done 互斥,一个 chunk 至多一个字段。
399
+ */
400
+ traceEvent?: AgentTraceEvent;
257
401
  /** 循环结束 */
258
402
  done?: {
259
403
  content: string;
@@ -297,6 +441,119 @@ declare function reactLoop(input: string, config: ReactLoopConfig): Promise<Reac
297
441
  */
298
442
  declare function reactLoopStream(input: string, config: ReactLoopConfig): AsyncIterable<ReactLoopStreamChunk>;
299
443
 
444
+ /**
445
+ * agent handle——注入到 handler 的 `agent` 参数,提供可调用的 agent 运行入口
446
+ *
447
+ * faapi 核心的 [agentHandle](../../faapi/src/injection/agentHandle.md) 工厂注册机制
448
+ * 让本包的 [plugin](./plugin.md) 在 setup 时注册工厂函数,injectParams 在
449
+ * `agent` 参数注入时调工厂拿到 `AgentHandle` 实例。
450
+ *
451
+ * `Agent` 类满足此接口(结构化类型),plugin 的工厂直接返回 `Agent` 实例,
452
+ * 无需额外包装层。handler 通过 `AgentHandle` 类型获得类型安全:
453
+ *
454
+ * ```ts
455
+ * import type { AgentHandle } from '@faapi/agent';
456
+ *
457
+ * // src/api/chat/handler.ts
458
+ * export function POST(agent: AgentHandle, body: { input: string }) {
459
+ * const result = await agent.run(body.input);
460
+ * return { content: result.content, turns: result.turns };
461
+ * }
462
+ * ```
463
+ *
464
+ * 工厂未注册(`@faapi/agent` 插件未加载或 `config.agent.llm` / `defaultAgent` 未配置)
465
+ * 时注入 `undefined`,handler 需自行处理。
466
+ *
467
+ * 详见 [agentHandle.md](./agentHandle.md)。
468
+ */
469
+ /**
470
+ * `agent.run` / `agent.stream` 的 options 参数——临时覆盖本次调用的 LLM 配置
471
+ *
472
+ * 所有字段可选,不传或 `undefined` 时回落到下一优先级(agent 元数据 → 全局配置)。
473
+ * **不修改 agent 自身状态**——下一次调用仍用默认配置。
474
+ *
475
+ * `model` 是字符串 key,支持三种形式(解析规则见 [agentHandle.md](./agentHandle.md) 的
476
+ * 「`options.model` 字符串 key 解析规则」):
477
+ * - llms 的 key 精确匹配(如 `'openai'`)
478
+ * - `provider/model` 一体化(如 `'openai/gpt-4o'`)
479
+ * - 纯 model 名(如 `'gpt-4o'`)—— 在所有 provider 的 `models` 里查找,唯一时切到对应 provider
480
+ *
481
+ * 优先级(高 → 低):`options` > agent 元数据(`config.model` / `config.maxTurns`)> 全局
482
+ * `AgentRuntimeConfig` / `defaultLlm` provider。详见 [agentHandle.md](./agentHandle.md) 的
483
+ * Run-level 覆盖优先级表。
484
+ *
485
+ * @example
486
+ * ```ts
487
+ * // 按请求切模型(纯 model 名,在 llms 里唯一时切到对应 provider)
488
+ * await agent.run(input, { model: 'gpt-4o-mini' });
489
+ *
490
+ * // provider/model 一体化形式(精确切换)
491
+ * await agent.run(input, { model: 'anthropic/claude-3-5-sonnet' });
492
+ * ```
493
+ */
494
+ interface AgentRunOptions {
495
+ /**
496
+ * 切换 provider + model 的字符串 key(支持 llms key / `provider/model` / 纯 model 名)
497
+ *
498
+ * 不传时用 `defaultLlm` provider + agent 元数据 `config.model`。
499
+ */
500
+ model?: string;
501
+ /** 采样温度(透传给 LLM API,覆盖 provider/model 级 temperature) */
502
+ temperature?: number;
503
+ /** 最大生成 token 数(透传给 LLM API) */
504
+ maxTokens?: number;
505
+ /**
506
+ * 启用 tracing(默认沿用全局 `config.agent.enableTracing`,全局默认 `true`)。
507
+ *
508
+ * 开启时 `ReactLoopResult.trace` / `ReactLoopStreamChunk.traceEvent` 填充
509
+ * 结构化调用明细,详见 [trace.md](./trace.md)。
510
+ *
511
+ * 业务方在生产主路径显式传 `false` 关闭以零开销运行。
512
+ */
513
+ enableTracing?: boolean;
514
+ }
515
+ interface AgentHandle {
516
+ /**
517
+ * 非流式执行 agent
518
+ *
519
+ * 组装 ReAct 循环 config(systemPrompt + tools + maxTurns + 应用 `options` 覆盖)→ 调
520
+ * [reactLoop](./reactLoop.md) → 返回最终结果。
521
+ *
522
+ * @param input 用户输入文本
523
+ * @param options 临时覆盖本次调用的 model(字符串 key)/ temperature / maxTokens
524
+ * (不修改 agent 自身状态,详见 {@link AgentRunOptions})
525
+ * @returns 循环结果(content + messages + turns + stopReason + usage)
526
+ * @throws {AgentError} agent 未注册
527
+ * @throws {ReactLoopError} 超出 maxTurns
528
+ * @throws {Error} LLM provider 抛错时立即传播
529
+ */
530
+ run(input: string, options?: AgentRunOptions): Promise<ReactLoopResult>;
531
+ /**
532
+ * 流式执行 agent
533
+ *
534
+ * 组装 config(应用 `options` 覆盖)→ 调 [reactLoopStream](./reactLoop.md) → yield 流式 chunk。
535
+ * 适用于 LLM token 流式输出、tool 调用过程展示等场景。
536
+ *
537
+ * @param input 用户输入文本
538
+ * @param options 临时覆盖本次调用的 model(字符串 key)/ temperature / maxTokens
539
+ * (不修改 agent 自身状态,详见 {@link AgentRunOptions})
540
+ * @yields 流式 chunk(deltaContent / toolCall / toolResult / done)
541
+ * @throws {AgentError} agent 未注册
542
+ * @throws {ReactLoopError} 超出 maxTurns
543
+ * @throws {Error} LLM provider 抛错时立即传播
544
+ */
545
+ stream(input: string, options?: AgentRunOptions): AsyncIterable<ReactLoopStreamChunk>;
546
+ /**
547
+ * 把自身包装为 `AgentToolDescriptor` 供 LLM 当 tool 调用
548
+ *
549
+ * 用于 agent-as-tool 场景:父 agent 把子 agent 包装为 tool,
550
+ * 加入 LLM 可见 tool 列表,LLM 调用时触发 sub-agent 递归执行。
551
+ *
552
+ * @returns `AgentToolDescriptor` 或 `undefined`(agent 未注册)
553
+ */
554
+ asTool(): AgentToolDescriptor | undefined;
555
+ }
556
+
300
557
  /**
301
558
  * 全局 agent 配置覆盖
302
559
  *
@@ -308,8 +565,16 @@ interface AgentRuntimeConfig {
308
565
  maxTurns?: number;
309
566
  /** agent 调用 agent 的最大递归深度(默认 3) */
310
567
  maxAgentDepth?: number;
311
- /** 默认 tool 列表,所有 agent 都可用 */
312
- defaultTools?: string[];
568
+ /**
569
+ * 启用 tracing 的全局默认值(默认 true)。
570
+ *
571
+ * 开启时 `ReactLoopResult.trace` / `ReactLoopStreamChunk.traceEvent` 填充
572
+ * 结构化调用明细,详见 [trace.md](./trace.md)。
573
+ *
574
+ * 单次调用可通过 `AgentRunOptions.enableTracing` 覆盖。
575
+ * 业务方在生产主路径显式设 `false` 关闭以零开销运行。
576
+ */
577
+ enableTracing?: boolean;
313
578
  }
314
579
  /**
315
580
  * tool schema 解析结果
@@ -342,26 +607,34 @@ interface ToolSchemaResolution {
342
607
  * 访问器签名与 faapi 核心对称(见 [agent.md](./agent.md) 依赖注入章节)。
343
608
  */
344
609
  interface AgentDeps {
345
- /** LLM provider 实例 */
346
- provider: LLMProvider;
610
+ /** LLM provider 实例映射(key 是 provider 名,来自 config.agent.llms) */
611
+ providers: Map<string, LLMProvider>;
612
+ /** 默认 provider 实例(config.agent.defaultLlm 对应,或 llms 第一个 key) */
613
+ defaultProvider: LLMProvider;
614
+ /** LLM provider 配置映射(含 models,用于 options.model key 解析) */
615
+ llms: Record<string, LlmConfig>;
616
+ /** 默认 provider key(config.agent.defaultLlm,或 llms 第一个 key) */
617
+ defaultLlm: string;
347
618
  /** 当前 agent 名 */
348
619
  agentName: string;
349
620
  /** 项目根目录(Phase 3.5 接线时用于加载器) */
350
621
  rootDir: string;
351
622
  /** 全局 agent 配置覆盖 */
352
623
  config?: AgentRuntimeConfig;
353
- /** 查 agent 元数据(对应 agentRegistry.getAgent) */
354
- getAgent: (name: string) => AgentMetadata | undefined;
624
+ /** 查 agent LLM 可见元数据(对应 agentRegistry.getAgent,返回 AgentCore) */
625
+ getAgent: (name: string) => AgentCore | undefined;
626
+ /** 查 agent 完整元数据(对应 agentRegistry.getAgentEntry,返回 AgentMetadata 含 filePath/hasRun) */
627
+ getAgentEntry: (name: string) => AgentMetadata | undefined;
355
628
  /** 查 tool 元数据(对应 toolRegistry.getTool) */
356
629
  getTool: (name: string) => ToolMetadata | undefined;
357
630
  /** 解析 agent 可用常规 tool(对应 agentRegistry.resolveAgentTools) */
358
631
  resolveAgentTools: (name: string) => ToolMetadata[];
359
- /** 解析 agent 可调用 sub-agent 列表(对应 agentRegistry.resolveSubAgents) */
360
- resolveSubAgents: (name: string) => AgentMetadata[];
632
+ /** 解析 agent 可调用 sub-agent 列表(对应 agentRegistry.resolveSubAgents,返回 AgentCore[]) */
633
+ resolveSubAgents: (name: string) => AgentCore[];
361
634
  /** 动态 import tool handler(对应 loadToolModule) */
362
635
  loadToolModule: (filePath: string, functionName: string) => Promise<ToolModule>;
363
- /** 动态 import agent handler(对应 loadAgentModule) */
364
- loadAgentModule: (filePath: string, hasConfig: boolean, hasRun: boolean) => Promise<AgentModule>;
636
+ /** 动态 import agent handler(对应 loadAgentModule,仅 hasRun 参数,无 hasConfig) */
637
+ loadAgentModule: (filePath: string, hasRun: boolean) => Promise<AgentModule>;
365
638
  /** tool input 的 schema 解析(Phase 3.5 实现,可选) */
366
639
  resolveToolSchema?: (tool: ToolMetadata) => Promise<ToolSchemaResolution | undefined>;
367
640
  }
@@ -406,30 +679,37 @@ declare class Agent {
406
679
  */
407
680
  private readonly schemaCache;
408
681
  /**
409
- * @param deps 运行时依赖(访问器 + provider + config)
682
+ * @param deps 运行时依赖(访问器 + providers Map + defaultProvider + llms + config)
410
683
  * @param depth 递归深度(默认 1 = 根 agent;sub-agent 递归时传入 depth+1)
411
684
  */
412
685
  constructor(deps: AgentDeps, depth?: number);
413
686
  /**
414
687
  * 非流式执行——组装 config 调 [reactLoop](./reactLoop.md)
415
688
  *
689
+ * reactLoop 不知 agent 名(只关心循环逻辑),返回的 `result.trace.agentName` 为空字符串。
690
+ * 本方法在 reactLoop 返回后填充 `this.deps.agentName`,让顶层 trace 标识"是哪个 agent 跑的"。
691
+ *
416
692
  * @param input 用户输入
417
- * @returns 最终结果(content + messages + turns + stopReason + usage)
693
+ * @param options 临时覆盖本次调用的 model(字符串 key)/ temperature / maxTokens / enableTracing
694
+ * (不修改 agent 自身状态,详见 [agentHandle](./agentHandle.md))
695
+ * @returns 最终结果(content + messages + turns + stopReason + usage + trace?)
418
696
  * @throws {AgentError} agent 未注册
419
697
  * @throws {ReactLoopError} 超出 maxTurns
420
698
  * @throws {Error} provider.complete 抛错时立即传播
421
699
  */
422
- run(input: string): Promise<ReactLoopResult>;
700
+ run(input: string, options?: AgentRunOptions): Promise<ReactLoopResult>;
423
701
  /**
424
702
  * 流式执行——组装 config 调 [reactLoopStream](./reactLoop.md)
425
703
  *
426
704
  * @param input 用户输入
705
+ * @param options 临时覆盖本次调用的 model(字符串 key)/ temperature / maxTokens
706
+ * (不修改 agent 自身状态,详见 [agentHandle](./agentHandle.md))
427
707
  * @yields 流式 chunk(deltaContent / toolCall / toolResult / done)
428
708
  * @throws {AgentError} agent 未注册
429
709
  * @throws {ReactLoopError} 超出 maxTurns
430
710
  * @throws {Error} provider.stream 抛错时立即传播
431
711
  */
432
- stream(input: string): AsyncIterable<ReactLoopStreamChunk>;
712
+ stream(input: string, options?: AgentRunOptions): AsyncIterable<ReactLoopStreamChunk>;
433
713
  /**
434
714
  * 把自身包装为 `AgentToolDescriptor` 供 LLM 当 tool 调用
435
715
  *
@@ -452,18 +732,39 @@ declare class Agent {
452
732
  /**
453
733
  * 组装 ReactLoopConfig
454
734
  *
455
- * 1. 查 agent 元数据(未注册抛 AgentError
735
+ * 1. 查 agent 元数据(未注册抛 AgentError)——用 `getAgent` 拿 AgentCore
736
+ * (LLM-facing 字段:systemPrompt / model / maxTurns)
456
737
  * 2. buildToolDefinitions 组装 tool 列表
457
- * 3. config 字段优先级:agent 元数据 > 全局 AgentRuntimeConfig
738
+ * 3. config 字段优先级(高 → 低):`options` > agent 元数据 > 全局 AgentRuntimeConfig / deps.defaultProvider
739
+ *
740
+ * `options.model` 是字符串 key,由 {@link resolveModelKey} 解析为 provider + model
741
+ * (支持 llms key 精确匹配 / `provider/model` 一体化 / 纯 model 名模糊匹配)。
742
+ * 不传 `options.model` 时用 `deps.defaultProvider` + agent 元数据 `config.model`。
743
+ * 详见 [agentHandle.md](./agentHandle.md) 的「`options.model` 字符串 key 解析规则」。
458
744
  */
459
745
  private buildLoopConfig;
746
+ /**
747
+ * 解析 `options.model` 字符串 key → provider + model
748
+ *
749
+ * 规则见 [agentHandle.md](./agentHandle.md) 的「`options.model` 字符串 key 解析规则」:
750
+ * 1. `undefined` → `deps.defaultProvider` + `meta.model`
751
+ * 2. 精确匹配 `deps.providers` 的 key → 该 provider + 其 `models` 第一个 key
752
+ * 3. 含 `/` → `provider/model` 形式,`deps.providers.get(provider)` + 该 model
753
+ * (要求该 model 在 `deps.llms[provider].models` 里)
754
+ * 4. 不含 `/` 且非 provider key → 在所有 provider 的 `models` 里按 model 名查找
755
+ * - 唯一 → 该 provider + 该 model
756
+ * - 多个 → 抛 `AgentError`(要求用 `provider/model` 消歧)
757
+ * - 无 → 抛 `AgentError`
758
+ *
759
+ * @throws {AgentError} key 解析失败(provider/model 不存在或歧义)
760
+ */
761
+ private resolveModelKey;
460
762
  /**
461
763
  * 组装 LLM 可见 tool 列表
462
764
  *
463
- * 合并三个来源(按 `name` 去重,先入者保留):
765
+ * 合并两个来源(按 `name` 去重,先入者保留):
464
766
  * 1. **resolveAgentTools** —— agent 显式声明的 `tools` 引用
465
- * 2. **全局 defaultTools** —— `config.defaultTools` 中的 tool 名(所有 agent 共享)
466
- * 3. **sub-agent** —— `resolveSubAgents` 每个包装为 `agent.<name>`
767
+ * 2. **sub-agent** —— `resolveSubAgents` 每个包装为 `agent.<name>`
467
768
  *
468
769
  * 每个常规 tool 的 `input`:
469
770
  * - `resolveToolSchema` 提供 → 用其 `jsonSchema`
@@ -475,9 +776,12 @@ declare class Agent {
475
776
  /**
476
777
  * tool 执行路由(由 reactLoop 调用)
477
778
  *
478
- * - `agent.` 前缀 → {@link executeSubAgent} 递归
779
+ * - `agent.` 前缀 → {@link executeSubAgent} 递归(含 enableTracing + TracingToolResult 包装)
479
780
  * - 常规 tool → `loadToolModule` 加载 handler + 可选 input 校验 → 调用
480
781
  *
782
+ * `enableTracing` 由 [buildLoopConfig](#buildLoopConfig) 闭包捕获传入,用于 sub-agent
783
+ * 调用时决定是否包装 [TracingToolResult](./trace.md) 携带 sub-trace。
784
+ *
481
785
  * **常规 tool 校验失败**:不抛错,返回 `{ error }` 对象——reactLoop stringify 后
482
786
  * 作为 tool 结果回传 LLM,LLM 可据此修正参数重试。
483
787
  *
@@ -488,76 +792,26 @@ declare class Agent {
488
792
  * sub-agent 递归执行
489
793
  *
490
794
  * 1. `maxAgentDepth` 防护——超限抛 {@link AgentRecursionError}
491
- * 2. sub-agent handler 导出 `run` 时调自定义 `mod.run(args)`
492
- * 3. 无 `run` 时调 `subAgent.run(JSON.stringify(args))` 走默认 reactLoop
493
- *
494
- * 自定义 run 接收原始 args 对象;默认 reactLoop 接收 stringify 后的 args
495
- * 作为 user 消息(agent-as-tool input 为开放式 JSON)。
496
- */
497
- private executeSubAgent;
498
- }
499
-
500
- /**
501
- * agent handle——注入到 handler 的 `agent` 参数,提供可调用的 agent 运行入口
502
- *
503
- * faapi 核心的 [agentHandle](../../faapi/src/injection/agentHandle.md) 工厂注册机制
504
- * 让本包的 [plugin](./plugin.md) 在 setup 时注册工厂函数,injectParams 在
505
- * `agent` 参数注入时调工厂拿到 `AgentHandle` 实例。
506
- *
507
- * `Agent` 类满足此接口(结构化类型),plugin 的工厂直接返回 `Agent` 实例,
508
- * 无需额外包装层。handler 通过 `AgentHandle` 类型获得类型安全:
509
- *
510
- * ```ts
511
- * import type { AgentHandle } from '@faapi/agent';
512
- *
513
- * // src/api/chat/handler.ts
514
- * export function POST(agent: AgentHandle, body: { input: string }) {
515
- * const result = await agent.run(body.input);
516
- * return { content: result.content, turns: result.turns };
517
- * }
518
- * ```
519
- *
520
- * 工厂未注册(`@faapi/agent` 插件未加载或 `config.agent.llm` / `defaultAgent` 未配置)
521
- * 时注入 `undefined`,handler 需自行处理。
522
- *
523
- * 详见 [agentHandle.md](./agentHandle.md)。
524
- */
525
- interface AgentHandle {
526
- /**
527
- * 非流式执行 agent
795
+ * 2. sub-agent handler 导出 `run` 时调自定义 `mod.run(args)`(无 trace,与常规 tool 一致)
796
+ * 3. 无 `run` 时调 `subAgent.run(stringify(args), { enableTracing })` 走默认 reactLoop
528
797
  *
529
- * 组装 ReAct 循环 config(systemPrompt + tools + maxTurns)→ 调
530
- * [reactLoop](./reactLoop.md) 返回最终结果。
798
+ * **tracing 路径**:`enableTracing=true` 时,subAgent.run 返回的 `result.trace`(agentName
799
+ * 已被 `Agent.run` 填为 subName)被包装为 [TracingToolResult](./trace.md) 返回给 reactLoop。
800
+ * reactLoop 通过 `isTracingToolResult` 识别后发出 `subagent_call` 事件,嵌入 sub-trace
801
+ * (递归结构,业务方可还原完整调用树)。`enableTracing=false` 时返回 `result.content`
802
+ * (unknown,与常规 tool 一致,零开销)。
531
803
  *
532
- * @param input 用户输入文本
533
- * @returns 循环结果(content + messages + turns + stopReason + usage)
534
- * @throws {AgentError} agent 未注册
535
- * @throws {ReactLoopError} 超出 maxTurns
536
- * @throws {Error} LLM provider 抛错时立即传播
537
- */
538
- run(input: string): Promise<ReactLoopResult>;
539
- /**
540
- * 流式执行 agent
541
- *
542
- * 组装 config → 调 [reactLoopStream](./reactLoop.md) → yield 流式 chunk。
543
- * 适用于 LLM token 流式输出、tool 调用过程展示等场景。
544
- *
545
- * @param input 用户输入文本
546
- * @yields 流式 chunk(deltaContent / toolCall / toolResult / done)
547
- * @throws {AgentError} agent 未注册
548
- * @throws {ReactLoopError} 超出 maxTurns
549
- * @throws {Error} LLM provider 抛错时立即传播
550
- */
551
- stream(input: string): AsyncIterable<ReactLoopStreamChunk>;
552
- /**
553
- * 把自身包装为 `AgentToolDescriptor` 供 LLM 当 tool 调用
804
+ * **自定义 run 无 trace**:业务方导出 `run` 函数时直接返回业务结果,无法采集 sub-agent
805
+ * 内部明细——需 trace 时应让 sub-agent 走默认 reactLoop(不导出 `run`)。
554
806
  *
555
- * 用于 agent-as-tool 场景:父 agent 把子 agent 包装为 tool,
556
- * 加入 LLM 可见 tool 列表,LLM 调用时触发 sub-agent 递归执行。
807
+ * 自定义 run 接收原始 args 对象;默认 reactLoop 接收 stringify 后的 args
808
+ * 作为 user 消息(agent-as-tool input 为开放式 JSON)。
557
809
  *
558
- * @returns `AgentToolDescriptor` `undefined`(agent 未注册)
810
+ * 加载 handler.js 用 `getAgentEntry`(返回 AgentMetadata,含 filePath/hasRun),
811
+ * 而非 `getAgent`(返回 AgentCore,无代码加载细节)。DB skill 无文件,
812
+ * `getAgentEntry` 返回 `undefined`,走默认 reactLoop。
559
813
  */
560
- asTool(): AgentToolDescriptor | undefined;
814
+ private executeSubAgent;
561
815
  }
562
816
 
563
817
  /**
@@ -569,7 +823,15 @@ interface AgentHandle {
569
823
  *
570
824
  * export default {
571
825
  * agent: {
572
- * llm: { provider: 'openai', apiKey: process.env.OPENAI_API_KEY, model: 'gpt-4o' },
826
+ * llms: {
827
+ * openai: {
828
+ * provider: 'openai',
829
+ * apiKey: process.env.OPENAI_API_KEY,
830
+ * baseURL: 'https://api.openai.com/v1',
831
+ * models: { 'gpt-4o': {}, 'gpt-4o-mini': { temperature: 0.5 } },
832
+ * },
833
+ * },
834
+ * defaultLlm: 'openai',
573
835
  * defaultAgent: 'researcher',
574
836
  * maxTurns: 10,
575
837
  * },
@@ -578,14 +840,15 @@ interface AgentHandle {
578
840
  * ```
579
841
  *
580
842
  * 插件 setup 时:
581
- * 1. `config.agent.llm` → `createProvider` → LLMProvider 实例(单例)
582
- * 2. 读 `config.agent.defaultAgent` / `maxTurns` / `maxAgentDepth` / `defaultTools`
583
- * 3. `@faapi/faapi` import 注册表/加载器访问器(getAgent / getTool / resolveAgentTools /
843
+ * 1. 遍历 `config.agent.llms` → 每项调 `createProvider` → `Map<providerKey, LLMProvider>`
844
+ * 2. 读 `config.agent.defaultLlm` `defaultProvider`(未设时用 `llms` 第一个 key)
845
+ * 3. `config.agent.defaultAgent` / `maxTurns` / `maxAgentDepth`
846
+ * 4. 从 `@faapi/faapi` import 注册表/加载器访问器(getAgent / getTool / resolveAgentTools /
584
847
  * resolveSubAgents / loadAgentModule / loadToolModule)
585
- * 4. `registerAgentHandleFactory` 注册工厂——每次请求时构造 [Agent](./agent.md) 实例注入到
848
+ * 5. `registerAgentHandleFactory` 注册工厂——每次请求时构造 [Agent](./agent.md) 实例注入到
586
849
  * handler 的 `agent` 参数
587
850
  *
588
- * 配置缺失时(`agent.llm` 或 `agent.defaultAgent` 未设置)跳过工厂注册并打印警告,
851
+ * 配置缺失时(`agent.llms` 或 `agent.defaultAgent` 未设置)跳过工厂注册并打印警告,
589
852
  * handler 的 `agent` 参数注入 `undefined`。
590
853
  *
591
854
  * 详见 [plugin.md](./plugin.md)。
@@ -599,4 +862,4 @@ interface AgentHandle {
599
862
  */
600
863
  declare const agentPlugin: FaapiPlugin;
601
864
 
602
- export { Agent, type AgentDeps, AgentError, type AgentHandle, AgentRecursionError, type AgentRuntimeConfig, type LLMCompleteRequest, type LLMMessage, type LLMProvider, LLMProviderError, type LLMResponse, type LLMStopReason, type LLMStreamChunk, type LLMToolCall, type LLMToolDefinition, type LLMUsage, type ReactLoopConfig, ReactLoopError, type ReactLoopResult, type ReactLoopStreamChunk, type ToolExecutor, type ToolSchemaResolution, createOpenAIProvider, createProvider, agentPlugin as default, reactLoop, reactLoopStream };
865
+ export { Agent, type AgentDeps, AgentError, type AgentHandle, AgentRecursionError, type AgentRunOptions, type AgentRuntimeConfig, type AgentTrace, type AgentTraceEvent, type LLMCompleteRequest, type LLMMessage, type LLMProvider, LLMProviderError, type LLMResponse, type LLMStopReason, type LLMStreamChunk, type LLMToolCall, type LLMToolDefinition, type LLMUsage, type LlmCallEvent, type ReactLoopConfig, ReactLoopError, type ReactLoopResult, type ReactLoopStreamChunk, type SubAgentCallEvent, type ToolCallEvent, type ToolExecutor, type ToolSchemaResolution, type TracingToolResult, createOpenAIProvider, createProvider, agentPlugin as default, isTracingToolResult, reactLoop, reactLoopStream };