@migor/agentia 0.2.1 → 0.3.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/LICENSE +21 -0
- package/README.en.md +151 -0
- package/README.md +206 -76
- package/dist/AGENTS.md +854 -0
- package/dist/container/container.d.ts +10 -2
- package/dist/container/container.js +27 -3
- package/dist/container/container.js.map +1 -1
- package/dist/core/abort.d.ts +10 -0
- package/dist/core/abort.js +28 -0
- package/dist/core/abort.js.map +1 -0
- package/dist/core/blackboard.d.ts +37 -0
- package/dist/core/blackboard.js +2 -0
- package/dist/core/blackboard.js.map +1 -0
- package/dist/core/json.d.ts +2 -0
- package/dist/core/json.js +4 -0
- package/dist/core/json.js.map +1 -1
- package/dist/core/run.d.ts +18 -0
- package/dist/core/run.js +2 -0
- package/dist/core/run.js.map +1 -0
- package/dist/core/schema.js +2 -2
- package/dist/core/schema.js.map +1 -1
- package/dist/core/tool.d.ts +62 -7
- package/dist/core/trace.d.ts +20 -4
- package/dist/engine/budget.d.ts +49 -0
- package/dist/engine/budget.js +30 -0
- package/dist/engine/budget.js.map +1 -0
- package/dist/engine/concurrency.d.ts +43 -0
- package/dist/engine/concurrency.js +95 -0
- package/dist/engine/concurrency.js.map +1 -0
- package/dist/engine/errors.d.ts +2 -0
- package/dist/engine/errors.js +7 -0
- package/dist/engine/errors.js.map +1 -1
- package/dist/engine/loop.d.ts +35 -10
- package/dist/engine/loop.js +336 -66
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/policy.d.ts +9 -1
- package/dist/engine/policy.js +8 -5
- package/dist/engine/policy.js.map +1 -1
- package/dist/engine/replay.d.ts +8 -5
- package/dist/engine/replay.js +30 -29
- package/dist/engine/replay.js.map +1 -1
- package/dist/engine/retry.d.ts +47 -0
- package/dist/engine/retry.js +51 -0
- package/dist/engine/retry.js.map +1 -0
- package/dist/{run → engine}/spec.d.ts +24 -4
- package/dist/{run → engine}/spec.js +4 -1
- package/dist/engine/spec.js.map +1 -0
- package/dist/engine/tracer.d.ts +9 -0
- package/dist/engine/tracer.js +57 -1
- package/dist/engine/tracer.js.map +1 -1
- package/dist/engine/{context.d.ts → trimming.d.ts} +19 -20
- package/dist/engine/trimming.js +238 -0
- package/dist/engine/trimming.js.map +1 -0
- package/dist/engine/types.d.ts +109 -13
- package/dist/engine/types.js +9 -1
- package/dist/engine/types.js.map +1 -1
- package/dist/engine/usage.d.ts +28 -2
- package/dist/engine/usage.js +39 -6
- package/dist/engine/usage.js.map +1 -1
- package/dist/eval/defineEval.d.ts +73 -0
- package/dist/eval/defineEval.js +44 -0
- package/dist/eval/defineEval.js.map +1 -0
- package/dist/eval/scripted.d.ts +26 -0
- package/dist/eval/scripted.js +43 -0
- package/dist/eval/scripted.js.map +1 -0
- package/dist/index.d.ts +62 -35
- package/dist/index.js +39 -20
- package/dist/index.js.map +1 -1
- package/dist/integrations/anthropic.d.ts +53 -0
- package/dist/integrations/anthropic.js +24 -0
- package/dist/integrations/anthropic.js.map +1 -0
- package/dist/integrations/mcp.d.ts +79 -0
- package/dist/integrations/mcp.js +108 -0
- package/dist/integrations/mcp.js.map +1 -0
- package/dist/integrations/metrics.d.ts +131 -0
- package/dist/integrations/metrics.js +488 -0
- package/dist/integrations/metrics.js.map +1 -0
- package/dist/{run → integrations}/openai.d.ts +12 -5
- package/dist/integrations/openai.js +386 -0
- package/dist/integrations/openai.js.map +1 -0
- package/dist/integrations/otlp.js.map +1 -0
- package/dist/integrations/report.d.ts +78 -0
- package/dist/integrations/report.js +257 -0
- package/dist/integrations/report.js.map +1 -0
- package/dist/runtime/context.d.ts +25 -0
- package/dist/{run → runtime}/context.js +8 -1
- package/dist/runtime/context.js.map +1 -0
- package/dist/{run → runtime}/memory.d.ts +1 -1
- package/dist/{run → runtime}/memory.js +3 -1
- package/dist/runtime/memory.js.map +1 -0
- package/dist/{run → runtime}/run.d.ts +22 -5
- package/dist/runtime/run.js +218 -0
- package/dist/runtime/run.js.map +1 -0
- package/dist/runtime/session.d.ts +27 -0
- package/dist/runtime/session.js +16 -0
- package/dist/runtime/session.js.map +1 -0
- package/dist/{run → runtime}/systemPrompt.d.ts +12 -0
- package/dist/{run → runtime}/systemPrompt.js +16 -3
- package/dist/runtime/systemPrompt.js.map +1 -0
- package/dist/{run → store}/fsStore.d.ts +15 -0
- package/dist/{run → store}/fsStore.js +43 -1
- package/dist/store/fsStore.js.map +1 -0
- package/dist/{run → store}/redisStore.d.ts +19 -1
- package/dist/{run → store}/redisStore.js +33 -6
- package/dist/store/redisStore.js.map +1 -0
- package/dist/{run → store}/sqliteStore.js +22 -1
- package/dist/store/sqliteStore.js.map +1 -0
- package/dist/{run → store}/store.d.ts +25 -3
- package/dist/store/store.js +63 -0
- package/dist/store/store.js.map +1 -0
- package/dist/toolkit/asset.d.ts +7 -6
- package/dist/toolkit/asset.js +7 -6
- package/dist/toolkit/asset.js.map +1 -1
- package/dist/toolkit/collect.d.ts +20 -7
- package/dist/toolkit/collect.js +21 -7
- package/dist/toolkit/collect.js.map +1 -1
- package/dist/toolkit/discover.d.ts +21 -1
- package/dist/toolkit/discover.js +59 -12
- package/dist/toolkit/discover.js.map +1 -1
- package/dist/toolkit/env.d.ts +47 -0
- package/dist/toolkit/env.js +62 -0
- package/dist/toolkit/env.js.map +1 -0
- package/dist/toolkit/middleware.d.ts +10 -10
- package/dist/toolkit/middleware.js +17 -4
- package/dist/toolkit/middleware.js.map +1 -1
- package/dist/toolkit/module.d.ts +100 -34
- package/dist/toolkit/module.js +112 -36
- package/dist/toolkit/module.js.map +1 -1
- package/dist/toolkit/prompt.d.ts +4 -6
- package/dist/toolkit/prompt.js +11 -10
- package/dist/toolkit/prompt.js.map +1 -1
- package/dist/toolkit/skill.d.ts +14 -16
- package/dist/toolkit/skill.js +32 -25
- package/dist/toolkit/skill.js.map +1 -1
- package/dist/toolkit/subagent.d.ts +10 -12
- package/dist/toolkit/subagent.js +23 -21
- package/dist/toolkit/subagent.js.map +1 -1
- package/dist/toolkit/tool.d.ts +19 -11
- package/dist/toolkit/tool.js +11 -8
- package/dist/toolkit/tool.js.map +1 -1
- package/dist/toolkit/zod.d.ts +4 -3
- package/dist/toolkit/zod.js +2 -1
- package/dist/toolkit/zod.js.map +1 -1
- package/dist/transport/async.d.ts +133 -0
- package/dist/transport/async.js +415 -0
- package/dist/transport/async.js.map +1 -0
- package/dist/transport/http.d.ts +146 -0
- package/dist/transport/http.js +387 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/{run → transport}/scheduler.d.ts +9 -1
- package/dist/transport/scheduler.js +133 -0
- package/dist/transport/scheduler.js.map +1 -0
- package/dist/transport/sse.d.ts +43 -0
- package/dist/transport/sse.js +51 -0
- package/dist/transport/sse.js.map +1 -0
- package/dist/{run → transport}/transport.d.ts +1 -1
- package/dist/{run → transport}/transport.js +1 -1
- package/dist/transport/transport.js.map +1 -0
- package/package.json +24 -9
- package/dist/engine/context.js +0 -146
- package/dist/engine/context.js.map +0 -1
- package/dist/run/async.d.ts +0 -59
- package/dist/run/async.js +0 -201
- package/dist/run/async.js.map +0 -1
- package/dist/run/context.d.ts +0 -16
- package/dist/run/context.js.map +0 -1
- package/dist/run/fsStore.js.map +0 -1
- package/dist/run/http.d.ts +0 -47
- package/dist/run/http.js +0 -119
- package/dist/run/http.js.map +0 -1
- package/dist/run/memory.js.map +0 -1
- package/dist/run/openai.js +0 -181
- package/dist/run/openai.js.map +0 -1
- package/dist/run/otlp.js.map +0 -1
- package/dist/run/redisStore.js.map +0 -1
- package/dist/run/run.js +0 -129
- package/dist/run/run.js.map +0 -1
- package/dist/run/scheduler.js +0 -85
- package/dist/run/scheduler.js.map +0 -1
- package/dist/run/spec.js.map +0 -1
- package/dist/run/sqliteStore.js.map +0 -1
- package/dist/run/store.js +0 -28
- package/dist/run/store.js.map +0 -1
- package/dist/run/systemPrompt.js.map +0 -1
- package/dist/run/transport.js.map +0 -1
- package/dist/run/types.d.ts +0 -12
- package/dist/run/types.js +0 -2
- package/dist/run/types.js.map +0 -1
- /package/dist/{run → integrations}/otlp.d.ts +0 -0
- /package/dist/{run → integrations}/otlp.js +0 -0
- /package/dist/{run → store}/sqliteStore.d.ts +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentia —— 长上下文策略的纯函数层(spec §5/§6:compaction / context editing 分清)。
|
|
3
|
+
*
|
|
4
|
+
* 三策略不混:
|
|
5
|
+
* - **context editing**(`trimToolPairs`):清旧 tool_use→tool_result 对,不掉内容、不额外调模型;
|
|
6
|
+
* - **compaction**(`compactMessages`):把旧消息前缀做**服务端摘要**(摘要器由上层注入,可走模型),
|
|
7
|
+
* 只保留最近 N 条 + 一段摘要 —— 新的消息数组仍保证角色交替合法;
|
|
8
|
+
* - **客户端剪裁** = 子 agent 的独立上下文(见 toolkit/subagent.ts)。
|
|
9
|
+
*
|
|
10
|
+
* 不在此自研 token 计数黑名单:`estimateMessages` 只是预算策略的默认启发式
|
|
11
|
+
* (CJK 感知估算,明确标注为估算),上层可注入真实 count_tokens 结果。
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* 默认估算(仅预算决策用,非精确记账):CJK 字符按 ~1.5 字/token,
|
|
15
|
+
* 其余(ASCII/拉丁)按 ~4 字符/token。纯字符/4 对中文系统性高估,
|
|
16
|
+
* 会让预算护栏过早触发压缩。
|
|
17
|
+
*/
|
|
18
|
+
/** BMP 内的 CJK 范围(用正则扫描数,不做逐码点 JS 循环) */
|
|
19
|
+
const CJK_BMP_RE = /[\u3000-\u30ff\u3400-\u9fff\uf900-\ufaff\uff00-\uffef]/g;
|
|
20
|
+
/** 增补平面 CJK(扩展 B–G)—— 极少出现,单独按码点计数 */
|
|
21
|
+
const CJK_SUPP_RE = /[\u{20000}-\u{2a6df}\u{2a700}-\u{2ebef}\u{30000}-\u{3134f}]/gu;
|
|
22
|
+
const CJK_SUPP_TEST = /[\u{20000}-\u{2a6df}\u{2a700}-\u{2ebef}\u{30000}-\u{3134f}]/u;
|
|
23
|
+
export function defaultEstimateTokens(text) {
|
|
24
|
+
// 快路径:把 BMP 内的 CJK 一次性抹掉,用长度差得到 BMP-CJK 字符数。
|
|
25
|
+
// 不用 `for..of` + `codePointAt` 逐码点判区间 —— 那是 JS 层循环,V8 的正则扫描快得多:
|
|
26
|
+
// 300KB 文本上实测 0.44ms → 0.18ms(JSON 工具结果),纯 ASCII 上 2.29ms → ~0ms。
|
|
27
|
+
// 这是框架里单点最大的 CPU 消耗(profile 占约 46%),所以值得走正则。
|
|
28
|
+
const rest = text.replace(CJK_BMP_RE, '').length;
|
|
29
|
+
let cjk = text.length - rest;
|
|
30
|
+
let other = rest; // 未被抹掉的 UTF-16 能力数(含代理对的两个能力)
|
|
31
|
+
// 增补平面:每个字符占 2 个 UTF-16 能力。旧口径把它算成 cjk+1、other 保留多出的 1 个能力,
|
|
32
|
+
// 这里逐字保持(cjk+1、other-1),否则估算值与护栏触发点都会变。
|
|
33
|
+
// 先 test() 再 matchAll:绝大多数文本不含增补平面,省掉一次带分配的全扫。
|
|
34
|
+
if (CJK_SUPP_TEST.test(text)) {
|
|
35
|
+
for (const _m of text.matchAll(CJK_SUPP_RE)) {
|
|
36
|
+
cjk += 1;
|
|
37
|
+
other -= 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return Math.max(1, Math.ceil(cjk / 1.5 + other / 4));
|
|
41
|
+
}
|
|
42
|
+
export function isToolResultMessage(msg) {
|
|
43
|
+
if (msg.role !== 'user')
|
|
44
|
+
return false;
|
|
45
|
+
if (typeof msg.content === 'string')
|
|
46
|
+
return false;
|
|
47
|
+
return msg.content.length > 0 && msg.content.every((b) => b.type === 'tool_result');
|
|
48
|
+
}
|
|
49
|
+
function hasToolUse(msg) {
|
|
50
|
+
if (msg.role !== 'assistant')
|
|
51
|
+
return false;
|
|
52
|
+
if (typeof msg.content === 'string')
|
|
53
|
+
return false;
|
|
54
|
+
return msg.content.some((b) => b.type === 'tool_use');
|
|
55
|
+
}
|
|
56
|
+
function contentToText(content) {
|
|
57
|
+
if (typeof content === 'string')
|
|
58
|
+
return content;
|
|
59
|
+
return content
|
|
60
|
+
.map((b) => {
|
|
61
|
+
switch (b.type) {
|
|
62
|
+
case 'text':
|
|
63
|
+
return b.text;
|
|
64
|
+
case 'tool_use':
|
|
65
|
+
return `${b.name}(${JSON.stringify(b.input)})`;
|
|
66
|
+
case 'tool_result':
|
|
67
|
+
return JSON.stringify(b.content);
|
|
68
|
+
default:
|
|
69
|
+
return JSON.stringify(b);
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
.join('\n');
|
|
73
|
+
}
|
|
74
|
+
function contentTokens(content, estimate) {
|
|
75
|
+
if (typeof content === 'string')
|
|
76
|
+
return estimate(content);
|
|
77
|
+
let n = 0;
|
|
78
|
+
for (const b of content) {
|
|
79
|
+
n += 1; // block 自身开销
|
|
80
|
+
n += estimate(contentToText([b]));
|
|
81
|
+
}
|
|
82
|
+
return n;
|
|
83
|
+
}
|
|
84
|
+
/** 整组消息的估算 token(含每条 role 开销)。 */
|
|
85
|
+
export function estimateMessages(messages, estimate = defaultEstimateTokens) {
|
|
86
|
+
let n = 0;
|
|
87
|
+
for (const m of messages) {
|
|
88
|
+
n += estimate(m.role);
|
|
89
|
+
n += contentTokens(m.content, estimate);
|
|
90
|
+
}
|
|
91
|
+
return n;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 增量 token 计数器(预算策略专用;**不属公共导出面**,故不进 index.ts)。
|
|
95
|
+
*
|
|
96
|
+
* 为什么需要:`estimateMessages` 是 O(消息数 × 块大小)(每个块还要 stringify),
|
|
97
|
+
* 而预算策略的 `beforeTurn` **每回合都要估一次**。历史只追加,若每回合从零重算,
|
|
98
|
+
* 就是 O(回合 × 上下文)。实测:160 回合时估算吃掉 run 全部框架 CPU 的约 88%、
|
|
99
|
+
* 累计 467ms(10→160 回合之间增长 ×103,明显超线性)。
|
|
100
|
+
*
|
|
101
|
+
* 这里缓存「已计过的前缀」,只对**新增消息**计数 —— 追加场景降为 O(上下文)。
|
|
102
|
+
* 数组引用变了、或长度变短(策略裁剪过 / 换了新数组)→ 自动从零重算,所以
|
|
103
|
+
* 在 `messages.splice(...)` 原地替换后也不会读到脏缓存。
|
|
104
|
+
*/
|
|
105
|
+
export function createTokenCounter(estimate = defaultEstimateTokens) {
|
|
106
|
+
let ref = null;
|
|
107
|
+
let counted = 0;
|
|
108
|
+
let tokens = 0;
|
|
109
|
+
/** 已计入区间**最后一个元素的对象标识** —— 用于发现「同数组、长度不减、内容却被换掉」 */
|
|
110
|
+
let boundary;
|
|
111
|
+
return function count(messages) {
|
|
112
|
+
const edge = counted > 0 ? messages[counted - 1] : undefined;
|
|
113
|
+
// 三重失效判据:换了数组 / 长度变短(被裁剪) / 边界元素已不是同一个对象
|
|
114
|
+
// (第三种覆盖自定义 contextPolicy 原地覆写同长度内容的情形 —— 只有长度判据会漏)
|
|
115
|
+
if (messages !== ref || messages.length < counted || edge !== boundary) {
|
|
116
|
+
ref = messages;
|
|
117
|
+
counted = 0;
|
|
118
|
+
tokens = 0;
|
|
119
|
+
}
|
|
120
|
+
for (let i = counted; i < messages.length; i++) {
|
|
121
|
+
tokens += estimate(messages[i].role);
|
|
122
|
+
tokens += contentTokens(messages[i].content, estimate);
|
|
123
|
+
}
|
|
124
|
+
counted = messages.length;
|
|
125
|
+
boundary = counted > 0 ? messages[counted - 1] : undefined;
|
|
126
|
+
return tokens;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** 把消息渲染成可喂给摘要器的纯文本(role: content)。 */
|
|
130
|
+
export function renderMessages(messages) {
|
|
131
|
+
return messages.map((m) => `${m.role}: ${contentToText(m.content)}`).join('\n\n');
|
|
132
|
+
}
|
|
133
|
+
/** assistant 消息里的 tool_use id 列表(无则空) */
|
|
134
|
+
function toolUseIds(msg) {
|
|
135
|
+
if (msg.role !== 'assistant' || typeof msg.content === 'string')
|
|
136
|
+
return [];
|
|
137
|
+
return msg.content
|
|
138
|
+
.filter((b) => b.type === 'tool_use')
|
|
139
|
+
.map((b) => b.id);
|
|
140
|
+
}
|
|
141
|
+
/** user 消息里的 tool_result 对应 id 列表(无则空) */
|
|
142
|
+
function toolResultIds(msg) {
|
|
143
|
+
if (msg.role !== 'user' || typeof msg.content === 'string')
|
|
144
|
+
return [];
|
|
145
|
+
return msg.content
|
|
146
|
+
.filter((b) => b.type === 'tool_result')
|
|
147
|
+
.map((b) => b.tool_use_id);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 历史是否「工具块严格成对」—— 只有这种历史才能安全地整对丢弃。
|
|
151
|
+
*
|
|
152
|
+
* 若历史非严格交替(例如连续两条 assistant 各带 tool_use,结果挤在第三条 user 里),
|
|
153
|
+
* 按「相邻性」配对会只丢掉后一对,把前一条 assistant 的 tool_use 变成**孤立块**
|
|
154
|
+
* → 下一次请求被 API 以 400 拒绝。检测到畸形即整体放弃裁剪,返回原数组更安全。
|
|
155
|
+
*/
|
|
156
|
+
function toolBlocksPaired(messages) {
|
|
157
|
+
for (let i = 0; i < messages.length; i++) {
|
|
158
|
+
const useIds = toolUseIds(messages[i]);
|
|
159
|
+
if (useIds.length > 0) {
|
|
160
|
+
const next = messages[i + 1];
|
|
161
|
+
if (!next || toolResultIds(next).length === 0)
|
|
162
|
+
return false; // tool_use 无对应结果消息
|
|
163
|
+
const resultIds = new Set(toolResultIds(next));
|
|
164
|
+
if (!useIds.every((id) => resultIds.has(id)))
|
|
165
|
+
return false; // 结果未覆盖全部 tool_use
|
|
166
|
+
}
|
|
167
|
+
const resultIds = toolResultIds(messages[i]);
|
|
168
|
+
if (resultIds.length > 0) {
|
|
169
|
+
const prev = messages[i - 1];
|
|
170
|
+
if (!prev || toolUseIds(prev).length === 0)
|
|
171
|
+
return false; // 孤立的 tool_result
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* context editing:丢弃旧的 tool_use→tool_result 对(超出 keepToolPairs 的),
|
|
178
|
+
* 保留最近 N 对以及所有非工具消息。逐对整体移除,保角色交替合法。
|
|
179
|
+
* 返回原数组引用(若无需裁剪)或新数组。
|
|
180
|
+
*
|
|
181
|
+
* 前置:历史必须是工具块严格成对的(toolBlocksPaired)。畸形历史直接返回原数组
|
|
182
|
+
* —— 宁可少裁剪,也不能切出孤立 tool_use/tool_result 让后续请求 400。
|
|
183
|
+
*/
|
|
184
|
+
export function trimToolPairs(messages, opts = {}) {
|
|
185
|
+
const keep = Math.max(0, opts.keepToolPairs ?? 1);
|
|
186
|
+
if (!toolBlocksPaired(messages))
|
|
187
|
+
return messages;
|
|
188
|
+
const pairs = [];
|
|
189
|
+
for (let i = 0; i + 1 < messages.length; i++) {
|
|
190
|
+
if (hasToolUse(messages[i]) && isToolResultMessage(messages[i + 1])) {
|
|
191
|
+
pairs.push([i, i + 1]);
|
|
192
|
+
i += 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (pairs.length <= keep)
|
|
196
|
+
return messages; // 无需裁剪(或本就留得下)
|
|
197
|
+
// 丢掉最旧的 (pairs.length - keep) 对,最近的 keep 对与所有非工具消息保留
|
|
198
|
+
const drop = new Set();
|
|
199
|
+
for (let k = 0; k < pairs.length - keep; k++) {
|
|
200
|
+
drop.add(pairs[k][0]);
|
|
201
|
+
drop.add(pairs[k][1]);
|
|
202
|
+
}
|
|
203
|
+
const out = messages.filter((_, i) => !drop.has(i));
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* compaction:把 messages 里“除最近 keepRecent 条”的旧前缀做摘要,替换成一段
|
|
208
|
+
* 摘要消息,尾部保留。产出保证:
|
|
209
|
+
* - 不拆散 (assistant tool_use → user tool_result) 对 —— cut 落到对中间时整体后移;
|
|
210
|
+
* - 角色交替合法 —— 尾段首条是普通 user 时摘要并入之;否则摘要以 user 角色放最前。
|
|
211
|
+
*/
|
|
212
|
+
export async function compactMessages(messages, opts) {
|
|
213
|
+
const keepRecent = Math.max(1, opts.keepRecent ?? 20);
|
|
214
|
+
if (messages.length <= keepRecent)
|
|
215
|
+
return messages;
|
|
216
|
+
let cut = messages.length - keepRecent;
|
|
217
|
+
// cut 若落在一对 tool_result 的开头(前一条是带 tool_use 的 assistant),整对后移进保留段
|
|
218
|
+
while (cut > 1 && cut < messages.length && isToolResultMessage(messages[cut])) {
|
|
219
|
+
cut -= 1;
|
|
220
|
+
}
|
|
221
|
+
const prefix = messages.slice(0, cut);
|
|
222
|
+
const tail = messages.slice(cut);
|
|
223
|
+
const summary = await opts.summarize(renderMessages(prefix));
|
|
224
|
+
const label = `[此前对话摘要]\n${summary}`;
|
|
225
|
+
// tail 必非空:keepRecent ≥ 1 且上面已早返回 messages.length <= keepRecent,
|
|
226
|
+
// 故 cut ∈ [1, messages.length),slice(cut) 至少一条。
|
|
227
|
+
const [first, ...rest] = tail;
|
|
228
|
+
if (first.role === 'user' && !isToolResultMessage(first)) {
|
|
229
|
+
// 并入首条普通 user 消息,保持 user→assistant 交替
|
|
230
|
+
const merged = typeof first.content === 'string'
|
|
231
|
+
? { ...first, content: `${label}\n\n${first.content}` }
|
|
232
|
+
: { ...first, content: [{ type: 'text', text: label }, ...first.content] };
|
|
233
|
+
return [merged, ...rest];
|
|
234
|
+
}
|
|
235
|
+
// 尾段以 assistant 开头:摘要作首条 user(user→assistant 交替合法)
|
|
236
|
+
return [{ role: 'user', content: label }, ...tail];
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=trimming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trimming.js","sourceRoot":"","sources":["../../src/engine/trimming.ts"],"names":[],"mappings":"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"}
|
package/dist/engine/types.d.ts
CHANGED
|
@@ -1,9 +1,35 @@
|
|
|
1
1
|
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import type { AgentTool, JsonSchema, ModelClient } from '../core/tool.js';
|
|
2
|
+
import type { AgentTool, JsonSchema, ModelClient, ModelPricing } from '../core/tool.js';
|
|
3
3
|
import type { SpanError, Trace } from '../core/trace.js';
|
|
4
|
+
import type { RetryOptions } from './retry.js';
|
|
5
|
+
/**
|
|
6
|
+
* engine 对模型端的最小结构面(R4 多模型):Anthropic SDK 天然满足,
|
|
7
|
+
* 其他 provider(OpenAI 兼容端点等)只需适配出同一形态。
|
|
8
|
+
* 定义在 core/tool.js 并从此处转导出。
|
|
9
|
+
*/
|
|
4
10
|
export type { ModelClient } from '../core/tool.js';
|
|
5
|
-
export type AgentStopReason =
|
|
6
|
-
/**
|
|
11
|
+
export type AgentStopReason =
|
|
12
|
+
/** 模型自然结束(含 stop_sequence:命中 stop 序列同样是正常收尾) */
|
|
13
|
+
'end_turn' | 'stop_sequence' | 'max_tokens' | 'refusal' | 'pause_turn' | 'max_iterations'
|
|
14
|
+
/** 调用方主动取消(AbortSignal):run 未跑完,按失败收尾 */
|
|
15
|
+
| 'aborted'
|
|
16
|
+
/**
|
|
17
|
+
* 成本硬管控触发(C1):累计 token/成本超限,记账后主动停 run。
|
|
18
|
+
* **算失败**(run 没跑完)—— 与 `max_iterations` 同类:是护栏拦下的,不是正常收尾。
|
|
19
|
+
*/
|
|
20
|
+
| 'budget_exceeded'
|
|
21
|
+
/** stop_reason=tool_use 但回合里没有可执行块(畸形响应),防死循环直接停 */
|
|
22
|
+
| 'tool_use_no_blocks'
|
|
23
|
+
/** 模型/网关返回了本框架未识别的 stop_reason:保留文本,但按失败收尾 */
|
|
24
|
+
| 'unknown_stop_reason' | 'error';
|
|
25
|
+
/**
|
|
26
|
+
* 是否「正常收尾」:end_turn(自然结束)与 stop_sequence(命中 stop 序列)都算。
|
|
27
|
+
* run 状态机 / trace 状态 / 子 agent 交回判定共用这一把尺子 —— 三处各写各的
|
|
28
|
+
* `=== 'end_turn'` 时,新增一个正常收尾原因就会漏改其中一处(stop_sequence 落地时
|
|
29
|
+
* 就出现过:loop 判成功、Run 判失败)。
|
|
30
|
+
*/
|
|
31
|
+
export declare function isSuccessStopReason(reason: AgentStopReason): boolean;
|
|
32
|
+
/** 可携带 cache_control 的 system 文本块(见 runtime/systemPrompt.ts) */
|
|
7
33
|
export interface SystemTextBlock {
|
|
8
34
|
type: 'text';
|
|
9
35
|
text: string;
|
|
@@ -26,12 +52,7 @@ export interface ContextPolicy {
|
|
|
26
52
|
model: string;
|
|
27
53
|
}): Promise<Anthropic.MessageParam[]>;
|
|
28
54
|
}
|
|
29
|
-
|
|
30
|
-
* engine 对模型端的最小结构面(R4 多模型):Anthropic SDK 天然满足,
|
|
31
|
-
* 其他 provider(OpenAI 兼容端点等)只需适配出同一形态。
|
|
32
|
-
* 定义在 core/tool.js 并从此处转导出。
|
|
33
|
-
*/
|
|
34
|
-
export interface RunAgentOptions {
|
|
55
|
+
export interface RunAgentOptions<S extends JsonSchema = JsonSchema> {
|
|
35
56
|
/** 顶层 system(SystemPrompt 产物)。稳定内容应放在 tools 之后、第一个 breakpoint 前 */
|
|
36
57
|
system?: SystemParam;
|
|
37
58
|
/** 初始消息;由调用方给 user 起始消息 */
|
|
@@ -49,17 +70,89 @@ export interface RunAgentOptions {
|
|
|
49
70
|
recorder?: import('./tracer.js').TraceRecorder;
|
|
50
71
|
/** 文本增量回调(终端/SSE 用) */
|
|
51
72
|
onText?: (delta: string) => void;
|
|
73
|
+
/** 中断信号:中止则本回合结束后以 stopReason='aborted' 收尾(不抛异常) */
|
|
74
|
+
signal?: AbortSignal;
|
|
52
75
|
runName?: string;
|
|
53
76
|
/** 上下文预算策略:每回合发送前可编辑/压缩消息(compaction / context editing) */
|
|
54
77
|
contextPolicy?: ContextPolicy;
|
|
78
|
+
/**
|
|
79
|
+
* 模型请求的重试策略(spec §6.5 确定性工程)。缺省**开启**
|
|
80
|
+
* (maxAttempts=3、指数退避 + 抖动);`false` 关闭。
|
|
81
|
+
* 只重试「尚未产出文本」的可重试失败(429 / 5xx / 连接失败)。
|
|
82
|
+
*
|
|
83
|
+
* 注意:子 agent / skill 内部的 llm 调用走各自的缺省策略,不受单次 run 的 `false` 影响。
|
|
84
|
+
*/
|
|
85
|
+
retry?: RetryOptions | false;
|
|
55
86
|
/**
|
|
56
87
|
* 结构化结果 schema(R2):给出后 engine 追加隐藏工具 submit_result,
|
|
57
88
|
* 模型调用它提交符合 schema 的最终结果,校验通过即结束循环并写入 AgentRunResult.typed;
|
|
58
89
|
* 模型始终未提交则 typed 为 undefined(行为与不设时一致)。
|
|
90
|
+
*
|
|
91
|
+
* 泛型 S:传 `fromZod<T>(...)`(TypedSchema<T>)时,返回值 `typed` 自动是 `T | undefined`;
|
|
92
|
+
* 传裸 JsonSchema 时回落 `unknown`。
|
|
59
93
|
*/
|
|
60
|
-
resultSchema?:
|
|
94
|
+
resultSchema?: S;
|
|
95
|
+
/**
|
|
96
|
+
* 成本硬管控(C1):整条 run(**含子 agent**)累计 token 上限。每回合记账后判断,
|
|
97
|
+
* 超限即停,run 以 `stopReason='budget_exceeded'` 收尾(**算失败**)。
|
|
98
|
+
* 与 `contextPolicy`(发送前的上下文裁剪)分工不同 —— 见 `createBudgetGuard`。
|
|
99
|
+
*
|
|
100
|
+
* 口径:input + output + cacheRead + cacheCreation。**不是硬实时**:一回合跑完才判,
|
|
101
|
+
* 所以实际用量可能略超上限(超一次回合的量)。
|
|
102
|
+
*/
|
|
103
|
+
maxTotalTokens?: number;
|
|
104
|
+
/**
|
|
105
|
+
* 成本硬管控(C1):累计成本(美元)上限。**依赖模型在价格表内**
|
|
106
|
+
* (`engine/usage.ts` 的 DEFAULT_PRICING,或本 run 的 `priceOverrides`)——
|
|
107
|
+
* 不在表里时成本恒为 0,此护栏不触发;要无条件兜底用 maxTotalTokens。
|
|
108
|
+
* 未定价模型会在 run 根记 `usage.unpriced` 事件(见 `onUnpricedModel`),
|
|
109
|
+
* 所以"护栏到底有没有生效"是**看得见**的。
|
|
110
|
+
*/
|
|
111
|
+
maxCostUsd?: number;
|
|
112
|
+
/**
|
|
113
|
+
* 价格表覆盖/追加($/1M tokens):覆盖内置同名项,或给非 Anthropic 模型定价
|
|
114
|
+
* (如 `{ 'deepseek-chat': { in: 0.27, out: 1.10 } }`)。见 `buildPricing`。
|
|
115
|
+
*
|
|
116
|
+
* 会**透传给嵌套能力**(@SubAgent / @Skill 的子循环),所以子 agent 用同一个模型
|
|
117
|
+
* 也能算成本 —— 不会出现「主 agent 有成本、子 agent 恒 0」的割裂。
|
|
118
|
+
* 非法单价在 run 开始时抛错(不静默算出 NaN)。
|
|
119
|
+
*/
|
|
120
|
+
priceOverrides?: Record<string, ModelPricing>;
|
|
121
|
+
/**
|
|
122
|
+
* 遇到不在价格表内的模型时的回调(**每个循环作用域内每模型一次**,去重后调用;
|
|
123
|
+
* 主 agent 与每个子 agent 各算一个作用域)。框架同时在该 turn span 上记
|
|
124
|
+
* `usage.unpriced` 事件 —— 成本护栏的失效不再静默。
|
|
125
|
+
*
|
|
126
|
+
* 回调抛错被吞掉(观测是辅助动作,不影响 run)。**不会**改变 run 结局:
|
|
127
|
+
* 定价缺失是宿主配置问题,不该把一次成功的 run 打成失败。
|
|
128
|
+
*/
|
|
129
|
+
onUnpricedModel?: (info: {
|
|
130
|
+
model: string;
|
|
131
|
+
spanId: string;
|
|
132
|
+
}) => void;
|
|
133
|
+
/**
|
|
134
|
+
* 单个工具执行的超时(毫秒);缺省 0 = 不限。超时**不杀 run**:
|
|
135
|
+
* 该条 tool_result 记 `is_error` 回给模型(与「工具抛错不中断 run」同语义,模型可自行换路)。
|
|
136
|
+
*
|
|
137
|
+
* ⚠️ 超时 = **放弃等待**,不是取消工具:`AgentTool.run` 没有 signal 参数,
|
|
138
|
+
* 副作用可能已经发生。想真停的工具请自行读 `ToolRunContext.signal`。
|
|
139
|
+
*/
|
|
140
|
+
toolTimeoutMs?: number;
|
|
141
|
+
/**
|
|
142
|
+
* 同一回合内并行工具调用的上限;缺省 `Infinity`(= 旧行为,全部并行)。
|
|
143
|
+
* 工具会打外部系统(DB/HTTP)时设个位数,避免一个回合把下游打爆。
|
|
144
|
+
*/
|
|
145
|
+
maxToolConcurrency?: number;
|
|
146
|
+
/**
|
|
147
|
+
* 提示词版本号(D4):写进 run 根 span 的 `system.version` attribute,
|
|
148
|
+
* trace 里据此可查「哪个版本的提示词产出的结果」。
|
|
149
|
+
*
|
|
150
|
+
* 走 `AgentApp.run` 时**不用手填** —— `system` 给 `SystemPrompt({ version })` 实例
|
|
151
|
+
* 就自动带上(见 runtime/systemPrompt.ts)。
|
|
152
|
+
*/
|
|
153
|
+
systemVersion?: string;
|
|
61
154
|
}
|
|
62
|
-
export interface AgentRunResult {
|
|
155
|
+
export interface AgentRunResult<T = unknown> {
|
|
63
156
|
/** 本次 run 的完整调用树 + usage(traceId == runId) */
|
|
64
157
|
trace: Trace;
|
|
65
158
|
stopReason: AgentStopReason;
|
|
@@ -67,6 +160,9 @@ export interface AgentRunResult {
|
|
|
67
160
|
finalText: string;
|
|
68
161
|
iterations: number;
|
|
69
162
|
error?: SpanError;
|
|
70
|
-
/**
|
|
71
|
-
|
|
163
|
+
/**
|
|
164
|
+
* resultSchema 校验通过的结构化结果;模型没提交(或未设 resultSchema)则为 undefined。
|
|
165
|
+
* 类型由 resultSchema 推导(见 RunAgentOptions.resultSchema 的泛型说明)。
|
|
166
|
+
*/
|
|
167
|
+
typed?: T;
|
|
72
168
|
}
|
package/dist/engine/types.js
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 是否「正常收尾」:end_turn(自然结束)与 stop_sequence(命中 stop 序列)都算。
|
|
3
|
+
* run 状态机 / trace 状态 / 子 agent 交回判定共用这一把尺子 —— 三处各写各的
|
|
4
|
+
* `=== 'end_turn'` 时,新增一个正常收尾原因就会漏改其中一处(stop_sequence 落地时
|
|
5
|
+
* 就出现过:loop 判成功、Run 判失败)。
|
|
6
|
+
*/
|
|
7
|
+
export function isSuccessStopReason(reason) {
|
|
8
|
+
return reason === 'end_turn' || reason === 'stop_sequence';
|
|
9
|
+
}
|
|
2
10
|
//# sourceMappingURL=types.js.map
|
package/dist/engine/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"AAiCA;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAuB;IACzD,OAAO,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,eAAe,CAAC;AAC7D,CAAC"}
|
package/dist/engine/usage.d.ts
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import type { ModelPricing } from '../core/tool.js';
|
|
2
3
|
import type { Usage } from '../core/trace.js';
|
|
4
|
+
/**
|
|
5
|
+
* Agentia —— 模型用量与成本估算(spec §6.4 成本硬管控的地基)。
|
|
6
|
+
*
|
|
7
|
+
* **成本是"可调优"的一部分,但历史实现有两处硬伤**(可观测·可调优设计 F1/F2):
|
|
8
|
+
* 1. 价格表是模块内常量 → 走 OpenAI 兼容端点(DeepSeek / 自建)的宿主**永远算不出成本**;
|
|
9
|
+
* 2. 未定价模型返回 `undefined` → 成本恒 0 → `maxCostUsd` 这条护栏**静默失效**、且无任何提示。
|
|
10
|
+
*
|
|
11
|
+
* 现在:价格表可经 `priceOverrides` 覆盖/追加(`buildPricing`),未定价模型会在 run 根
|
|
12
|
+
* 记 `usage.unpriced` 事件并有 `agentia_model_unpriced_turns_total` 指标 —— 护栏是否
|
|
13
|
+
* 真的生效**看得见**。
|
|
14
|
+
*/
|
|
15
|
+
/** 内置价格表($/1M tokens)。宿主用 `priceOverrides` 覆盖同名项或追加新模型。 */
|
|
16
|
+
export declare const DEFAULT_PRICING: Readonly<Record<string, ModelPricing>>;
|
|
17
|
+
/**
|
|
18
|
+
* 合并内置表与宿主覆盖(**覆盖优先**);返回合并后的表。
|
|
19
|
+
* 不给覆盖时直接复用内置表(同一引用,零拷贝)。
|
|
20
|
+
*
|
|
21
|
+
* 非法单价(非有限数 / 负数)在**构造期抛错** —— 与其让它算出 NaN 成本、再让
|
|
22
|
+
* `maxCostUsd` 拿 NaN 去比较(永远 false,护栏静默失效),不如立刻响亮失败。
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildPricing(overrides?: Record<string, ModelPricing>): Record<string, ModelPricing>;
|
|
3
25
|
export declare function usageFromAnthropic(u: Anthropic.Usage): Usage;
|
|
4
|
-
/**
|
|
5
|
-
|
|
26
|
+
/**
|
|
27
|
+
* 近似成本估计:缓存写≈1.25×输入,缓存读≈0.1×输入。
|
|
28
|
+
* 模型不在传入的价格表内时返回 `undefined` —— 调用方须把"未定价"**显式**表达出来
|
|
29
|
+
* (记 `usage.unpriced` 事件 / 计数),不要当成"成本 0"。
|
|
30
|
+
*/
|
|
31
|
+
export declare function costEstimate(model: string, usage: Usage, pricing?: Record<string, ModelPricing>): number | undefined;
|
package/dist/engine/usage.js
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Agentia —— 模型用量与成本估算(spec §6.4 成本硬管控的地基)。
|
|
3
|
+
*
|
|
4
|
+
* **成本是"可调优"的一部分,但历史实现有两处硬伤**(可观测·可调优设计 F1/F2):
|
|
5
|
+
* 1. 价格表是模块内常量 → 走 OpenAI 兼容端点(DeepSeek / 自建)的宿主**永远算不出成本**;
|
|
6
|
+
* 2. 未定价模型返回 `undefined` → 成本恒 0 → `maxCostUsd` 这条护栏**静默失效**、且无任何提示。
|
|
7
|
+
*
|
|
8
|
+
* 现在:价格表可经 `priceOverrides` 覆盖/追加(`buildPricing`),未定价模型会在 run 根
|
|
9
|
+
* 记 `usage.unpriced` 事件并有 `agentia_model_unpriced_turns_total` 指标 —— 护栏是否
|
|
10
|
+
* 真的生效**看得见**。
|
|
11
|
+
*/
|
|
12
|
+
/** 内置价格表($/1M tokens)。宿主用 `priceOverrides` 覆盖同名项或追加新模型。 */
|
|
13
|
+
export const DEFAULT_PRICING = Object.freeze({
|
|
3
14
|
'claude-opus-5': { in: 5, out: 25 },
|
|
4
15
|
'claude-opus-4-8': { in: 5, out: 25 },
|
|
5
16
|
'claude-opus-4-7': { in: 5, out: 25 },
|
|
6
17
|
'claude-sonnet-5': { in: 2, out: 10 },
|
|
7
18
|
'claude-sonnet-4-6': { in: 3, out: 15 },
|
|
8
19
|
'claude-haiku-4-5': { in: 1, out: 5 },
|
|
9
|
-
};
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* 合并内置表与宿主覆盖(**覆盖优先**);返回合并后的表。
|
|
23
|
+
* 不给覆盖时直接复用内置表(同一引用,零拷贝)。
|
|
24
|
+
*
|
|
25
|
+
* 非法单价(非有限数 / 负数)在**构造期抛错** —— 与其让它算出 NaN 成本、再让
|
|
26
|
+
* `maxCostUsd` 拿 NaN 去比较(永远 false,护栏静默失效),不如立刻响亮失败。
|
|
27
|
+
*/
|
|
28
|
+
export function buildPricing(overrides) {
|
|
29
|
+
if (!overrides)
|
|
30
|
+
return DEFAULT_PRICING;
|
|
31
|
+
for (const [model, p] of Object.entries(overrides)) {
|
|
32
|
+
const ok = (n) => typeof n === 'number' && Number.isFinite(n) && n >= 0;
|
|
33
|
+
if (!p || !ok(p.in) || !ok(p.out)) {
|
|
34
|
+
throw new Error(`priceOverrides["${model}"] 非法:需要 { in, out } 两个非负有限数($/1M tokens),收到 ${JSON.stringify(p)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { ...DEFAULT_PRICING, ...overrides };
|
|
38
|
+
}
|
|
10
39
|
export function usageFromAnthropic(u) {
|
|
11
40
|
return {
|
|
12
41
|
inputTokens: u.input_tokens ?? 0,
|
|
@@ -15,9 +44,13 @@ export function usageFromAnthropic(u) {
|
|
|
15
44
|
cacheCreationTokens: u.cache_creation_input_tokens ?? 0,
|
|
16
45
|
};
|
|
17
46
|
}
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
47
|
+
/**
|
|
48
|
+
* 近似成本估计:缓存写≈1.25×输入,缓存读≈0.1×输入。
|
|
49
|
+
* 模型不在传入的价格表内时返回 `undefined` —— 调用方须把"未定价"**显式**表达出来
|
|
50
|
+
* (记 `usage.unpriced` 事件 / 计数),不要当成"成本 0"。
|
|
51
|
+
*/
|
|
52
|
+
export function costEstimate(model, usage, pricing = DEFAULT_PRICING) {
|
|
53
|
+
const p = pricing[model];
|
|
21
54
|
if (!p)
|
|
22
55
|
return undefined;
|
|
23
56
|
const { inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens } = usage;
|
package/dist/engine/usage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/engine/usage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/engine/usage.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,eAAe,GAA2C,MAAM,CAAC,MAAM,CAAC;IACnF,eAAe,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;IACnC,iBAAiB,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;IACrC,iBAAiB,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;IACrC,iBAAiB,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;IACrC,mBAAmB,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;IACvC,kBAAkB,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE;CACtC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAwC;IAExC,IAAI,CAAC,SAAS;QAAE,OAAO,eAA+C,CAAC;IACvE,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACnD,MAAM,EAAE,GAAG,CAAC,CAAU,EAAW,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1F,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CACb,mBAAmB,KAAK,gDAAgD,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAC5F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,EAAE,GAAG,eAAe,EAAE,GAAG,SAAS,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,CAAkB;IACnD,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,YAAY,IAAI,CAAC;QAChC,YAAY,EAAE,CAAC,CAAC,aAAa,IAAI,CAAC;QAClC,eAAe,EAAE,CAAC,CAAC,uBAAuB,IAAI,CAAC;QAC/C,mBAAmB,EAAE,CAAC,CAAC,2BAA2B,IAAI,CAAC;KACxD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAa,EACb,KAAY,EACZ,OAAO,GAAiC,eAA+C;IAEvF,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,GAAG,KAAK,CAAC;IAClF,MAAM,IAAI,GACR,CAAC,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;QAC1B,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG;QAC5B,CAAC,eAAe,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG;QACpC,CAAC,mBAAmB,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC;IAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AgentRunResult } from '../engine/types.js';
|
|
2
|
+
import type { Trace } from '../core/trace.js';
|
|
3
|
+
import type { ModelClient } from '../core/tool.js';
|
|
4
|
+
import type { AgentApp, RunAppOptions } from '../toolkit/module.js';
|
|
5
|
+
/**
|
|
6
|
+
* Agentia —— evals(D2):把「agent 有没有回归」变成断言。
|
|
7
|
+
*
|
|
8
|
+
* 不引测试框架:`defineEval(...).run()` 跑完给一份 `EvalReport`(通过/失败 + 失败
|
|
9
|
+
* case 的 trace),接 CI 只要看 `report.ok`。断言源是既有的 `Trace` —— 「先调 search
|
|
10
|
+
* 才调 summarize」这类顺序断言全从 trace 里读,框架不为此新增埋点
|
|
11
|
+
* (`traceToMessages` 也能把 trace 还原成 messages,做更细的重放断言)。
|
|
12
|
+
*/
|
|
13
|
+
export interface EvalCase {
|
|
14
|
+
/** 用例名(报告里用);缺省 `case#<序号>` */
|
|
15
|
+
name?: string;
|
|
16
|
+
/** 本用例的用户输入(框架会包成单条 user message) */
|
|
17
|
+
input: string;
|
|
18
|
+
/** 本用例的模型脚本:`scriptedClient([...])` */
|
|
19
|
+
client: ModelClient;
|
|
20
|
+
/**
|
|
21
|
+
* 单次 run 选项(同 `app.run(messages, opts)`),用于给单个用例注入
|
|
22
|
+
* `resultSchema`(断言 `result.typed`)、`maxIterations`、`blackboard` 等。
|
|
23
|
+
* `client` 由 eval 自己填(本字段里的 client 会被忽略)。
|
|
24
|
+
*/
|
|
25
|
+
opts?: RunAppOptions;
|
|
26
|
+
}
|
|
27
|
+
/** 断言上下文:`trace` 与 `result.trace` 是同一份(此处并列给出,读起来更顺) */
|
|
28
|
+
export interface EvalContext {
|
|
29
|
+
trace: Trace;
|
|
30
|
+
}
|
|
31
|
+
export interface EvalCaseReport {
|
|
32
|
+
name: string;
|
|
33
|
+
ok: boolean;
|
|
34
|
+
/** 跑完才有(断言失败时也有 —— 断言抛错不吞掉结果) */
|
|
35
|
+
stopReason?: string;
|
|
36
|
+
/** 失败原因(断言抛出的 message,或 app.run 抛出的错) */
|
|
37
|
+
error?: string;
|
|
38
|
+
/** 失败 case 的完整 trace(能拿到就给)—— 排查回归就看它 */
|
|
39
|
+
trace?: Trace;
|
|
40
|
+
}
|
|
41
|
+
export interface EvalReport {
|
|
42
|
+
name: string;
|
|
43
|
+
total: number;
|
|
44
|
+
passed: number;
|
|
45
|
+
failed: number;
|
|
46
|
+
/** 全通过才 true(`total === 0` 视为通过:没有用例就没有回归) */
|
|
47
|
+
ok: boolean;
|
|
48
|
+
cases: EvalCaseReport[];
|
|
49
|
+
}
|
|
50
|
+
export interface EvalDefinition<T = unknown> {
|
|
51
|
+
name: string;
|
|
52
|
+
/**
|
|
53
|
+
* 建应用。**一次 `run()` 只调一次**(工具菜单/DI 实例在用例间复用 ——
|
|
54
|
+
* 每个用例都重建应用会掩盖「装配期状态泄漏」这类问题,而且慢)。
|
|
55
|
+
* 需要每用例新建就自己 `app: () => createApp(...)` 返回新实例。
|
|
56
|
+
*/
|
|
57
|
+
app: () => Promise<AgentApp> | AgentApp;
|
|
58
|
+
cases: EvalCase[];
|
|
59
|
+
/**
|
|
60
|
+
* 断言。抛错 = 该用例失败(`assert` 原生就够用,不引断言库)。
|
|
61
|
+
* 返回 Promise 也行(要读外部系统时)。
|
|
62
|
+
*/
|
|
63
|
+
expect: (r: AgentRunResult<T>, ctx: EvalContext) => void | Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 定义一个 eval。`run()` **不会因用例失败而抛出** —— 失败进报告(这样一次跑完
|
|
67
|
+
* 能看到所有回归,而不是修一个跑一次)。只有「应用建不起来」才冒泡(那是环境错误,
|
|
68
|
+
* 不是回归)。
|
|
69
|
+
*/
|
|
70
|
+
export declare function defineEval<T = unknown>(def: EvalDefinition<T>): {
|
|
71
|
+
name: string;
|
|
72
|
+
run(): Promise<EvalReport>;
|
|
73
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
function messageOf(e) {
|
|
2
|
+
return e instanceof Error ? e.message : String(e);
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* 定义一个 eval。`run()` **不会因用例失败而抛出** —— 失败进报告(这样一次跑完
|
|
6
|
+
* 能看到所有回归,而不是修一个跑一次)。只有「应用建不起来」才冒泡(那是环境错误,
|
|
7
|
+
* 不是回归)。
|
|
8
|
+
*/
|
|
9
|
+
export function defineEval(def) {
|
|
10
|
+
return {
|
|
11
|
+
name: def.name,
|
|
12
|
+
async run() {
|
|
13
|
+
const app = await def.app();
|
|
14
|
+
const cases = [];
|
|
15
|
+
for (let i = 0; i < def.cases.length; i++) {
|
|
16
|
+
const c = def.cases[i];
|
|
17
|
+
const label = c.name ?? `case#${i + 1}`;
|
|
18
|
+
const messages = [{ role: 'user', content: c.input }];
|
|
19
|
+
let report = { name: label, ok: false };
|
|
20
|
+
try {
|
|
21
|
+
const { result } = await app.run(messages, { ...c.opts, client: c.client });
|
|
22
|
+
const typed = result;
|
|
23
|
+
// 先落 stopReason/trace 再断言:断言失败时报告里仍带着「跑出来长什么样」
|
|
24
|
+
report = { name: label, ok: true, stopReason: result.stopReason, trace: result.trace };
|
|
25
|
+
await def.expect(typed, { trace: result.trace });
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
report = { ...report, ok: false, error: messageOf(e) };
|
|
29
|
+
}
|
|
30
|
+
cases.push(report);
|
|
31
|
+
}
|
|
32
|
+
const passed = cases.filter((c) => c.ok).length;
|
|
33
|
+
return {
|
|
34
|
+
name: def.name,
|
|
35
|
+
total: cases.length,
|
|
36
|
+
passed,
|
|
37
|
+
failed: cases.length - passed,
|
|
38
|
+
ok: passed === cases.length,
|
|
39
|
+
cases,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=defineEval.js.map
|