mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-26 02:04:37 +08:00
fix(ai): enforce the tool round limit
Stop after one final-answer turn and pin Pi 0.84.2 for the public stop hook. 到达工具轮次上限后只允许一轮终答,并锁定提供公开停止钩子的 Pi 0.84.2。
This commit is contained in:
1 parent
e544392a83
commit
02b9ccb59f
5 files changed
+125
-54
No files matched your search
@@ -48,8 +48,8 @@
|
||||
"dependencies": {
|
||||
"@fastify/multipart": "^10.0.0",
|
||||
"@fastify/static": "^9.1.3",
|
||||
"@earendil-works/pi-agent-core": "0.83.0",
|
||||
"@earendil-works/pi-ai": "0.83.0",
|
||||
"@earendil-works/pi-agent-core": "0.84.2",
|
||||
"@earendil-works/pi-ai": "0.84.2",
|
||||
"@modelcontextprotocol/sdk": "^1.12.1",
|
||||
"better-sqlite3": "^12.4.6",
|
||||
"commander": "^13.1.0",
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
"./src/*": "./src/*.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@earendil-works/pi-agent-core": "0.83.0",
|
||||
"@earendil-works/pi-ai": "0.83.0",
|
||||
"@earendil-works/pi-agent-core": "0.84.2",
|
||||
"@earendil-works/pi-ai": "0.84.2",
|
||||
"@huggingface/transformers": "^4.2.0",
|
||||
"@openchatlab/config": "workspace:*",
|
||||
"@openchatlab/core": "workspace:*",
|
||||
|
||||
@@ -359,6 +359,93 @@ describe('runAgentCore runtime contract', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('stops after one final-answer turn when the tool round limit is reached', async () => {
|
||||
let providerCalls = 0
|
||||
let handlerCalls = 0
|
||||
const tool = createTool('lookup', async () => {
|
||||
handlerCalls += 1
|
||||
return { content: [{ type: 'text', text: 'tool result' }], details: null }
|
||||
})
|
||||
const toolCall = () =>
|
||||
assistantMessage([{ type: 'toolCall', id: `tool-${providerCalls}`, name: 'lookup', arguments: {} }], {
|
||||
stopReason: 'toolUse',
|
||||
})
|
||||
|
||||
const result = await runAgentCore(
|
||||
createOptions({
|
||||
tools: [tool],
|
||||
maxToolRounds: 1,
|
||||
steerMessage: 'Finish now.',
|
||||
streamFn: createScriptedStream([
|
||||
() => {
|
||||
providerCalls += 1
|
||||
return toolCall()
|
||||
},
|
||||
(context) => {
|
||||
providerCalls += 1
|
||||
assert.deepEqual(context.tools, [])
|
||||
return toolCall()
|
||||
},
|
||||
() => {
|
||||
providerCalls += 1
|
||||
return toolCall()
|
||||
},
|
||||
() => {
|
||||
providerCalls += 1
|
||||
return assistantMessage([{ type: 'text', text: 'Too late' }])
|
||||
},
|
||||
]),
|
||||
})
|
||||
)
|
||||
|
||||
assert.deepEqual(
|
||||
{ providerCalls, handlerCalls, toolRounds: result.toolRounds },
|
||||
{ providerCalls: 2, handlerCalls: 1, toolRounds: 1 }
|
||||
)
|
||||
})
|
||||
|
||||
it('stops after the first turn when the tool round limit is zero', async () => {
|
||||
let providerCalls = 0
|
||||
let handlerCalls = 0
|
||||
const tool = createTool('lookup', async () => {
|
||||
handlerCalls += 1
|
||||
return { content: [{ type: 'text', text: 'tool result' }], details: null }
|
||||
})
|
||||
const toolCall = () =>
|
||||
assistantMessage([{ type: 'toolCall', id: `tool-${providerCalls}`, name: 'lookup', arguments: {} }], {
|
||||
stopReason: 'toolUse',
|
||||
})
|
||||
|
||||
const result = await runAgentCore(
|
||||
createOptions({
|
||||
tools: [tool],
|
||||
maxToolRounds: 0,
|
||||
streamFn: createScriptedStream([
|
||||
(context) => {
|
||||
providerCalls += 1
|
||||
assert.deepEqual(context.tools, [])
|
||||
return toolCall()
|
||||
},
|
||||
() => {
|
||||
providerCalls += 1
|
||||
return toolCall()
|
||||
},
|
||||
]),
|
||||
})
|
||||
)
|
||||
|
||||
assert.deepEqual(
|
||||
{ providerCalls, handlerCalls, toolRounds: result.toolRounds },
|
||||
{ providerCalls: 1, handlerCalls: 0, toolRounds: 0 }
|
||||
)
|
||||
assert.deepEqual(
|
||||
result.finalMessages.map((message) => message.role),
|
||||
['user', 'assistant', 'toolResult']
|
||||
)
|
||||
const finalMessage = result.finalMessages.at(-1)
|
||||
assert.equal(finalMessage?.role === 'toolResult' && finalMessage.isError, true)
|
||||
})
|
||||
|
||||
it('forwards the stable AI conversation id as the provider session id', async () => {
|
||||
let capturedOptions: SimpleStreamOptions | undefined
|
||||
|
||||
|
||||
@@ -96,7 +96,8 @@ export async function runAgentCore(options: AgentCoreOptions): Promise<AgentCore
|
||||
})()
|
||||
|
||||
const finalThinkingLevel = resolvedThinkingLevel ?? (piModel.reasoning ? undefined : 'off')
|
||||
let hasReachedToolRoundLimit = false
|
||||
let hasReachedToolRoundLimit = maxToolRounds <= 0
|
||||
let hasCompletedFinalAnswerTurn = false
|
||||
|
||||
// DeepSeek-format APIs require reasoning_content on assistant messages that
|
||||
// precede tool results; build replay options so toPiHistoryMessages includes
|
||||
@@ -129,6 +130,7 @@ export async function runAgentCore(options: AgentCoreOptions): Promise<AgentCore
|
||||
},
|
||||
afterToolCall: async ({ result }) =>
|
||||
(result as { isError?: boolean }).isError === true ? { isError: true } : undefined,
|
||||
shouldStopAfterTurn: async () => hasCompletedFinalAnswerTurn,
|
||||
prepareNextTurnWithContext: ({ context }) =>
|
||||
hasReachedToolRoundLimit
|
||||
? {
|
||||
@@ -219,9 +221,10 @@ export async function runAgentCore(options: AgentCoreOptions): Promise<AgentCore
|
||||
})
|
||||
} else if (event.type === 'turn_end') {
|
||||
const hadToolCalls = event.toolResults.length > 0
|
||||
if (hadToolCalls) {
|
||||
const isFinalAnswerTurn = hasReachedToolRoundLimit
|
||||
if (hadToolCalls && !hasReachedToolRoundLimit) {
|
||||
toolRounds += 1
|
||||
if (!hasReachedToolRoundLimit && maxToolRounds > 0 && toolRounds >= maxToolRounds) {
|
||||
if (maxToolRounds > 0 && toolRounds >= maxToolRounds) {
|
||||
hasReachedToolRoundLimit = true
|
||||
coreAgent.state.tools = []
|
||||
coreAgent.steer({
|
||||
@@ -231,6 +234,7 @@ export async function runAgentCore(options: AgentCoreOptions): Promise<AgentCore
|
||||
} as PiMessage)
|
||||
}
|
||||
}
|
||||
if (isFinalAnswerTurn) hasCompletedFinalAnswerTurn = true
|
||||
onEvent({ type: 'turn_end', round: toolRounds, hadToolCalls })
|
||||
} else if (event.type === 'message_end') {
|
||||
if (event.message.role === 'assistant') {
|
||||
|
||||
Generated
+27
-47
@@ -136,11 +136,11 @@ importers:
|
||||
apps/cli:
|
||||
dependencies:
|
||||
'@earendil-works/pi-agent-core':
|
||||
specifier: 0.83.0
|
||||
version: 0.83.0(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
specifier: 0.84.2
|
||||
version: 0.84.2(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
'@earendil-works/pi-ai':
|
||||
specifier: 0.83.0
|
||||
version: 0.83.0(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
specifier: 0.84.2
|
||||
version: 0.84.2(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
'@fastify/multipart':
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
@@ -390,11 +390,11 @@ importers:
|
||||
packages/node-runtime:
|
||||
dependencies:
|
||||
'@earendil-works/pi-agent-core':
|
||||
specifier: 0.83.0
|
||||
version: 0.83.0(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
specifier: 0.84.2
|
||||
version: 0.84.2(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
'@earendil-works/pi-ai':
|
||||
specifier: 0.83.0
|
||||
version: 0.83.0(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
specifier: 0.84.2
|
||||
version: 0.84.2(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
'@huggingface/transformers':
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
@@ -808,15 +808,19 @@ packages:
|
||||
search-insights:
|
||||
optional: true
|
||||
|
||||
'@earendil-works/pi-agent-core@0.83.0':
|
||||
resolution: {integrity: sha512-RorGp9OH5l3ElpuC5a5ZQ2eWcchZGXflXRzVGkV99y3y6tT+LLNyxoYIdVKvTKWEObwhExeQbTH0fI2tE4iX4g==}
|
||||
'@earendil-works/pi-agent-core@0.84.2':
|
||||
resolution: {integrity: sha512-8Pn3wSCxj0cfo5I6jxQYVB/3uuQRmHhAlEclyjqpOuMEdQMIODHizRogv56FLdbU+dTiGnybeHQ2N+sV1/L2YA==}
|
||||
engines: {node: '>=22.19.0'}
|
||||
|
||||
'@earendil-works/pi-ai@0.83.0':
|
||||
resolution: {integrity: sha512-m3IZD4g3er0V8TC9+Vpgw/sjTKqcJlkcIBy/JvsgRubuuik3tAVzyugUg4rVrShIkkOT69mEd34NEqKUIsl6JQ==}
|
||||
'@earendil-works/pi-ai@0.84.2':
|
||||
resolution: {integrity: sha512-6MzsrYIYNVlE7SfpbL2yYb67Qo58p/7Q+xWG1RZvoX1P80aRCHSod2/13aFpxkow1lPO2LEh3c495J0Gwmyjig==}
|
||||
engines: {node: '>=22.19.0'}
|
||||
hasBin: true
|
||||
|
||||
'@earendil-works/pi-telemetry@0.84.3':
|
||||
resolution: {integrity: sha512-sgEkWoKrvSGaKn+YfLLFZmn+/A7B/w62eLwTD57nI+C9to8ITlFFVbgC2OtwvPnT3NFGHdCd53qhBEMIlptD1g==}
|
||||
engines: {node: '>=22.19.0'}
|
||||
|
||||
'@electron-toolkit/preload@3.0.2':
|
||||
resolution: {integrity: sha512-TWWPToXd8qPRfSXwzf5KVhpXMfONaUuRAZJHsKthKgZR/+LqX1dZVSSClQ8OTAEduvLGdecljCsoT2jSshfoUg==}
|
||||
peerDependencies:
|
||||
@@ -1825,14 +1829,6 @@ packages:
|
||||
resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@mistralai/mistralai@2.2.6':
|
||||
resolution: {integrity: sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.9.0
|
||||
peerDependenciesMeta:
|
||||
'@opentelemetry/api':
|
||||
optional: true
|
||||
|
||||
'@modelcontextprotocol/sdk@1.29.0':
|
||||
resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -2447,10 +2443,6 @@ packages:
|
||||
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
'@opentelemetry/semantic-conventions@1.43.0':
|
||||
resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@peculiar/asn1-schema@2.9.0':
|
||||
resolution: {integrity: sha512-AKvPMOM7LfK0uFe1m7o7+veOa8xQGPqsqOrKi3QKgCElzwjGp39mbhr2g7mt/v/mXQHiIvJmDj5cJS173x8Q9Q==}
|
||||
|
||||
@@ -5692,9 +5684,8 @@ packages:
|
||||
onnxruntime-web@1.26.0-dev.20260416-b7804b056c:
|
||||
resolution: {integrity: sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw==}
|
||||
|
||||
openai@6.26.0:
|
||||
resolution: {integrity: sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==}
|
||||
hasBin: true
|
||||
openai@6.40.0:
|
||||
resolution: {integrity: sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA==}
|
||||
peerDependencies:
|
||||
ws: ^8.18.0
|
||||
zod: ^3.25 || ^4.0
|
||||
@@ -7677,9 +7668,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@algolia/client-search'
|
||||
|
||||
'@earendil-works/pi-agent-core@0.83.0(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)':
|
||||
'@earendil-works/pi-agent-core@0.84.2(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@earendil-works/pi-ai': 0.83.0(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
'@earendil-works/pi-ai': 0.84.2(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)
|
||||
'@earendil-works/pi-telemetry': 0.84.3
|
||||
diff: 8.0.4
|
||||
ignore: 7.0.5
|
||||
typebox: 1.3.7
|
||||
@@ -7692,17 +7684,17 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@earendil-works/pi-ai@0.83.0(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)':
|
||||
'@earendil-works/pi-ai@0.84.2(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)(ws@8.19.0)(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@anthropic-ai/sdk': 0.91.1(zod@3.25.76)
|
||||
'@aws-sdk/client-bedrock-runtime': 3.1048.0
|
||||
'@earendil-works/pi-telemetry': 0.84.3
|
||||
'@google/genai': 1.52.0(@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76))(supports-color@7.2.0)
|
||||
'@mistralai/mistralai': 2.2.6(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@smithy/node-http-handler': 4.7.3
|
||||
http-proxy-agent: 7.0.2(supports-color@7.2.0)
|
||||
https-proxy-agent: 7.0.6(supports-color@7.2.0)
|
||||
openai: 6.26.0(ws@8.19.0)(zod@3.25.76)
|
||||
openai: 6.40.0(ws@8.19.0)(zod@3.25.76)
|
||||
partial-json: 0.1.7
|
||||
typebox: 1.3.7
|
||||
transitivePeerDependencies:
|
||||
@@ -7713,6 +7705,8 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@earendil-works/pi-telemetry@0.84.3': {}
|
||||
|
||||
'@electron-toolkit/preload@3.0.2(electron@35.7.5(supports-color@7.2.0))':
|
||||
dependencies:
|
||||
electron: 35.7.5(supports-color@7.2.0)
|
||||
@@ -8532,18 +8526,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@mistralai/mistralai@2.2.6(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/semantic-conventions': 1.43.0
|
||||
ws: 8.19.0
|
||||
zod: 3.25.76
|
||||
zod-to-json-schema: 3.25.1(zod@3.25.76)
|
||||
optionalDependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@hono/node-server': 1.19.14(hono@4.12.18)
|
||||
@@ -9221,8 +9203,6 @@ snapshots:
|
||||
|
||||
'@opentelemetry/api@1.9.0': {}
|
||||
|
||||
'@opentelemetry/semantic-conventions@1.43.0': {}
|
||||
|
||||
'@peculiar/asn1-schema@2.9.0':
|
||||
dependencies:
|
||||
'@peculiar/utils': 2.0.3
|
||||
@@ -12563,7 +12543,7 @@ snapshots:
|
||||
platform: 1.3.6
|
||||
protobufjs: 7.5.4
|
||||
|
||||
openai@6.26.0(ws@8.19.0)(zod@3.25.76):
|
||||
openai@6.40.0(ws@8.19.0)(zod@3.25.76):
|
||||
optionalDependencies:
|
||||
ws: 8.19.0
|
||||
zod: 3.25.76
|
||||
|
||||
Reference in new issue
Block a user