@yeaft/webchat-agent 0.1.774 → 0.1.776
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/package.json
CHANGED
package/unify/engine.js
CHANGED
|
@@ -1230,8 +1230,8 @@ export class Engine {
|
|
|
1230
1230
|
// `turnStartIdx` is where the current user message lives; the arc
|
|
1231
1231
|
// we may collapse spans (arcStartIdx .. last assistant/tool).
|
|
1232
1232
|
//
|
|
1233
|
-
// Periodic-T1 fix: T1 must fire EVERY TOOL_BATCH_SIZE (
|
|
1234
|
-
// calls, not just the first
|
|
1233
|
+
// Periodic-T1 fix: T1 must fire EVERY TOOL_BATCH_SIZE (30) tool
|
|
1234
|
+
// calls, not just the first batch. So instead of a one-shot boolean,
|
|
1235
1235
|
// track:
|
|
1236
1236
|
// • `lastT1AtToolCount` — toolCount snapshot at the last T1
|
|
1237
1237
|
// ATTEMPT (success OR error). Trigger when
|
|
@@ -1797,6 +1797,7 @@ export class Engine {
|
|
|
1797
1797
|
originalUserMsg: prompt,
|
|
1798
1798
|
toolPairs: pairs,
|
|
1799
1799
|
assistantText,
|
|
1800
|
+
language: this.#config.language,
|
|
1800
1801
|
signal,
|
|
1801
1802
|
});
|
|
1802
1803
|
// Detach: never await. The promise outlives this query() and
|
|
@@ -2034,9 +2035,9 @@ export class Engine {
|
|
|
2034
2035
|
}
|
|
2035
2036
|
|
|
2036
2037
|
// PR-L: T1 in-turn (synchronous) reflection. Fires once per
|
|
2037
|
-
// adapter loop iteration where ≥ TOOL_BATCH_SIZE (
|
|
2038
|
+
// adapter loop iteration where ≥ TOOL_BATCH_SIZE (30) tool
|
|
2038
2039
|
// calls have accumulated since the last T1 firing — not just
|
|
2039
|
-
// the first
|
|
2040
|
+
// the first batch of the query(). Generates a markdown reflection
|
|
2040
2041
|
// over the assistant+tool arc since the last T1 firing (or
|
|
2041
2042
|
// since the user prompt for the first batch) and rewrites that
|
|
2042
2043
|
// range to a SINGLE synthetic user message before the next
|
|
@@ -2044,10 +2045,10 @@ export class Engine {
|
|
|
2044
2045
|
//
|
|
2045
2046
|
// Loop semantics:
|
|
2046
2047
|
// - First batch: arcStartIdx = turnStartIdx + 1, fires when
|
|
2047
|
-
// queryToolCount reaches
|
|
2048
|
+
// queryToolCount reaches TOOL_BATCH_SIZE.
|
|
2048
2049
|
// - Each subsequent batch: arcStartIdx is updated to the slot
|
|
2049
2050
|
// right after the just-inserted reflection message; fires
|
|
2050
|
-
// again whenever
|
|
2051
|
+
// again whenever TOOL_BATCH_SIZE more tools have run since
|
|
2051
2052
|
// lastT1AtToolCount.
|
|
2052
2053
|
// - The dedup Set key includes `lastT1AtToolCount` so each
|
|
2053
2054
|
// batch within the same query gets a distinct entry — without
|
|
@@ -2084,6 +2085,7 @@ export class Engine {
|
|
|
2084
2085
|
originalUserMsg: prompt,
|
|
2085
2086
|
toolPairs: pairs,
|
|
2086
2087
|
assistantText,
|
|
2088
|
+
language: this.#config.language,
|
|
2087
2089
|
signal,
|
|
2088
2090
|
});
|
|
2089
2091
|
const next = collapseRangeToReflection(
|
|
@@ -2129,8 +2131,8 @@ export class Engine {
|
|
|
2129
2131
|
};
|
|
2130
2132
|
// Advance lastT1AtToolCount past this batch so we don't
|
|
2131
2133
|
// tight-loop on a hiccuping reflector. The next attempt is
|
|
2132
|
-
//
|
|
2133
|
-
// alone because history wasn't rewritten — the tail still
|
|
2134
|
+
// TOOL_BATCH_SIZE tools from now, not immediately. arcStartIdx is
|
|
2135
|
+
// left alone because history wasn't rewritten — the tail still
|
|
2134
2136
|
// begins where it did. The trade-off: the next batch's
|
|
2135
2137
|
// reflection will cover the tools that just failed too,
|
|
2136
2138
|
// which is fine (they're still in conversationMessages).
|
|
@@ -9,14 +9,25 @@
|
|
|
9
9
|
* - Duplicate-reminder text formatter
|
|
10
10
|
*
|
|
11
11
|
* The constants are NOT config-driven — V7 design freezes them in code.
|
|
12
|
+
*
|
|
13
|
+
* TOOL_BATCH_SIZE history: was 13 originally; raised to 30 (2026-05-15)
|
|
14
|
+
* after user feedback that 13 fired too often inside a single task and
|
|
15
|
+
* fragmented otherwise-coherent tool arcs into multiple reflections. 30
|
|
16
|
+
* keeps the periodic-reflection contract (it still fires every N tools,
|
|
17
|
+
* not just once) but gives a single task arc room to breathe before the
|
|
18
|
+
* arc gets collapsed.
|
|
12
19
|
*/
|
|
13
20
|
|
|
14
|
-
export const TOOL_BATCH_SIZE =
|
|
21
|
+
export const TOOL_BATCH_SIZE = 30;
|
|
15
22
|
export const TURN_SUMMARY_THRESHOLD = 5;
|
|
16
23
|
export const DUP_TOOL_THRESHOLD = 3;
|
|
17
24
|
|
|
18
25
|
export { ExecLog, buildEntry, argsHashOf } from './exec-log.js';
|
|
19
|
-
export {
|
|
26
|
+
export {
|
|
27
|
+
buildReflectionPrompt,
|
|
28
|
+
REFLECTION_TEMPLATE_EN,
|
|
29
|
+
REFLECTION_TEMPLATE_ZH,
|
|
30
|
+
} from './reflection-prompt.js';
|
|
20
31
|
export { runT1Reflection } from './t1-reflector.js';
|
|
21
32
|
export { runT2Reflection } from './t2-reflector.js';
|
|
22
33
|
export { buildFallbackStub } from './fallback-stub.js';
|
|
@@ -5,9 +5,21 @@
|
|
|
5
5
|
* The primary model is asked to REFLECT on a sequence of tool calls — not
|
|
6
6
|
* just summarise. The output is markdown with five fixed sections so the
|
|
7
7
|
* frontend ReflectionCard can render each independently.
|
|
8
|
+
*
|
|
9
|
+
* Bilingual: the prompt itself is rendered in the user's language (en/zh)
|
|
10
|
+
* so the model's output (which the user reads inside the ReflectionCard
|
|
11
|
+
* and which the next adapter loop sees as a synthetic user message) is
|
|
12
|
+
* in the matching language. Falls back to English for any other lang
|
|
13
|
+
* value or when `language` is omitted.
|
|
14
|
+
*
|
|
15
|
+
* Section headings stay English in both templates because the frontend
|
|
16
|
+
* ReflectionCard parses them by literal string match ("## What was
|
|
17
|
+
* attempted" et al.) and a localised heading would break rendering.
|
|
8
18
|
*/
|
|
9
19
|
|
|
10
|
-
|
|
20
|
+
import { isZhLanguage } from '../prompts.js';
|
|
21
|
+
|
|
22
|
+
const TEMPLATE_EN = `You are reviewing a sequence of {N} tool calls executed by an AI agent.
|
|
11
23
|
Your job is NOT just to summarize, but to REFLECT.
|
|
12
24
|
|
|
13
25
|
Output as markdown with these exact sections:
|
|
@@ -39,19 +51,54 @@ User original request:
|
|
|
39
51
|
Tool execution sequence:
|
|
40
52
|
{toolCallsAndResults}`;
|
|
41
53
|
|
|
54
|
+
const TEMPLATE_ZH = `你正在复盘一个 AI agent 刚刚执行的 {N} 次工具调用。
|
|
55
|
+
你的任务不是简单总结,而是要"反思"。
|
|
56
|
+
|
|
57
|
+
请用 markdown 输出,必须严格包含以下五个段落(标题保持英文,便于前端解析):
|
|
58
|
+
|
|
59
|
+
## What was attempted
|
|
60
|
+
2-3 句话说明本批工具调用的目标和动作链路。
|
|
61
|
+
|
|
62
|
+
## Key findings
|
|
63
|
+
具体事实:路径、行号、ID、错误码(必须逐字保留原始值,不要改写)。
|
|
64
|
+
|
|
65
|
+
## Direction check
|
|
66
|
+
- 当前轨迹是否仍然贴合用户最初的请求?
|
|
67
|
+
- 是否有偏离 / 范围漂移?
|
|
68
|
+
- 是否有看起来重复或多余的工具调用?
|
|
69
|
+
- 是否有迹象表明陷入了无效循环?
|
|
70
|
+
|
|
71
|
+
## Suggested next direction
|
|
72
|
+
下一轮 loop 应该聚焦什么?应该避免什么?
|
|
73
|
+
|
|
74
|
+
## Tool execution log
|
|
75
|
+
紧凑列表:<tool_name> × <count>(附上值得注意的参数)。
|
|
76
|
+
|
|
77
|
+
注意:所有标识符、路径、URL、行号、错误消息必须逐字保留,不允许改写数据值。
|
|
78
|
+
|
|
79
|
+
用户最初的请求:
|
|
80
|
+
{originalUserMessage}
|
|
81
|
+
|
|
82
|
+
工具执行序列:
|
|
83
|
+
{toolCallsAndResults}`;
|
|
84
|
+
|
|
42
85
|
/**
|
|
43
86
|
* Render the prompt.
|
|
44
87
|
*
|
|
45
|
-
* @param {{ originalUserMsg: string, toolPairs: Array<{ name: string, input: any, output: string, isError: boolean }>, assistantText?: string }} p
|
|
88
|
+
* @param {{ originalUserMsg: string, toolPairs: Array<{ name: string, input: any, output: string, isError: boolean }>, assistantText?: string, language?: string }} p
|
|
46
89
|
* @returns {string}
|
|
47
90
|
*/
|
|
48
|
-
export function buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText }) {
|
|
91
|
+
export function buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText, language }) {
|
|
49
92
|
const N = toolPairs.length;
|
|
50
93
|
const seq = toolPairs.map((p, i) => formatPair(i + 1, p)).join('\n\n');
|
|
94
|
+
const isZh = isZhLanguage(language);
|
|
51
95
|
const head = assistantText && assistantText.trim()
|
|
52
|
-
?
|
|
96
|
+
? (isZh
|
|
97
|
+
? `本批工具调用期间助手输出的文本:\n${assistantText.trim()}\n\n`
|
|
98
|
+
: `Assistant text emitted during this batch:\n${assistantText.trim()}\n\n`)
|
|
53
99
|
: '';
|
|
54
|
-
|
|
100
|
+
const template = isZh ? TEMPLATE_ZH : TEMPLATE_EN;
|
|
101
|
+
return template
|
|
55
102
|
.replace('{N}', String(N))
|
|
56
103
|
.replace('{originalUserMessage}', String(originalUserMsg || '').slice(0, 4000))
|
|
57
104
|
.replace('{toolCallsAndResults}', head + seq);
|
|
@@ -71,4 +118,8 @@ function formatPair(idx, p) {
|
|
|
71
118
|
return `[${idx}] ${p.name}${status}\n args: ${inputStr}\n result: ${outputStr}`;
|
|
72
119
|
}
|
|
73
120
|
|
|
74
|
-
|
|
121
|
+
// Section headings stay English in both templates because ReflectionCard
|
|
122
|
+
// parses them by literal string match. Exported separately so a debug panel
|
|
123
|
+
// or test can compare against either dictionary directly.
|
|
124
|
+
export const REFLECTION_TEMPLATE_EN = TEMPLATE_EN;
|
|
125
|
+
export const REFLECTION_TEMPLATE_ZH = TEMPLATE_ZH;
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* t1-reflector.js — V7 in-turn (synchronous) reflection (PR-L).
|
|
3
3
|
*
|
|
4
|
-
* Triggered when the current turn has accumulated TOOL_BATCH_SIZE (
|
|
4
|
+
* Triggered when the current turn has accumulated TOOL_BATCH_SIZE (30) tool
|
|
5
5
|
* results and the engine is about to loop back into adapter.stream(). Calls
|
|
6
6
|
* the PRIMARY model — never the fast model — to generate a markdown
|
|
7
7
|
* reflection over the batch.
|
|
8
8
|
*
|
|
9
9
|
* On success: returns { content, durationMs }.
|
|
10
10
|
* On failure: throws (engine catches and leaves history unchanged).
|
|
11
|
+
*
|
|
12
|
+
* The `language` param is forwarded to buildReflectionPrompt so the prompt
|
|
13
|
+
* (and therefore the model's response) renders in the user's language. The
|
|
14
|
+
* engine reads this from config.language and passes it through.
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
import { buildReflectionPrompt } from './reflection-prompt.js';
|
|
@@ -19,13 +23,14 @@ import { buildReflectionPrompt } from './reflection-prompt.js';
|
|
|
19
23
|
* originalUserMsg: string,
|
|
20
24
|
* toolPairs: Array<{ name: string, input: any, output: string, isError: boolean }>,
|
|
21
25
|
* assistantText?: string,
|
|
26
|
+
* language?: string,
|
|
22
27
|
* signal?: AbortSignal,
|
|
23
28
|
* }} p
|
|
24
29
|
* @returns {Promise<{ content: string, durationMs: number }>}
|
|
25
30
|
*/
|
|
26
|
-
export async function runT1Reflection({ adapter, model, originalUserMsg, toolPairs, assistantText, signal }) {
|
|
31
|
+
export async function runT1Reflection({ adapter, model, originalUserMsg, toolPairs, assistantText, language, signal }) {
|
|
27
32
|
const t0 = Date.now();
|
|
28
|
-
const prompt = buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText });
|
|
33
|
+
const prompt = buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText, language });
|
|
29
34
|
const result = await adapter.call({
|
|
30
35
|
model,
|
|
31
36
|
system: prompt,
|
|
@@ -17,13 +17,14 @@ import { buildReflectionPrompt } from './reflection-prompt.js';
|
|
|
17
17
|
* originalUserMsg: string,
|
|
18
18
|
* toolPairs: Array<{ name: string, input: any, output: string, isError: boolean }>,
|
|
19
19
|
* assistantText?: string,
|
|
20
|
+
* language?: string,
|
|
20
21
|
* signal?: AbortSignal,
|
|
21
22
|
* }} p
|
|
22
23
|
* @returns {Promise<{ content: string, durationMs: number }>}
|
|
23
24
|
*/
|
|
24
|
-
export async function runT2Reflection({ adapter, model, originalUserMsg, toolPairs, assistantText, signal }) {
|
|
25
|
+
export async function runT2Reflection({ adapter, model, originalUserMsg, toolPairs, assistantText, language, signal }) {
|
|
25
26
|
const t0 = Date.now();
|
|
26
|
-
const prompt = buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText });
|
|
27
|
+
const prompt = buildReflectionPrompt({ originalUserMsg, toolPairs, assistantText, language });
|
|
27
28
|
const result = await adapter.call({
|
|
28
29
|
model,
|
|
29
30
|
system: prompt,
|