core · 数据模型

所有公共导出来自 @migor/agentia 包根(src/index.ts)。core 层是零依赖的数据模型与结构面:四类单元最终都编译成 AgentTool,一次 run 沉淀为一条 Trace

导出签名摘要说明
AgentTool<I, O>{ name; description; inputSchema; strict?; run(input: I, ctx?: ToolRunContext) }模型可见工具的最小结构面。run 抛错被包成 is_error 的 tool_result 回给模型,run 不中断。
JsonSchema{ type; description?; properties?; required?; additionalProperties?; … }工具 input_schema 的 JSON Schema 子集(v1 裸 schema,不依赖 zod)。
ModelClient{ messages.stream(params) → { on('text', cb); finalMessage() } }engine 对模型端的最小结构面;Anthropic SDK 天然满足,其他 provider 适配成同一形态。
ToolRunContext{ client; recorder; parentSpanId }engine 调用每个工具时注入的执行上下文,供子单元把嵌套 span 挂进当前 trace。
RecorderBackendbegin / end / event / setAttribute / snapshotTraceRecorder 面向子单元的最小记账面(core 不依赖 engine)。
Trace{ traceId; rootSpanId; spans: Span[]; status; totalUsage }一次 run == 一条 trace(traceId == runId);totalUsage 为各 span 求和。
Span{ spanId; parentSpanId; kind; name; startedAt; endedAt?; status; error?; usage?; attributes; events }调用树节点;unit 名为 ${unitType}:${unitName},llm.turn 名为模型 id。
Usage{ inputTokens; outputTokens; cacheReadTokens; cacheCreationTokens; costEstimate? }span 聚合用量;成本由 token × 单价表估算。
SpanKind'run' | 'unit' | 'llm.turn' | 'internal'span 层级:整次运行 / 单元调用 / 单元内每次模型往返。
UnitType'tool' | 'skill' | 'prompt' | 'subagent'四类单元,共用命名空间。
SpanEvent{ time; name; body }span 上的结构化事件(如 tool.input / tool.output / compaction)。
SpanError{ type; message; retryable }错误分类;retryable 标记 429 / 5xx / 网络类可安全重试。
SpanStatus / SpanId / TraceId'ok' | 'error' / string / string基础类型别名。
validateJsonSchema(schema: JsonSchema, input: unknown) → string | nullJSON Schema 子集校验(fromZod 接入时先走 zod),返回首条错误消息或 null。

engine · 运行时内核

流式 manual loop:模型往返 + 递归工具调用,直到 end_turn / 循环上限 / submit_result 提交结构化结果。

导出签名摘要说明
runAgent(options: RunAgentOptions) → Promise<AgentRunResult>主循环入口;不注入 recorder 时内部新建,traceId 即 runId。
resolveDefaultModel(over?: string) → string模型缺省解析:显式参数 > AGENTIA_MODEL 环境变量 > claude-opus-5
TraceRecorderclass:begin(kind, name, parent) / end(id, patch?) / event / setAttribute / snapshot(status)span 记账器,traceId 随实例生成。
classifyError(e: unknown) → SpanErrorSDK 异常归类:rate_limit / connection 标 retryable。
AgentRunResult{ trace; stopReason; finalText; iterations; error?; typed? }typed 为 resultSchema 校验通过的结构化结果(模型未提交则为 undefined)。
AgentStopReason'end_turn' | 'max_tokens' | 'refusal' | 'pause_turn' | 'max_iterations' | 'tool_use_no_blocks' | 'error'循环终止原因。
ContextPolicy{ budgetTokens?; beforeTurn(messages, { iteration, model }) → Promise<messages> }上下文预算策略面:每回合发送前可编辑 / 压缩消息。
SystemParam / SystemTextBlockstring | SystemTextBlock[]system 参数:纯文本,或带 cache_control breakpoint 的可缓存块数组。

RunAgentOptions 选项

