feat(beaver): 完成Task Team功能v1实现,重构后端架构支持统一内核

新增内部Task系统,包括验证、反馈门控机制,实现自动质量验证
(通过率>=0.75)和用户反馈闭环(satisfied/revise/abandon)。

实现Agent Team v1协调器,支持sequence/parallel/dag执行策略,
sub-agent复用主AgentLoop,每个run使用独立memory snapshot。

建立Skill学习pipeline,包含draft/审核/发布/回滚完整生命周期,
通过Task验证通过且用户满意才生成学习候选。

重构目录结构,移除third_party依赖,建立统一engine内核,
所有agent共享运行时基础组件。

更新ContextBuilder清理provider消息字段,增强SkillContext版本管理,
集成TaskExecutionPlanner和TaskSkillResolver实现技能解析机制。
This commit is contained in:
2026-05-08 17:14:14 +08:00
parent 5ba5c7e4c1
commit 8a12c30141
93 changed files with 16724 additions and 1247 deletions

View File

@ -1,7 +1,7 @@
'use client';
import React from 'react';
import { Bot, Loader2, Paperclip, User } from 'lucide-react';
import { Bot, Loader2, Paperclip, RefreshCcw, ThumbsUp, User, XCircle } from 'lucide-react';
import type { ChatMessage, ProcessArtifact, ProcessEvent, ProcessRun } from '@/types';
import { getAccessToken, getFileUrl } from '@/lib/api';
@ -37,7 +37,16 @@ function AuthImage({ src, alt, className }: { src: string; alt: string; classNam
return <img src={blobUrl} alt={alt} className={className} loading="lazy" decoding="async" />;
}
function MessageBubble({ message }: { message: ChatMessage }) {
function MessageBubble({
message,
canSendFeedback,
onFeedback,
}: {
message: ChatMessage;
canSendFeedback: boolean;
onFeedback: (runId: string, feedbackType: 'satisfied' | 'revise' | 'abandon') => void;
}) {
const { locale } = useAppI18n();
const isUser = message.role === 'user';
const textContent = typeof message.content === 'string' ? message.content : String(message.content || '');
@ -101,6 +110,56 @@ function MessageBubble({ message }: { message: ChatMessage }) {
) : (
<MarkdownContent content={textContent} />
)}
{!isUser && canSendFeedback && message.run_id && (
<div className="mt-3 flex flex-wrap items-center gap-2 border-t border-border/70 pt-2">
{message.feedback_state ? (
<span className="text-xs text-muted-foreground">
{message.feedback_state === 'satisfied'
? pickAppText(locale, '已标记满意', 'Marked satisfied')
: message.feedback_state === 'revise'
? pickAppText(locale, '已请求修改', 'Revision requested')
: pickAppText(locale, '已放弃任务', 'Task abandoned')}
</span>
) : (
<>
<button
type="button"
onClick={() => onFeedback(message.run_id!, 'satisfied')}
className="inline-flex h-7 items-center gap-1 rounded-md border border-border px-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
>
<ThumbsUp className="h-3.5 w-3.5" />
{pickAppText(locale, '满意', 'Satisfied')}
</button>
<button
type="button"
onClick={() => onFeedback(message.run_id!, 'revise')}
className="inline-flex h-7 items-center gap-1 rounded-md border border-border px-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
>
<RefreshCcw className="h-3.5 w-3.5" />
{pickAppText(locale, '需要修改', 'Revise')}
</button>
<button
type="button"
onClick={() => onFeedback(message.run_id!, 'abandon')}
className="inline-flex h-7 items-center gap-1 rounded-md border border-border px-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
>
<XCircle className="h-3.5 w-3.5" />
{pickAppText(locale, '放弃', 'Abandon')}
</button>
</>
)}
{message.validation_status && message.validation_status !== 'unknown' && (
<span className="text-xs text-muted-foreground">
{message.validation_status === 'passed'
? pickAppText(locale, '验证通过', 'Validated')
: pickAppText(locale, '验证未通过', 'Validation failed')}
</span>
)}
{message.feedback_error && (
<span className="text-xs text-destructive">{message.feedback_error}</span>
)}
</div>
)}
</div>
{isUser && (
<div className="w-7 h-7 rounded-full bg-secondary flex items-center justify-center flex-shrink-0 mt-0.5">
@ -198,6 +257,7 @@ export function MessageList({
selectedRunId,
onSelectRun,
onCancelRun,
onFeedback,
}: {
messages: ChatMessage[];
isThinking: boolean;
@ -209,6 +269,7 @@ export function MessageList({
selectedRunId: string | null;
onSelectRun: (runId: string) => void;
onCancelRun: (runId: string) => void;
onFeedback: (runId: string, feedbackType: 'satisfied' | 'revise' | 'abandon') => void;
}) {
const { locale } = useAppI18n();
const visibleMessages = React.useMemo(
@ -245,6 +306,9 @@ export function MessageList({
return a.order - b.order;
});
}, [teamGroups, visibleMessages]);
const latestAssistantRunId = [...visibleMessages]
.reverse()
.find((message) => message.role === 'assistant' && message.run_id && message.task_id)?.run_id;
return (
<ScrollArea className="h-full px-4" viewportRef={viewportRef}>
@ -259,7 +323,12 @@ export function MessageList({
{timelineItems.map((item) =>
item.kind === 'message' ? (
<MessageBubble key={item.key} message={item.message} />
<MessageBubble
key={item.key}
message={item.message}
canSendFeedback={Boolean(latestAssistantRunId && item.message.run_id === latestAssistantRunId)}
onFeedback={onFeedback}
/>
) : (
<AgentTeamBlock
key={item.key}