Merge pull request #45 from mmhh256/fix/query-agent-system-messages

fix: 合并 Query Agent 的连续 system messages
This commit is contained in:
qingmao
2026-09-20 16:11:08 +08:00
committed by GitHub
3 changed files with 30 additions and 5 deletions
+5 -2
View File
@@ -1309,10 +1309,13 @@ export class QueryAgentService {
const scopeNote = options.conversationScope
? conversationScopeNote(options.conversationScope)
: undefined
// Some OpenAI-compatible providers (including strict vLLM/Qwen chat templates)
// reject consecutive system messages. Keep the scope note as system context, but
// fold it into the leading prompt so every request starts with exactly one system message.
const systemContent = scopeNote ? `${SYSTEM_PROMPT}\n\n${scopeNote}` : SYSTEM_PROMPT
const messages: Array<Record<string, unknown>> = [
{ role: 'system', content: SYSTEM_PROMPT },
// 范围说明是**上下文**,不是强制执行手段:真正的边界由 Engine 拒绝越界 target 来保证。
...(scopeNote ? [{ role: 'system', content: scopeNote }] : []),
{ role: 'system', content: systemContent },
...history.flatMap((turn) => [
{ role: 'user', content: turn.question },
{ role: 'assistant', content: turn.answer }
+2 -2
View File
@@ -473,8 +473,8 @@ describe('AskWechatService — 搜索范围(conversationScope)', () => {
expect(seen[0]).toEqual({ kind: 'groups' })
// 范围说明只描述边界;强制由 Engine 完成(越界 target 会被结构化拒绝)。
expect(String(calls[0].messages[1]?.content)).toContain('群聊专属')
expect(String(calls[0].messages[1]?.content)).toContain('群成员实际发送的消息')
expect(String(calls[0].messages[0]?.content)).toContain('群聊专属')
expect(String(calls[0].messages[0]?.content)).toContain('群成员实际发送的消息')
})
it('没有 scope 时不注入范围说明(保持毕业版本的 messages 形状)', async () => {
+23 -1
View File
@@ -1,5 +1,8 @@
import { describe, expect, it, vi } from 'vitest'
import { QueryAgentService, type QueryAgentProvider } from '../../src/main/services/query-agent-service'
import {
QueryAgentService,
type QueryAgentProvider
} from '../../src/main/services/query-agent-service'
function capturingProvider(configured = true): {
provider: QueryAgentProvider
@@ -54,6 +57,25 @@ describe('QueryAgentService — 历史上下文不改变毕业语义', () => {
])
})
it('传 conversationScope 时把范围说明合并进唯一的 system message', async () => {
const { provider, calls } = capturingProvider()
const service = new QueryAgentService(provider, vi.fn())
await service.run('这个群最近聊了什么', {
conversationScope: {
scope: { kind: 'current', conversationId: 'fixture-conversation' },
label: '当前会话:fixture-group'
}
})
expect(calls[0].messages.map((message) => message.role)).toEqual(['system', 'user'])
expect(calls[0].messages.filter((message) => message.role === 'system')).toHaveLength(1)
expect(calls[0].messages[0]).toMatchObject({
content: expect.stringContaining('当前搜索范围(由应用界面决定):当前会话:fixture-group。')
})
expect(String(calls[0].messages[0].content)).toContain('你是 TraceMemo 的本地聊天查询助手')
})
it('失败分类是 additive 字段:成功时不存在', async () => {
const { provider } = capturingProvider()
const service = new QueryAgentService(provider, vi.fn())