选项类型说明
messages(必填)MessageParam[]初始消息;由调用方给 user 起始消息。
systemSystemParam顶层 system(SystemPrompt 产物);稳定内容放在第一个 breakpoint 前。
toolsAgentTool[]主 agent 可调工具菜单(裸 JSON schema)。
modelstring缺省走 resolveDefaultModel
maxTokensnumber流式请求的 max_tokens,给足避免中途截断。
maxIterationsnumber循环安全上限,防止无限 tool 往返。
clientModelClient缺省 new Anthropic()(读环境变量);多模型场景注入适配 client。
recorderTraceRecorderrun 层复用注入;不注入则内部新建。
onText(delta: string) => void文本增量回调(终端 / SSE 用)。
runNamestring写入 trace 根 span 名。
contextPolicyContextPolicy每回合发送前的上下文预算策略(compaction / context editing)。
resultSchemaJsonSchema给出后追加隐藏工具 submit_result,模型提交校验通过的结果写入 result.typed 并结束循环。

engine · 长上下文策略

预算驱动的上下文护栏:估算 ≤ 预算走快路径原样放行;超预算先 context editing(不调模型);仍超且有 summarize 才 compaction,并带滞回避免每回合反复压缩。

导出签名摘要说明
createBudgetPolicy(opts?: BudgetPolicyOptions) → ContextPolicy上述三段降级的开箱实现;框架不替你造 token,可注入按 /count_tokens 的 estimate。
BudgetPolicyOptions{ budgetTokens?=60000; keepRecent?=20; estimateTokens?; editBeforeCompact?=true; summarize?; compactEvery?=1 }summarize 提供才允许 compaction;compactEvery 为压缩滞回(回合数)。
trimToolPairs(messages, opts?: TrimOptions) → messagescontext editing:丢弃超出 keepRecent(缺省 1)的旧 tool_use→tool_result 对。
traceToMessages(trace, opts?: ReplayOptions) → MessageParam[]trace 重放基底:把完成的 run 还原成可喂回模型的对话(角色交替合法、tool 对配对、可截断)。
compactMessages(messages, opts: CompactOptions) → Promise<messages>compaction:旧前缀经 summarize 摘要,只留最近 keepRecent(缺省 20)条。
defaultEstimateTokens(text: string) → numberCJK 感知的 token 启发式估算(预算决策用,非精确记账)。
estimateMessages(messages, estimate?) → number整组消息 token 估算。
renderMessages(messages) → string消息渲染成纯文本,作摘要器输入。
TrimOptions / CompactOptions{ keepRecent? } / { keepRecent?; summarize }上述两函数的选项类型。

run · 生命周期与触发

一次 run 的完整生命周期(RunContext + recorder + 记忆水合/回写),以及同步 RPC、异步任务、定时调度三类触发 —— 共用同一份输入契约,换宿主不换语义。

