feat(agent): 检索工具、模型接入与结构化回复

Agent 按组织与知识源范围调用检索工具,密钥加密保存,回复含来源与转人工意图。
This commit is contained in:
WangShen007 committed 2026-10-02 21:25:00 +08:00
1 parent 738e37fbb0
commit 362d79a5c2
65 files changed
+33409

No files matched your search

@@ -0,0 +1,77 @@
// @vitest-environment jsdom
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { ref } from 'vue'
const getAgentQueueItems = vi.fn()
const getKnowledgeByAgent = vi.fn()
vi.mock('@/services/knowledge', () => ({
knowledgeService: {
getAgentQueueItems: (...a: unknown[]) => getAgentQueueItems(...a),
getKnowledgeByAgent: (...a: unknown[]) => getKnowledgeByAgent(...a)
}
}))
vi.mock('@/services/widget', () => ({ widgetService: { createAgentWidget: vi.fn() } }))
vi.mock('@/composables/useOnboardingTestChat', () => ({
useOnboardingTestChat: () => ({
messages: ref([]), loading: ref(false), connecting: ref(false), connectionError: ref(''),
start: vi.fn(), send: vi.fn(), cleanup: vi.fn()
})
}))
import StepTest from '@/components/aiagent/onboarding/steps/StepTest.vue'
const SITE = 'https://teeckbox.com'
const REASON = 'This site could not be found — the domain does not resolve.'
const queue = (status: string) => ({
queue_items: [{ id: 1, source: SITE, source_type: 'website', status, created_at: '',
reason: status === 'failed' ? REASON : null }]
})
const knowledgePage = (count: number) => ({ knowledge: [], pagination: { total_count: count } })
const mountStep = async () => {
const wrapper = mount(StepTest, { props: { agentId: 'agent-1', agentName: 'Rex', widgetId: 'w-1' } })
await flushPromises()
return wrapper
}
describe('StepTest - what the agent knows', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.useFakeTimers()
})
afterEach(() => vi.useRealTimers())
it('a crawl that fails after Teach says why, not "Knowledge indexed"', async () => {
getAgentQueueItems.mockResolvedValueOnce(queue('pending')).mockResolvedValue(queue('failed'))
getKnowledgeByAgent.mockResolvedValue(knowledgePage(0))
const wrapper = await mountStep()
expect(wrapper.text()).toContain('知识库仍在后台建立索引')
await vi.advanceTimersByTimeAsync(5000)
await flushPromises()
expect(wrapper.text()).not.toContain('知识库已索引')
expect(wrapper.text()).toContain(`无法读取 ${SITE}。${REASON}`)
expect(wrapper.text()).toContain('它还完全不了解你的业务')
})
it('a crawl that finishes says it is indexed', async () => {
getAgentQueueItems.mockResolvedValueOnce(queue('pending')).mockResolvedValue(queue('completed'))
getKnowledgeByAgent.mockResolvedValue(knowledgePage(1))
const wrapper = await mountStep()
await vi.advanceTimersByTimeAsync(5000)
await flushPromises()
expect(wrapper.text()).toContain('知识库已索引')
})
it('arriving after the failure shows it too', async () => {
getAgentQueueItems.mockResolvedValue(queue('failed'))
getKnowledgeByAgent.mockResolvedValue(knowledgePage(0))
const wrapper = await mountStep()
expect(wrapper.text()).toContain(`无法读取 ${SITE}。`)
})
})