feat: 全新智能体功能

- 基于先进智能体框架,增加智能体编排功能
- 增加智能体聊天,并对接持久化
This commit is contained in:
2026-05-25 11:42:48 +08:00
parent 6c3d98eaac
commit 72df00f25b
168 changed files with 22045 additions and 400 deletions

View File

@@ -0,0 +1,213 @@
import {beforeEach, describe, expect, it} from 'vitest';
import {useAgentTryoutRawRounds} from './useAgentTryoutRawRounds';
describe('useAgentTryoutRawRounds', () => {
beforeEach(() => {
sessionStorage.clear();
});
it('按原始事件顺序生成 timeline', () => {
const store = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-1',
});
const roundId = store.createRound('上一轮问题');
store.recordEvent(roundId, {
domain: 'LLM',
payload: { reasoning: '先思考' },
type: 'THINKING',
});
store.recordEvent(roundId, {
domain: 'LLM',
payload: { delta: '上一轮回答' },
type: 'MESSAGE',
});
store.completeRound(roundId);
expect(store.buildTimelineItems().map((item) => item.type)).toEqual([
'message',
'message',
]);
});
it('业务引用只用于展示', () => {
const store = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-2',
});
const roundId = store.createRound('查知识库');
store.recordEvent(roundId, {
domain: 'BUSINESS',
payload: {
items: [
{
chunkContent: '知识库原文',
chunkId: 'chunk-1',
documentId: 'doc-1',
knowledgeId: 'kb-1',
},
],
},
type: 'CITATIONS',
});
store.recordEvent(roundId, {
domain: 'LLM',
payload: { delta: '引用后的回答' },
type: 'MESSAGE',
});
store.completeRound(roundId);
expect(store.buildTimelineItems().map((item) => item.type)).toEqual([
'message',
'knowledge',
'message',
]);
});
it('AgentScope fragment 工具事件不进入页面时间线', () => {
const store = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-fragment',
});
const roundId = store.createRound('调用内部片段');
store.recordEvent(roundId, {
domain: 'TOOL',
payload: {
input: { text: 'fragment' },
toolCallId: 'fragment-1',
toolName: '__fragment__',
},
type: 'TOOL_CALL',
});
store.recordEvent(roundId, {
domain: 'TOOL',
payload: {
output: 'internal',
toolCallId: 'fragment-1',
toolName: '__fragment__',
},
type: 'TOOL_RESULT',
});
expect(store.buildTimelineItems().map((item) => item.type)).toEqual([
'message',
]);
});
it('刷新后能从 raw rounds 恢复 timeline', () => {
const first = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-5',
});
const roundId = first.createRound('问题');
first.recordEvent(roundId, {
domain: 'LLM',
payload: { delta: '回答' },
type: 'MESSAGE',
});
first.completeRound(roundId);
const restored = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-5',
});
expect(restored.buildTimelineItems().map((item) => item.type)).toEqual([
'message',
'message',
]);
});
it('错误轮次不会被 completeRound 覆盖为成功状态', () => {
const store = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-error',
});
const roundId = store.createRound('会失败的问题');
store.recordEvent(roundId, {
domain: 'LLM',
payload: { delta: '半截回答' },
type: 'MESSAGE',
});
store.recordEvent(roundId, {
domain: 'SYSTEM',
payload: { message: '调用失败' },
type: 'ERROR',
});
store.completeRound(roundId);
const assistant = store
.buildTimelineItems()
.find((item) => item.type === 'message' && item.role === 'assistant');
expect(assistant).toMatchObject({ status: 'error' });
});
it('流式重建 timeline 时保持稳定 item id', () => {
const store = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-stable-id',
});
const roundId = store.createRound('问题');
store.recordEvent(roundId, {
domain: 'LLM',
payload: { delta: '你' },
type: 'MESSAGE',
});
const firstIds = store.buildTimelineItems().map((item) => item.id);
store.recordEvent(roundId, {
domain: 'LLM',
payload: { delta: '好' },
type: 'MESSAGE',
});
const secondIds = store.buildTimelineItems().map((item) => item.id);
expect(secondIds).toEqual(firstIds);
});
it('审批状态作为展示事件缓存并可刷新恢复', () => {
const first = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-approval',
});
const roundId = first.createRound('审批工具');
first.recordEvent(roundId, {
domain: 'TOOL',
payload: {
requestId: 'req-1',
resumeToken: 'resume-1',
toolCallId: 'call-approval',
toolName: 'dangerous_tool',
},
type: 'FORM_REQUEST',
});
first.recordEvent(roundId, {
domain: 'TOOL',
payload: {
requestId: 'req-1',
resumeToken: 'resume-1',
toolCallId: 'call-approval',
},
type: 'FORM_APPROVING',
});
first.flush();
const restored = useAgentTryoutRawRounds({
mode: 'draft',
sessionId: 'session-raw-approval',
});
const tool = restored
.buildTimelineItems()
.find((item) => item.type === 'tool');
expect(tool).toMatchObject({
status: 'approving',
toolCallId: 'call-approval',
});
});
});