导出签名摘要说明
executeRun(options: ExecuteRunOptions) → Promise<{ run; result }>run 生命周期入口:RunContext 作用域内跑 runAgent,finish / fail 收口。
ExecuteRunOptionsRunAgentOptions & { idempotencyKey?; contextInit?; memory? }memory:run 开始把 store.load(keys) 水合进 blackboard(用户种子优先),结束回写。
Runclass:start() / finish(result) / fail(error) / toMeta()run 状态机(queued → running → succeeded/failed);runId == recorder.traceId。
RunContextclass:static current() / get<T>(key) / set / has / delete / keysrun 内 blackboard,AsyncLocalStorage 传播,不用全局单例。
withRunContext(ctx: RunContext, fn) → Promise<T>在指定 ctx 作用域内执行 fn。
RunMeta / RunStatus{ runId; status; idempotencyKey?; createdAt; startedAt?; finishedAt?; error? } / 'queued' | 'running' | 'succeeded' | 'failed'运行记录与状态。
SystemPromptclass:add(name, text, stable?) / add(section) / build({ cache? })分段组装 system:stable 段进可缓存前缀打 ephemeral breakpoint,volatile 段放其后。
SystemSection{ name; text; stable? }system 段落;stable=false 不进可缓存前缀。
normalizeMessages(input: RunInput | unknown) → MessageParam[]任意任务入参(string / messages / { prompt | text | messages })规范成 messages;空数组抛错。
RunInputstring | MessageParam[] | { prompt?; text?; messages? }transport 层接受的原始入参形态。
RunSpec{ messages; options?; source? }一次任务的规范化入参;source 标记触发来源(sync / async / schedule:<id>)。
RunInvocationOptions{ model?; maxTokens?; maxIterations?; client?; onText?; blackboard?; contextPolicy?; idempotencyKey?; rethrow?; tools? }单次 run 的调用参数,三类触发共用。
runSync / createSyncHandler(app, input, opts?) / (app) → (input, opts?) => …同步 RPC:规范化入参后直接 app.run
AsyncRunnernew AsyncRunner(app, { client?, store?, concurrency? }):submit(input, { idempotencyKey?, source?, options? }) / resumePending()提交即返回任务记录、后台执行;同幂等键未失败任务 last-wins 去重;concurrency 限并发,超出排队。
AppCallable{ name; run(messages, opts?) }应用最小调用面(run 层不向上依赖 toolkit)。
Schedulernew Scheduler(runner):every(intervalMs, input, opts?) / at(when, input, opts?)周期 / 定点触发,到点转为异步任务提交;返回 ScheduleHandle { id, cancel() }
TaskStoreinterface:save / get / byIdempotency / list / clear任务记录存储面;byIdempotency 为 last-wins 去重依据。
TaskRecord{ taskId; status; idempotencyKey?; spec: RunSpec; runId?; createdAt; startedAt?; … }任务记录;完成后回填 runId(== traceId)。
InMemoryTaskStoreclass(TaskStore)Map 实现:测试与缺省场景用。
FileTaskStorenew FileTaskStore(file)JSONL 一行一快照落盘;宿主重启后 resumePending() 续跑 queued/running。
SqliteTaskStorenew SqliteTaskStore(path)node:sqlite 落库(WAL 读写不互斥),支持 ':memory:'
RedisTaskStorenew RedisTaskStore(client, { prefix? })duck-typed Redis 客户端(ioredis/node-redis 均可,可选 peer),TaskStore 方法支持异步。

run · 宿主与导出

同一份应用一行接入 HTTP;trace 可导出到任何 OTLP 收集器;OpenAI 兼容端点一行接入多模型。

导出签名摘要说明
createHttpHandler(app: AppCallable, opts?: { runner? }) → (req, res) => Promise<void>node:http 接入:POST /run(同步)、POST /tasks(异步)、GET /tasks/:id(轮询)。
createOtlpExporter({ endpoint, headers?, serviceName? }) → { export(trace) → Promise<void> }trace 导出到任意 OTLP/HTTP 收集器(缺省 service.name 为 'agentia')。
createOpenAIClient(opts?: { apiKey?; baseURL?; fetchImpl? }) → ModelClientOpenAI 兼容端点(含 DeepSeek 等)适配成 ModelClient;apiKey 缺省读 OPENAI_API_KEY
MemoryStoreinterface:load(keys) / save(entries)跨 run 记忆存储面(可同步或异步实现)。
InMemoryMemoryStoreclass(MemoryStore)Map 实现:进程内,无持久化。

container · 显式 DI

显式 provider 注册,覆盖式语义(后注册覆盖先注册);resolve 单例缓存并做循环依赖检测。

导出签名摘要说明
Containerclass:register(...providers) / has(token) / resolve<T>(token)DI 容器;register 返回 this 可链式。
Tokenstringprovider 注册键。
ProviderValueProvider | ClassProvider | FactoryProvider三种 provider 形态的联合。
ValueProvider{ provide; useValue }直接给值。
ClassProvider{ provide; useClass; deps? }类实例化;deps 按序注入构造器参数。
FactoryProvider{ provide; useFactory; deps? }工厂函数;deps 与 useFactory 形参一一对应。

toolkit · 声明式单元与装配

装饰器声明四类单元,createApp 装配:DI 注册 → 扫描收集菜单 → 装配期静态校验(菜单查重、tools 引用存在性、toolSources 指向、DI 循环依赖)。

导出签名摘要说明
createApp(opts: AppOptions) → AgentApp;带 discover 时 → Promise<AgentApp>装配应用;目录发现走动态 import,故返回 Promise。
AppOptions{ name?; providers?; modules?; discover?; system; model?; maxTokens?; maxIterations?; contextPolicy?; toolSources?; middleware? }system 必填:SystemPrompt 实例(自动打缓存)或拼好的 SystemParam。
AgentAppclass:run(messages, opts?: RunAppOptions) → Promise<AgentRunOutput>;tools;container装配产物;run 每执行一次从 SystemPrompt 重建 system,保证 volatile 新鲜。
RunAppOptionsRunInvocationOptions & { system? }单次调用参数,可覆盖 system。
AgentRunOutput{ run: Run; result: AgentRunResult }app.run 的返回。
defineModule / AgentModule(m: AgentModule) → AgentModule / { providers; middleware? }第三方能力包约定:providers 先于应用级注册(同 token 应用级覆盖),middleware 拼在更外层;defineModule 为 identity 函数。
Tool@Tool(spec: ToolSpec) 方法装饰器类方法即工具:入参按 schema 先校验再执行。
ToolSpec{ name?; description; schema: JsonSchema; strict? }name 缺省取方法名;strict 需 additionalProperties:false + required 齐全。
collectTools(instance: object) → AgentTool[]沿原型链扫描 @Tool 方法并绑定实例。
Skill@Skill(spec: SkillSpec) 方法装饰器方法体是确定性脚本;模型调用只在显式 ctx.llm() 时发生,记在 skill 自己的 unit span 下。
SkillSpec{ name?; description; schema?; model?; maxTokens?; maxIterations?; tools? }tools 为容器 provider token 列表(ctx.llm() 可调)。
SkillContext{ model?; llm(opts: SkillLlmOptions) → Promise<SkillLlmResult> }受限子运行句柄:每次 llm() 在 unit span 下开一轮独立 agent 循环。
SkillLlmOptions / SkillLlmResult{ prompt?; messages?; system?; model?; maxTokens?; maxIterations? } / { text; stopReason }prompt 与 messages 二选一;system 接受 string 或 SystemPrompt。
collectSkills / skillToTool(instance) → SkillUnit[] / (unit, resolveTools) → AgentTool扫描 @Skill 方法 / 编译成主 agent 菜单项。
SkillUnit{ name; description; inputSchema; spec; invoke(input, ctx) }含绑定实例的方法执行器。
SubAgent@SubAgent(spec: SubAgentSpec) 方法装饰器方法体不执行:框架按 system 另起隔离循环,中间过程不外泄,只有最终报告回流。
SubAgentSpec{ name?; description; schema; system; tools?; model?; maxTokens?; maxIterations? }system 支持 string / SystemPrompt / (task) => SystemParam(动态拼,框架不附加);tools 为 provider token 列表。
collectSubAgents / subagentToTool(instance) → SubAgentUnit[] / (unit, resolveTools) → AgentTool扫描 @SubAgent 方法 / 编译成 AgentTool(子循环复用 ctx.client 与 recorder)。
SubAgentUnit{ name; description; inputSchema; spec }子代理单元元数据。
Prompt@Prompt(spec: PromptSpec) 方法装饰器纯文本资产编译成无副作用的拉取型工具;每次调用重算(volatile 语义)。
PromptSpec{ name?; description; schema? }description 写清何时该拉取,模型据此决定调用;schema 缺省空对象(无参资产)。
collectPrompts(instance: object) → AgentTool[]扫描 @Prompt 方法(static 方法亦可)。
asset(base: string | URL, rel: string) → stringasset(import.meta.url, './system.md') 相对单元目录读文本资产;每次调用现读不缓存。
discoverProviders(dir: string) → Promise<Provider[]>目录约定扫描 units/<name>/index.ts:default export 为类 / Provider / Provider[],类以文件夹名为 token 注册。
applyMiddleware(tools: AgentTool[], middleware: UnitMiddleware[]) → AgentTool[]洋葱模型包裹整个菜单(链序 = 注册顺序,先注册最外层);链为空原样返回,零开销。
UnitMiddleware(call: UnitCall, next: UnitNext) => unknown单元调用前后横切(鉴权 / 缓存 / 审计);不调 next 即短路,抛错按单元失败处理。
UnitCall / UnitNext{ unit; input; ctx? } / (input?) => unknown入参 schema 校验已过;next(newInput) 可改写入参。
fromZod(jsonSchema: JsonSchema, zod: unknown) → JsonSchemazod 可选接入(peer):校验走 zod safeParse(结构面识别,框架不 import zod),错误路径原样回给模型自我修正。