- 将所有环境变量前缀从NANO_改为BEAVER_ - 更新README.md文档内容,包括项目介绍、组件说明和快速开始指南 - 修改.gitignore文件,添加auth-portal运行时路径排除规则 - 更新app-instance镜像标签从nano/app-instance改为beaver/app-instance - 增强技能安全检查器,支持工具前缀白名单功能 - 添加技能草稿重新检查安全性API端点 - 扩展证据选择器,收集工具调用名称用于技能学习 - 改进技能合成器,基于实际调用的工具生成工具提示 - 优化路由超时处理机制,增加重试逻辑 - 更新后端架构文档,添加可视化入口和基础概念说明 - 实现在WebSocket消息中传递工具迭代次数信息
1389 lines
62 KiB
HTML
1389 lines
62 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Beaver Backend Visualization</title>
|
||
<style>
|
||
:root {
|
||
color-scheme: light;
|
||
--bg: #f7f8fb;
|
||
--ink: #172033;
|
||
--muted: #5d6b82;
|
||
--line: #d7deea;
|
||
--panel: #ffffff;
|
||
--blue: #2563eb;
|
||
--green: #0f8b6f;
|
||
--amber: #b7791f;
|
||
--rose: #be4266;
|
||
--violet: #6953c6;
|
||
--cyan: #147d96;
|
||
--shadow: 0 14px 36px rgba(23, 32, 51, 0.08);
|
||
--radius: 8px;
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
margin: 0;
|
||
background: var(--bg);
|
||
color: var(--ink);
|
||
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
button {
|
||
font: inherit;
|
||
}
|
||
|
||
.page {
|
||
width: min(1440px, 100%);
|
||
margin: 0 auto;
|
||
padding: 28px 28px 40px;
|
||
}
|
||
|
||
.topbar {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
h1 {
|
||
margin: 0 0 8px;
|
||
font-size: 30px;
|
||
line-height: 1.14;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.subtitle {
|
||
margin: 0;
|
||
max-width: 760px;
|
||
color: var(--muted);
|
||
font-size: 15px;
|
||
}
|
||
|
||
.meta {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, max-content);
|
||
gap: 8px;
|
||
align-items: center;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.pill,
|
||
.pill-link {
|
||
border: 1px solid var(--line);
|
||
background: var(--panel);
|
||
border-radius: 999px;
|
||
padding: 6px 10px;
|
||
}
|
||
|
||
.pill-link {
|
||
color: var(--ink);
|
||
text-decoration: none;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.layout {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr);
|
||
gap: 16px;
|
||
align-items: start;
|
||
}
|
||
|
||
.canvas {
|
||
background: var(--panel);
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.tabs {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
padding: 14px;
|
||
border-bottom: 1px solid var(--line);
|
||
background: #fbfcff;
|
||
}
|
||
|
||
.tab {
|
||
border: 1px solid var(--line);
|
||
border-radius: 999px;
|
||
background: #fff;
|
||
color: var(--ink);
|
||
min-height: 34px;
|
||
padding: 7px 12px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.tab[aria-selected="true"] {
|
||
color: #fff;
|
||
border-color: var(--blue);
|
||
background: var(--blue);
|
||
}
|
||
|
||
.stage {
|
||
display: none;
|
||
padding: 22px;
|
||
}
|
||
|
||
.stage.active {
|
||
display: block;
|
||
}
|
||
|
||
.stage-title {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
align-items: flex-start;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
h2 {
|
||
margin: 0;
|
||
font-size: 20px;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.hint {
|
||
margin: 4px 0 0;
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.legend {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
align-items: center;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.dot {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 999px;
|
||
display: inline-block;
|
||
margin-right: 5px;
|
||
vertical-align: -1px;
|
||
}
|
||
|
||
.architecture-grid {
|
||
display: grid;
|
||
grid-template-columns: 1.1fr 1.2fr 1fr 1fr;
|
||
gap: 14px;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.lane {
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
background: #fbfcff;
|
||
min-height: 590px;
|
||
padding: 12px;
|
||
}
|
||
|
||
.lane h3 {
|
||
margin: 0 0 10px;
|
||
font-size: 14px;
|
||
color: #374151;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.node {
|
||
position: relative;
|
||
width: 100%;
|
||
border: 1px solid var(--line);
|
||
background: #fff;
|
||
border-left: 5px solid var(--blue);
|
||
border-radius: var(--radius);
|
||
padding: 11px 12px;
|
||
margin: 10px 0;
|
||
text-align: left;
|
||
color: var(--ink);
|
||
cursor: pointer;
|
||
transition: border-color 120ms ease, box-shadow 120ms ease, transform 120ms ease;
|
||
}
|
||
|
||
.node:hover,
|
||
.node:focus-visible,
|
||
.node.selected {
|
||
outline: none;
|
||
border-color: #9bb7f7;
|
||
box-shadow: 0 10px 24px rgba(37, 99, 235, 0.12);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.node[data-kind="service"] { border-left-color: var(--blue); }
|
||
.node[data-kind="engine"] { border-left-color: var(--green); }
|
||
.node[data-kind="capability"] { border-left-color: var(--violet); }
|
||
.node[data-kind="interface"] { border-left-color: var(--cyan); }
|
||
.node[data-kind="store"] { border-left-color: var(--amber); }
|
||
.node[data-kind="governance"] { border-left-color: var(--rose); }
|
||
|
||
.node strong {
|
||
display: block;
|
||
font-size: 14px;
|
||
line-height: 1.25;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.node span {
|
||
display: block;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.connector {
|
||
display: grid;
|
||
place-items: center;
|
||
height: 22px;
|
||
color: #8793a8;
|
||
font-size: 18px;
|
||
user-select: none;
|
||
}
|
||
|
||
.flow {
|
||
display: grid;
|
||
gap: 10px;
|
||
}
|
||
|
||
.diagram {
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
background: #fbfcff;
|
||
padding: 14px;
|
||
margin: 0 0 16px;
|
||
}
|
||
|
||
.diagram-title {
|
||
margin: 0 0 12px;
|
||
font-size: 14px;
|
||
color: #374151;
|
||
}
|
||
|
||
.diagram-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||
gap: 10px;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.diagram-row.four {
|
||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
}
|
||
|
||
.diagram-row.six {
|
||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||
}
|
||
|
||
.diagram-card {
|
||
position: relative;
|
||
border: 1px solid #dfe6f2;
|
||
border-radius: var(--radius);
|
||
background: #fff;
|
||
padding: 12px;
|
||
min-height: 92px;
|
||
}
|
||
|
||
.diagram-card:not(:last-child)::after {
|
||
content: "→";
|
||
position: absolute;
|
||
right: -10px;
|
||
top: 50%;
|
||
transform: translate(50%, -50%);
|
||
width: 22px;
|
||
height: 22px;
|
||
display: grid;
|
||
place-items: center;
|
||
border: 1px solid var(--line);
|
||
border-radius: 999px;
|
||
background: #fff;
|
||
color: var(--muted);
|
||
font-size: 14px;
|
||
z-index: 1;
|
||
}
|
||
|
||
.diagram-card strong {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-size: 13px;
|
||
color: var(--ink);
|
||
}
|
||
|
||
.diagram-card span {
|
||
display: block;
|
||
color: var(--muted);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.demo {
|
||
border: 1px solid #dfe6f2;
|
||
border-radius: var(--radius);
|
||
background: #fff;
|
||
padding: 14px;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.demo h3 {
|
||
margin: 0 0 8px;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.demo p {
|
||
margin: 0 0 12px;
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.demo-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 12px;
|
||
}
|
||
|
||
.demo-block {
|
||
min-width: 0;
|
||
}
|
||
|
||
.demo-block h4 {
|
||
margin: 0 0 6px;
|
||
font-size: 12px;
|
||
color: #374151;
|
||
}
|
||
|
||
.demo pre {
|
||
margin: 0;
|
||
min-height: 160px;
|
||
overflow: auto;
|
||
border: 1px solid #e5ebf5;
|
||
border-radius: 6px;
|
||
background: #f7f9fc;
|
||
color: #24324b;
|
||
padding: 10px;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
||
font-size: 12px;
|
||
line-height: 1.45;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.primer-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 14px;
|
||
}
|
||
|
||
.primer-card,
|
||
.glossary-card {
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
background: #fff;
|
||
padding: 14px;
|
||
}
|
||
|
||
.primer-card h3,
|
||
.glossary-card h3 {
|
||
margin: 0 0 8px;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.primer-card p,
|
||
.glossary-card p {
|
||
margin: 0;
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.plain-flow {
|
||
display: grid;
|
||
gap: 8px;
|
||
margin-top: 14px;
|
||
}
|
||
|
||
.plain-step {
|
||
display: grid;
|
||
grid-template-columns: 92px minmax(0, 1fr);
|
||
gap: 12px;
|
||
align-items: start;
|
||
border: 1px solid #e3e9f4;
|
||
border-radius: var(--radius);
|
||
background: #fbfcff;
|
||
padding: 12px;
|
||
}
|
||
|
||
.plain-step strong {
|
||
color: var(--blue);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.plain-step p {
|
||
margin: 0;
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.glossary-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 12px;
|
||
margin-top: 14px;
|
||
}
|
||
|
||
.step {
|
||
display: grid;
|
||
grid-template-columns: 40px minmax(0, 1fr);
|
||
gap: 12px;
|
||
align-items: start;
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
background: #fff;
|
||
padding: 12px;
|
||
}
|
||
|
||
.num {
|
||
width: 34px;
|
||
height: 34px;
|
||
display: grid;
|
||
place-items: center;
|
||
border-radius: 50%;
|
||
color: #fff;
|
||
background: var(--blue);
|
||
font-weight: 700;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.step h3 {
|
||
margin: 0 0 4px;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.step p {
|
||
margin: 0;
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.team-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 14px;
|
||
}
|
||
|
||
.strategy {
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
background: #fff;
|
||
padding: 14px;
|
||
min-height: 190px;
|
||
}
|
||
|
||
.strategy h3 {
|
||
margin: 0 0 10px;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.mini-flow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-wrap: wrap;
|
||
margin: 12px 0;
|
||
}
|
||
|
||
.mini-node {
|
||
border: 1px solid var(--line);
|
||
border-radius: 999px;
|
||
padding: 7px 10px;
|
||
background: #fbfcff;
|
||
color: #283348;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.mini-arrow {
|
||
color: #8793a8;
|
||
}
|
||
|
||
.matrix {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 14px;
|
||
}
|
||
|
||
.stack {
|
||
border: 1px solid var(--line);
|
||
border-radius: var(--radius);
|
||
background: #fff;
|
||
padding: 14px;
|
||
}
|
||
|
||
.stack h3 {
|
||
margin: 0 0 10px;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.stack ul {
|
||
margin: 0;
|
||
padding-left: 18px;
|
||
color: var(--muted);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.stack li + li {
|
||
margin-top: 6px;
|
||
}
|
||
|
||
.code {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
||
font-size: 12px;
|
||
color: #24324b;
|
||
background: #f3f6fb;
|
||
border: 1px solid #e5ebf5;
|
||
border-radius: 6px;
|
||
padding: 2px 5px;
|
||
}
|
||
|
||
@media (max-width: 1180px) {
|
||
.architecture-grid,
|
||
.matrix,
|
||
.diagram-row,
|
||
.diagram-row.four,
|
||
.diagram-row.six,
|
||
.demo-grid {
|
||
grid-template-columns: 1fr 1fr;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 760px) {
|
||
.page {
|
||
padding: 18px;
|
||
}
|
||
|
||
.topbar,
|
||
.stage-title {
|
||
display: block;
|
||
}
|
||
|
||
.meta {
|
||
grid-template-columns: 1fr;
|
||
margin-top: 14px;
|
||
}
|
||
|
||
.architecture-grid,
|
||
.team-grid,
|
||
.matrix,
|
||
.primer-grid,
|
||
.glossary-grid,
|
||
.diagram-row,
|
||
.diagram-row.four,
|
||
.diagram-row.six,
|
||
.demo-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.diagram-card:not(:last-child)::after {
|
||
content: "↓";
|
||
right: 50%;
|
||
top: auto;
|
||
bottom: -10px;
|
||
transform: translate(50%, 50%);
|
||
}
|
||
|
||
.lane {
|
||
min-height: auto;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<main class="page">
|
||
<header class="topbar">
|
||
<div>
|
||
<h1>Beaver Backend 可视化</h1>
|
||
<p class="subtitle">基于 <span class="code">app-instance/backend</span> 当前代码结构绘制,适合配合讲解使用:先给零基础读者建立概念,再逐页展开 Task、自学习、Skill/Tool、Memory/Session 和 Agent Team。</p>
|
||
</div>
|
||
<div class="meta" aria-label="visualization metadata">
|
||
<a class="pill-link" href="project-comparison.html">项目对比分析</a>
|
||
<span class="pill">范围: backend</span>
|
||
<span class="pill">入口: Web / CLI / Gateway / MCP</span>
|
||
<span class="pill">核心: shared AgentLoop</span>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="layout">
|
||
<section class="canvas" aria-label="backend visualization">
|
||
<nav class="tabs" aria-label="views">
|
||
<button class="tab" type="button" data-view="primer" aria-selected="true">入门导读</button>
|
||
<button class="tab" type="button" data-view="overview" aria-selected="false">总览</button>
|
||
<button class="tab" type="button" data-view="task-detail">Task 概念</button>
|
||
<button class="tab" type="button" data-view="learning-detail">自学习</button>
|
||
<button class="tab" type="button" data-view="skill-tool-detail">Skill / Tool</button>
|
||
<button class="tab" type="button" data-view="memory-detail">Memory / Session</button>
|
||
<button class="tab" type="button" data-view="team">Agent Team</button>
|
||
<button class="tab" type="button" data-view="request">请求主链</button>
|
||
<button class="tab" type="button" data-view="capabilities">能力层</button>
|
||
</nav>
|
||
|
||
<section class="stage active" data-stage="primer">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>先把它当成一个“帮用户完成任务的后台工厂”</h2>
|
||
<p class="hint">如果你完全没接触过后端、agent、skills、tools,先看这一页。它不要求你理解代码细节。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="diagram">
|
||
<h3 class="diagram-title">整体理解:用户一句话如何变成后台执行</h3>
|
||
<div class="diagram-row six">
|
||
<div class="diagram-card"><strong>用户说需求</strong><span>网页、命令行或其他入口收到一句话</span></div>
|
||
<div class="diagram-card"><strong>入口接住</strong><span>把请求整理成后端内部格式</span></div>
|
||
<div class="diagram-card"><strong>判断类型</strong><span>普通聊天还是需要跟踪的任务</span></div>
|
||
<div class="diagram-card"><strong>执行任务</strong><span>选择技能、工具并调用模型</span></div>
|
||
<div class="diagram-card"><strong>保存过程</strong><span>记录 session、task、run 和反馈</span></div>
|
||
<div class="diagram-card"><strong>返回结果</strong><span>把最终答复展示给用户</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="primer-grid">
|
||
<div class="primer-card">
|
||
<h3>这个后端在做什么</h3>
|
||
<p>用户在前端发一句话,后端负责判断这句话是不是一个任务、要不要查资料或改文件、需不需要拆给多个小助手处理,最后把结果和过程记录下来。</p>
|
||
</div>
|
||
<div class="primer-card">
|
||
<h3>为什么要分这么多层</h3>
|
||
<p>因为“接收请求”“决定怎么做”“调用模型”“使用工具”“保存记忆”“学习新技能”是不同职责。分开以后,问题更容易定位,也方便单独替换或测试。</p>
|
||
</div>
|
||
<div class="primer-card">
|
||
<h3>最重要的一句话</h3>
|
||
<p>所有真正执行 agent 的地方,最后都会回到同一个 <span class="code">AgentLoop</span>。这保证主 agent 和 sub-agent 用同一套运行规则。</p>
|
||
</div>
|
||
<div class="primer-card">
|
||
<h3>看图时抓住四类东西</h3>
|
||
<p>入口负责接话,服务层负责安排,运行内核负责执行,能力层提供工具、技能、记忆和外部连接。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="plain-flow">
|
||
<div class="plain-step"><strong>用户</strong><p>在网页、命令行或其他渠道输入一句话,比如“帮我整理这个项目的后端架构”。</p></div>
|
||
<div class="plain-step"><strong>入口层</strong><p>把这句话接住,整理成后端能理解的请求格式。</p></div>
|
||
<div class="plain-step"><strong>服务层</strong><p>判断这是不是一个任务。如果只是闲聊,就简单回答;如果需要执行,就创建或继续一个 Task。</p></div>
|
||
<div class="plain-step"><strong>运行内核</strong><p>准备上下文、挑选技能和工具、调用大模型,并在模型要求使用工具时执行工具。</p></div>
|
||
<div class="plain-step"><strong>能力层</strong><p>提供文件读写、搜索、记忆、MCP、定时任务、外部集成等具体能力。</p></div>
|
||
<div class="plain-step"><strong>状态层</strong><p>记录这次对话、任务、工具结果、验证结果和用户反馈,方便继续任务或沉淀经验。</p></div>
|
||
</div>
|
||
|
||
<div class="glossary-grid">
|
||
<div class="glossary-card"><h3>Backend</h3><p>前端背后的服务程序。用户看不见它的界面,但所有保存、调用模型、执行工具都在这里发生。</p></div>
|
||
<div class="glossary-card"><h3>Agent</h3><p>可以根据目标自己决定下一步动作的模型运行单元,不只是简单问答。</p></div>
|
||
<div class="glossary-card"><h3>Task</h3><p>一个需要跟踪结果的工作项,例如实现功能、分析代码、修复问题。</p></div>
|
||
<div class="glossary-card"><h3>Skill</h3><p>给 agent 的专门说明书,告诉它处理某类任务时应该遵循什么步骤和标准。</p></div>
|
||
<div class="glossary-card"><h3>Tool</h3><p>agent 可以调用的具体动作,比如读文件、写文件、搜索、执行命令、访问 MCP。</p></div>
|
||
<div class="glossary-card"><h3>Memory</h3><p>系统保存的历史、经验、任务结果和反馈,让后续运行可以参考。</p></div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:一句普通需求如何被后台处理</h3>
|
||
<p>适合开场讲解:先不讲代码,只讲用户输入、后台判断、最终输出。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>{
|
||
"user_message": "帮我总结 backend 的 Task 机制",
|
||
"channel": "web"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>后台理解</h4><pre>{
|
||
"is_task": true,
|
||
"reason": "需要阅读代码并产出解释",
|
||
"needs_tools": ["read_file", "search_files"],
|
||
"needs_memory": true
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"answer": "Task 是一张可追踪工作单...",
|
||
"task_created": true,
|
||
"saved_to_session": true,
|
||
"can_continue_later": true
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="stage" data-stage="overview">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>模块分层与依赖方向</h2>
|
||
<p class="hint">这一页只展示大模块位置和依赖方向,适合先建立全局地图。</p>
|
||
</div>
|
||
<div class="legend">
|
||
<span><i class="dot" style="background: var(--cyan)"></i>入口</span>
|
||
<span><i class="dot" style="background: var(--blue)"></i>服务</span>
|
||
<span><i class="dot" style="background: var(--green)"></i>运行内核</span>
|
||
<span><i class="dot" style="background: var(--violet)"></i>能力</span>
|
||
<span><i class="dot" style="background: var(--amber)"></i>状态</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="diagram">
|
||
<h3 class="diagram-title">四层架构:从入口到能力</h3>
|
||
<div class="diagram-row four">
|
||
<div class="diagram-card"><strong>Interfaces</strong><span>Web、CLI、Gateway、MCP 等入口</span></div>
|
||
<div class="diagram-card"><strong>Services</strong><span>把请求安排成内部工作流</span></div>
|
||
<div class="diagram-card"><strong>Engine</strong><span>统一 AgentLoop 执行模型调用和工具循环</span></div>
|
||
<div class="diagram-card"><strong>Capabilities</strong><span>Skills、Tools、Memory、Permissions</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="architecture-grid">
|
||
<div class="lane">
|
||
<h3>Interfaces 薄入口</h3>
|
||
<button class="node selected" data-kind="interface" data-node="web">
|
||
<strong>Web FastAPI</strong>
|
||
<span>REST、WebSocket、文件、skills、cron、tasks API</span>
|
||
</button>
|
||
<div class="connector">↓</div>
|
||
<button class="node" data-kind="interface" data-node="cli">
|
||
<strong>CLI / Gateway</strong>
|
||
<span>命令行和消息网关入口,共用服务层</span>
|
||
</button>
|
||
<div class="connector">↓</div>
|
||
<button class="node" data-kind="interface" data-node="mcp">
|
||
<strong>MCP Servers</strong>
|
||
<span>memory_server、tools_server 暴露协议能力</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="lane">
|
||
<h3>Application Services</h3>
|
||
<button class="node" data-kind="service" data-node="agent-service">
|
||
<strong>AgentService</strong>
|
||
<span>接口层统一入口,管理 running/direct 两种调用模式</span>
|
||
</button>
|
||
<div class="connector">↓</div>
|
||
<button class="node" data-kind="service" data-node="router">
|
||
<strong>MainAgentRouter</strong>
|
||
<span>把消息路由到 simple chat 或 internal Task mode</span>
|
||
</button>
|
||
<button class="node" data-kind="service" data-node="tasks">
|
||
<strong>TaskService + Validation</strong>
|
||
<span>创建任务、记录 run、反馈门控、验证结果</span>
|
||
</button>
|
||
<button class="node" data-kind="service" data-node="planner">
|
||
<strong>任务规划器</strong>
|
||
<span>判断单 agent 完成,还是拆成小型 agent team</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="lane">
|
||
<h3>Shared Runtime</h3>
|
||
<button class="node" data-kind="engine" data-node="engine-loader">
|
||
<strong>EngineLoader</strong>
|
||
<span>一次装配 session、memory、skills、tools、MCP</span>
|
||
</button>
|
||
<div class="connector">↓</div>
|
||
<button class="node" data-kind="engine" data-node="agent-loop">
|
||
<strong>AgentLoop</strong>
|
||
<span>所有 root agent 和 delegated agent 复用同一运行内核</span>
|
||
</button>
|
||
<button class="node" data-kind="engine" data-node="providers">
|
||
<strong>ProviderBundle</strong>
|
||
<span>main / auxiliary / embedding provider 路由</span>
|
||
</button>
|
||
<button class="node" data-kind="engine" data-node="tool-loop">
|
||
<strong>Tool Loop</strong>
|
||
<span>LLM 请求、tool calls、tool results、最终回复</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="lane">
|
||
<h3>Capabilities + State</h3>
|
||
<button class="node" data-kind="capability" data-node="skills">
|
||
<strong>Skills</strong>
|
||
<span>catalog、assembler、drafts、reviews、publisher、learning</span>
|
||
</button>
|
||
<button class="node" data-kind="capability" data-node="tools">
|
||
<strong>Tools</strong>
|
||
<span>registry、assembler、executor、built-in 和 MCP tools</span>
|
||
</button>
|
||
<button class="node" data-kind="store" data-node="memory">
|
||
<strong>Memory Stores</strong>
|
||
<span>session、curated memory、run receipts、skill learning</span>
|
||
</button>
|
||
<button class="node" data-kind="governance" data-node="permissions">
|
||
<strong>Permissions / Authz</strong>
|
||
<span>权限 profile、guard、authz-service 集成点</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:同一个请求经过四层架构</h3>
|
||
<p>用一条请求把每层职责串起来,说明每层只做自己的事。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>POST /api/chat
|
||
{
|
||
"session_id": "web-demo-001",
|
||
"message": "请分析这个项目的后端结构"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>层级流转</h4><pre>{
|
||
"interfaces": "接收 HTTP 请求",
|
||
"services": "判断是否进入 Task mode",
|
||
"engine": "调用模型并执行工具",
|
||
"capabilities": "提供 skill、tool、memory"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"session_id": "web-demo-001",
|
||
"task_id": "task_arch_001",
|
||
"message": "后端可以分为入口层、服务层..."
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="stage" data-stage="task-detail">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>Task 的概念:从一句话到可跟踪工作</h2>
|
||
<p class="hint">这一页围绕 Intent Agent、主 Agent、Sub-agent 和 Task 展示。讲解重点是:不是所有聊天都是 Task,只有需要执行、追踪和反馈的工作才进入 Task mode。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="diagram">
|
||
<h3 class="diagram-title">Task 概念图:谁负责什么</h3>
|
||
<div class="diagram-row six">
|
||
<div class="diagram-card"><strong>用户消息</strong><span>可能是闲聊,也可能是工作请求</span></div>
|
||
<div class="diagram-card"><strong>Intent Agent</strong><span>只判断路由,不回答问题</span></div>
|
||
<div class="diagram-card"><strong>Task</strong><span>需要追踪的工作单</span></div>
|
||
<div class="diagram-card"><strong>主 Agent</strong><span>负责最终答案和整体一致性</span></div>
|
||
<div class="diagram-card"><strong>Sub-agent</strong><span>只处理被拆出去的局部工作</span></div>
|
||
<div class="diagram-card"><strong>验收反馈</strong><span>满意、修改或放弃都会更新任务状态</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flow">
|
||
<div class="step"><div class="num">1</div><div><h3>用户输入</h3><p>用户可能只是问一句普通问题,也可能要求系统完成一件事,例如“分析后端架构”“修复测试”“整理技能文档”。</p></div></div>
|
||
<div class="step"><div class="num">2</div><div><h3>Intent Agent 判断意图</h3><p><span class="code">MainAgentRouter</span> 只做路由,不回答用户。它判断当前消息是 <span class="code">simple_chat</span>、<span class="code">new_task</span>、<span class="code">continue_task</span>、<span class="code">close_task</span> 还是 <span class="code">abandon_task</span>。</p></div></div>
|
||
<div class="step"><div class="num">3</div><div><h3>TaskService 创建或恢复任务</h3><p>如果要进入 Task mode,<span class="code">TaskService</span> 会创建 <span class="code">TaskRecord</span>,保存目标、状态、run 列表、反馈、验证结果和事件流。</p></div></div>
|
||
<div class="step"><div class="num">4</div><div><h3>主 Agent 负责最终回答</h3><p>主 Agent 通过 <span class="code">AgentLoop</span> 运行,负责综合上下文、技能、工具结果和 team 输出,最终给用户一个完整答复。</p></div></div>
|
||
<div class="step"><div class="num">5</div><div><h3>Sub-agent 只处理局部工作</h3><p>复杂任务会先交给任务规划器判断是否拆分。Sub-agent 处理调研、检查、实现片段等局部任务,结果再交回主 Agent 综合。</p></div></div>
|
||
<div class="step"><div class="num">6</div><div><h3>验证和反馈让 Task 闭环</h3><p>每次 Task run 后会记录 validation。用户点击满意、要求修改或放弃时,会更新 Task 状态,并影响后续学习候选。</p></div></div>
|
||
</div>
|
||
|
||
<div class="matrix" style="margin-top:14px">
|
||
<div class="stack"><h3>TaskRecord 里有什么</h3><ul><li><span class="code">task_id</span>、<span class="code">session_id</span>、目标和描述。</li><li><span class="code">status</span> 表示 open、running、awaiting_feedback、closed 等状态。</li><li><span class="code">run_ids</span> 连接每次模型运行。</li><li><span class="code">feedback</span> 和 <span class="code">validation_result</span> 记录结果质量。</li></ul></div>
|
||
<div class="stack"><h3>Task CRUD</h3><ul><li>Create:<span class="code">TaskService.create_task</span>。</li><li>Read:<span class="code">GET /api/tasks</span>、<span class="code">GET /api/tasks/{task_id}</span>。</li><li>Update:start run、append run、record validation、add feedback。</li><li>Delete:<span class="code">DELETE /api/tasks/{task_id}</span>。</li></ul></div>
|
||
<div class="stack"><h3>讲解抓手</h3><ul><li>Intent Agent 是分流员。</li><li>Task 是工作单。</li><li>主 Agent 是总负责人。</li><li>Sub-agent 是临时分工人员。</li><li>Validation 和 feedback 是验收记录。</li></ul></div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:Task 从创建到等待反馈</h3>
|
||
<p>这组数据可以用来解释 Task 不是一句回复,而是一张有状态的工作单。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>{
|
||
"session_id": "web-demo-001",
|
||
"message": "分析 backend 的 Task 机制,并给我讲解稿"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>关键数据格式</h4><pre>{
|
||
"router_decision": {
|
||
"action": "new_task",
|
||
"short_title": "Task机制讲解"
|
||
},
|
||
"task": {
|
||
"task_id": "task_001",
|
||
"status": "awaiting_feedback",
|
||
"run_ids": ["run_001"],
|
||
"skill_names": ["backend-reader"]
|
||
},
|
||
"validation_result": {
|
||
"accepted": true,
|
||
"score": 0.91
|
||
}
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"task_id": "task_001",
|
||
"task_status": "awaiting_feedback",
|
||
"answer": "Task 机制可以理解为...",
|
||
"feedback_options": [
|
||
"satisfied",
|
||
"revise",
|
||
"abandon"
|
||
]
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="stage" data-stage="learning-detail">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>自学习:从 Task 反馈到 Skill 生命周期</h2>
|
||
<p class="hint">这一页围绕 Task、Skill、自学习 Skill 的 CRUD 展开。讲解重点是:系统不会自动发布新技能,必须经过候选、草稿、安全检查、评估、审核和发布。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="diagram">
|
||
<h3 class="diagram-title">自学习闭环:成功经验如何变成可复用 Skill</h3>
|
||
<div class="diagram-row six">
|
||
<div class="diagram-card"><strong>Task 成功</strong><span>有结果、验证和用户满意反馈</span></div>
|
||
<div class="diagram-card"><strong>Learning Candidate</strong><span>系统发现这次经验值得沉淀</span></div>
|
||
<div class="diagram-card"><strong>Skill Draft</strong><span>生成一个待审核的技能草稿</span></div>
|
||
<div class="diagram-card"><strong>Safety / Eval</strong><span>安全检查和效果评估</span></div>
|
||
<div class="diagram-card"><strong>Review</strong><span>人工 approve 或 reject</span></div>
|
||
<div class="diagram-card"><strong>Publish</strong><span>发布成可选择的新版本 Skill</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flow">
|
||
<div class="step"><div class="num">1</div><div><h3>Task 产生证据</h3><p>每次 Task run 都会写入 run record、激活过的 skill receipt、tool 使用结果、validation 和用户反馈。</p></div></div>
|
||
<div class="step"><div class="num">2</div><div><h3>满意反馈触发候选</h3><p>当用户反馈 <span class="code">satisfied</span> 且 validation 通过时,<span class="code">SkillLearningService</span> 可以基于这次成功经验生成 learning candidate。</p></div></div>
|
||
<div class="step"><div class="num">3</div><div><h3>候选生成 Draft</h3><p><span class="code">POST /api/skills/candidates/{candidate_id}/draft</span> 会合成技能草稿,同时生成 safety report 和 eval report。</p></div></div>
|
||
<div class="step"><div class="num">4</div><div><h3>人工 Review</h3><p>草稿可 submit、approve 或 reject。高风险内容不能静默发布,发布接口需要显式确认。</p></div></div>
|
||
<div class="step"><div class="num">5</div><div><h3>Publish / Disable / Rollback</h3><p>通过后进入 published skill;后续可以 disable 或 rollback 到指定版本。Worker 只生成和评估,不自动 approve/publish。</p></div></div>
|
||
</div>
|
||
|
||
<div class="matrix" style="margin-top:14px">
|
||
<div class="stack"><h3>Candidate CRUD</h3><ul><li>List:<span class="code">GET /api/skills/candidates</span>。</li><li>Read:<span class="code">GET /api/skills/candidates/{candidate_id}</span>。</li><li>Create:由满意 Task 反馈或 worker run-once 生成。</li><li>Update:合成 draft、regenerate draft、写 audit event。</li></ul></div>
|
||
<div class="stack"><h3>Draft / Review CRUD</h3><ul><li>List:<span class="code">GET /api/skills/drafts</span>。</li><li>Read:draft、safety、eval 三类详情接口。</li><li>Update:submit、approve、reject。</li><li>Delete:底层 <span class="code">DraftService.delete_draft</span> 支持删除草稿。</li></ul></div>
|
||
<div class="stack"><h3>Published Skill CRUD</h3><ul><li>Create:<span class="code">publish</span> 生成新版本。</li><li>Read:skills list、detail、version、file、download。</li><li>Update:disable、rollback。</li><li>Delete:<span class="code">DELETE /api/skills/{name}</span> 删除或归档技能入口。</li></ul></div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:一次满意反馈如何变成 Skill 草稿</h3>
|
||
<p>适合讲“自学习不是自动上线”,它只是生成候选和草稿,后面仍要审核。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>POST /api/chat/feedback
|
||
{
|
||
"session_id": "web-demo-001",
|
||
"run_id": "run_001",
|
||
"feedback_type": "satisfied",
|
||
"comment": "这次后端讲解结构很好"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>关键数据格式</h4><pre>{
|
||
"candidate": {
|
||
"candidate_id": "cand_001",
|
||
"status": "candidate",
|
||
"trigger_run_id": "run_001",
|
||
"evidence": ["Task 通过验证", "用户满意"]
|
||
},
|
||
"draft": {
|
||
"draft_id": "draft_001",
|
||
"skill_name": "backend-explainer",
|
||
"status": "draft",
|
||
"proposal_kind": "new_skill"
|
||
}
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"learning_candidate_created": true,
|
||
"draft_created": true,
|
||
"safety_report": "passed",
|
||
"eval_report": "passed",
|
||
"next_step": "submit_review"
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="stage" data-stage="skill-tool-detail">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>Skill 和 Tool:如何选技能、选工具,以及 Skill 里的 tool 字段</h2>
|
||
<p class="hint">这一页关注 Skill 选择器、工具选择器,以及 skill 中工具提示字段如何影响工具暴露。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="diagram">
|
||
<h3 class="diagram-title">Skill / Tool 选择链路</h3>
|
||
<div class="diagram-row six">
|
||
<div class="diagram-card"><strong>任务描述</strong><span>本轮要解决的问题</span></div>
|
||
<div class="diagram-card"><strong>Skill 召回</strong><span>embedding 找到候选 Skill</span></div>
|
||
<div class="diagram-card"><strong>Skill 精选</strong><span>LLM 选择真正激活的 Skill</span></div>
|
||
<div class="diagram-card"><strong>tool_hints</strong><span>Skill 建议常用工具</span></div>
|
||
<div class="diagram-card"><strong>Tool 选择</strong><span>always tools + hints + embedding top-k</span></div>
|
||
<div class="diagram-card"><strong>Tool Schema</strong><span>只把选中的工具暴露给模型</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flow">
|
||
<div class="step"><div class="num">1</div><div><h3>Skill 选择器先看任务</h3><p><span class="code">SkillAssembler</span> 读取所有可选 skill 摘要,用 embedding 召回候选,再让辅助模型从候选中选出本轮真正激活的 skill。</p></div></div>
|
||
<div class="step"><div class="num">2</div><div><h3>激活 Skill 变成上下文</h3><p>被选中的 skill 会变成 <span class="code">SkillContext</span>,写入 system prompt,并生成 <span class="code">SkillActivationReceipt</span> 方便后续学习和审计。</p></div></div>
|
||
<div class="step"><div class="num">3</div><div><h3>Skill 的 tool_hints 影响工具选择</h3><p>Skill 版本里有 <span class="code">tool_hints</span>。如果某个 skill 明确提示需要 <span class="code">read_file</span>、<span class="code">patch_file</span> 等工具,工具选择器会优先加入这些工具。</p></div></div>
|
||
<div class="step"><div class="num">4</div><div><h3>工具选择器再补充工具</h3><p><span class="code">ToolAssembler</span> 先加入 always tools,再加入 skill hints,再通过 embedding 从 registry 中召回更多相关工具。</p></div></div>
|
||
<div class="step"><div class="num">5</div><div><h3>ToolRegistry 导出 schema</h3><p><span class="code">ToolRegistry</span> 不执行工具,只把选中的 <span class="code">ToolSpec</span> 导出成 provider 可消费的 function schema。</p></div></div>
|
||
<div class="step"><div class="num">6</div><div><h3>ToolExecutor 执行调用</h3><p>模型返回 tool call 后,<span class="code">ToolExecutor</span> 执行工具,并把 tool result 写回 session history,供下一轮模型继续使用。</p></div></div>
|
||
</div>
|
||
|
||
<div class="matrix" style="margin-top:14px">
|
||
<div class="stack"><h3>Skill 中关键字段</h3><ul><li><span class="code">name</span>:技能唯一名。</li><li><span class="code">description</span>:选择器理解技能用途。</li><li><span class="code">content</span>:真正注入给 agent 的指导正文。</li><li><span class="code">tool_hints</span>:建议本 skill 常用哪些工具。</li><li><span class="code">tags</span>、<span class="code">owners</span>:分类和治理信息。</li></ul></div>
|
||
<div class="stack"><h3>ToolSpec 中关键字段</h3><ul><li><span class="code">name</span>:模型调用函数名。</li><li><span class="code">description</span>:模型理解工具何时可用。</li><li><span class="code">input_schema</span>:参数结构。</li><li><span class="code">toolset</span>:所属工具集。</li><li><span class="code">always_available</span>:是否每轮都暴露。</li></ul></div>
|
||
<div class="stack"><h3>选择顺序</h3><ul><li>Skill:候选召回 -> LLM shortlist -> LLM final -> 注入上下文。</li><li>Tool:always tools -> skill tool_hints -> embedding top-k。</li><li>最终:只把选中的工具 schema 给模型,减少噪音和误用。</li></ul></div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:Skill 选择器和工具选择器如何配合</h3>
|
||
<p>用一个 Skill 的 frontmatter 解释 <span class="code">tool_hints</span> 为什么会影响本轮可用工具。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>{
|
||
"task_description": "阅读 backend 代码并解释 Task 机制"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>关键数据格式</h4><pre>---
|
||
name: backend-explainer
|
||
description: Explain backend architecture
|
||
tool_hints:
|
||
- search_files
|
||
- read_file
|
||
- session_search
|
||
---
|
||
先定位入口,再追踪服务层和运行内核。</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"activated_skills": [
|
||
"backend-explainer"
|
||
],
|
||
"selected_tools": [
|
||
"memory",
|
||
"session_search",
|
||
"search_files",
|
||
"read_file"
|
||
],
|
||
"tool_schema_count": 4
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="stage" data-stage="memory-detail">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>Memory 和 Session:CRUD、装载与运行时快照</h2>
|
||
<p class="hint">这一页解释会话、记忆和运行记录如何创建、读取、更新、归档,以及 AgentLoop 如何装载它们。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="diagram">
|
||
<h3 class="diagram-title">Memory / Session 装载和写回</h3>
|
||
<div class="diagram-row six">
|
||
<div class="diagram-card"><strong>EngineLoader</strong><span>创建状态组件</span></div>
|
||
<div class="diagram-card"><strong>Session</strong><span>保存对话消息和事件</span></div>
|
||
<div class="diagram-card"><strong>Memory Snapshot</strong><span>每次 run 捕获一份快照</span></div>
|
||
<div class="diagram-card"><strong>AgentLoop</strong><span>用快照组装上下文并执行</span></div>
|
||
<div class="diagram-card"><strong>Run Memory</strong><span>记录运行证据和 skill effect</span></div>
|
||
<div class="diagram-card"><strong>Feedback</strong><span>反馈反写到任务和学习数据</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flow">
|
||
<div class="step"><div class="num">1</div><div><h3>EngineLoader 装载状态组件</h3><p><span class="code">EngineLoader.load</span> 创建 <span class="code">SessionManager</span>、curated <span class="code">MemoryStore</span>、<span class="code">MemoryService</span>、<span class="code">RunMemoryStore</span> 和 <span class="code">SkillLearningStore</span>。</p></div></div>
|
||
<div class="step"><div class="num">2</div><div><h3>Session 保存对话过程</h3><p>每次 run 都会确保 session 存在,并追加 system、user、assistant、tool 等消息,同时记录 event payload、usage、run_id 和上下文可见性。</p></div></div>
|
||
<div class="step"><div class="num">3</div><div><h3>MemoryService 捕获快照</h3><p>AgentLoop 每次运行都会调用 <span class="code">capture_snapshot_for_run</span>。并行 team run 各拿自己的 frozen snapshot,避免互相覆盖。</p></div></div>
|
||
<div class="step"><div class="num">4</div><div><h3>RunMemoryStore 保存运行证据</h3><p>run record 记录输入、输出、激活 skill、验证、反馈;skill effect 记录某个 skill 在这次 run 中的效果。</p></div></div>
|
||
<div class="step"><div class="num">5</div><div><h3>API 提供 Session CRUD</h3><p>前端通过 sessions API 创建、查看、归档会话,也可以读取 session process 和 debug chat logs。</p></div></div>
|
||
</div>
|
||
|
||
<div class="matrix" style="margin-top:14px">
|
||
<div class="stack"><h3>Session CRUD</h3><ul><li>Create:<span class="code">POST /api/sessions/{session_id}</span> 或 run 时 <span class="code">ensure_session</span>。</li><li>Read:<span class="code">GET /api/sessions</span>、<span class="code">GET /api/sessions/{session_id}</span>。</li><li>Update:<span class="code">append_message</span>、<span class="code">update_usage</span>、<span class="code">update_system_prompt</span>。</li><li>Delete/Archive:<span class="code">DELETE /api/sessions/{session_id}</span> 或 archive。</li></ul></div>
|
||
<div class="stack"><h3>Memory / Run CRUD</h3><ul><li>Create:append run record、append skill effect、curated memory item。</li><li>Read:session search、memory tool、run list、skill effects。</li><li>Update:validation、feedback、success ratio、performance snapshot。</li><li>Archive:通过 session end reason 或 skill/version 状态保留历史。</li></ul></div>
|
||
<div class="stack"><h3>装载顺序</h3><ul><li>配置解析 workspace。</li><li>初始化 curated memory store。</li><li>创建 session 和 run stores。</li><li>注入 ToolContext services。</li><li>运行时按 run 捕获 memory snapshot。</li></ul></div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:Session、Message、Memory Snapshot 的关系</h3>
|
||
<p>适合讲“为什么能继续对话”,以及“为什么并行 sub-agent 不会互相污染记忆”。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>POST /api/sessions/web-demo-001
|
||
|
||
POST /api/chat
|
||
{
|
||
"session_id": "web-demo-001",
|
||
"message": "继续解释刚才的 Task"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>关键数据格式</h4><pre>{
|
||
"session": {
|
||
"id": "web-demo-001",
|
||
"source": "web",
|
||
"message_count": 12
|
||
},
|
||
"message": {
|
||
"role": "user",
|
||
"run_id": "run_002",
|
||
"content": "继续解释刚才的 Task"
|
||
},
|
||
"memory_snapshot": {
|
||
"captured_for_run": "run_002",
|
||
"frozen": true
|
||
}
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"session_loaded": true,
|
||
"history_used": true,
|
||
"run_id": "run_002",
|
||
"assistant_message_saved": true
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="stage" data-stage="request">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>一次用户请求如何流过后端</h2>
|
||
<p class="hint">从 WebSocket/HTTP 进入,到 AgentLoop 选择 skills/tools,最后写回 session 和 run memory。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="diagram">
|
||
<h3 class="diagram-title">请求主链:技术视角</h3>
|
||
<div class="diagram-row six">
|
||
<div class="diagram-card"><strong>HTTP / WS</strong><span>接口收到用户请求</span></div>
|
||
<div class="diagram-card"><strong>AgentService</strong><span>统一收口 direct / running 模式</span></div>
|
||
<div class="diagram-card"><strong>Router</strong><span>判断 simple chat 或 Task mode</span></div>
|
||
<div class="diagram-card"><strong>Planner</strong><span>选择 single 或 team</span></div>
|
||
<div class="diagram-card"><strong>AgentLoop</strong><span>模型调用和工具循环</span></div>
|
||
<div class="diagram-card"><strong>Validation</strong><span>记录验证、反馈和学习证据</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flow">
|
||
<div class="step"><div class="num">1</div><div><h3>接口接收请求</h3><p><span class="code">interfaces/web/app.py</span> 提供 <span class="code">/api/chat</span>、<span class="code">/ws/{session_id}</span>、tasks、files、skills、cron 等入口,只做参数转换和响应拼装。</p></div></div>
|
||
<div class="step"><div class="num">2</div><div><h3>AgentService 收口</h3><p>接口层不直接 new loop,而是调用 <span class="code">AgentService.process_direct</span> 或 <span class="code">submit_direct</span>,由 service 管理 direct/running mode。</p></div></div>
|
||
<div class="step"><div class="num">3</div><div><h3>Intent Agent 路由</h3><p><span class="code">MainAgentRouter</span> 用辅助模型判断 simple chat、继续任务、新建任务、关闭或放弃任务;simple chat 会关闭 skill/tool assembly。</p></div></div>
|
||
<div class="step"><div class="num">4</div><div><h3>Task mode 规划</h3><p><span class="code">TaskService</span> 创建或恢复任务,任务规划器决定单 agent 还是 team;team 模式先运行 sub-agent,再交给主 agent synthesis。</p></div></div>
|
||
<div class="step"><div class="num">5</div><div><h3>EngineLoader 装配运行时</h3><p>统一加载 session manager、memory service、skills loader、tool registry、tool executor、skill learning pipeline、agent registry、MCP manager。</p></div></div>
|
||
<div class="step"><div class="num">6</div><div><h3>AgentLoop 运行主链</h3><p>捕获 memory snapshot,组装 prompt,选择 skills 和 tools,调用 provider;若返回 tool calls,则执行 tool loop 并把结果追加回上下文。</p></div></div>
|
||
<div class="step"><div class="num">7</div><div><h3>验证、反馈和学习沉淀</h3><p>Task 结果进入 validation 和 feedback gate;满意反馈会更新 run record、skill effects,并可能生成 assisted learning candidate。</p></div></div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:完整 /api/chat 输入输出</h3>
|
||
<p>这可以作为接口级例子:用户输入什么,后端可能返回什么。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>POST /api/chat
|
||
{
|
||
"session_id": "web-demo-001",
|
||
"message": "帮我画出 Task、自学习、Skill 和 Memory 的关系",
|
||
"thinking_enabled": true
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>关键内部事件</h4><pre>[
|
||
{"event_type": "intent_agent_decision", "action": "new_task"},
|
||
{"event_type": "task_execution_planned", "mode": "team"},
|
||
{"event_type": "skill_activation_snapshotted", "skills": ["backend-explainer"]},
|
||
{"event_type": "tool_selection_snapshotted", "tools": ["search_files", "read_file"]},
|
||
{"event_type": "task_validation_snapshotted", "accepted": true}
|
||
]</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"session_id": "web-demo-001",
|
||
"run_id": "run_003",
|
||
"task_id": "task_002",
|
||
"task_status": "awaiting_feedback",
|
||
"message": "可以把这四者理解为..."
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="stage" data-stage="team">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>Agent Team:什么时候拆分、怎么编排、如何回到主 Agent</h2>
|
||
<p class="hint">这一页展示 Agent Team 的完整概念:注册的 agent 能力、Task planner 的 team plan、ExecutionGraph 的三种策略、每个节点的 skill 绑定,以及最终 synthesis。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="diagram">
|
||
<h3 class="diagram-title">Agent Team 执行图</h3>
|
||
<div class="diagram-row six">
|
||
<div class="diagram-card"><strong>Task</strong><span>复杂工作进入任务模式</span></div>
|
||
<div class="diagram-card"><strong>Team Plan</strong><span>Planner 生成执行图</span></div>
|
||
<div class="diagram-card"><strong>Skill Binding</strong><span>为每个节点绑定 Skill 或临时指导</span></div>
|
||
<div class="diagram-card"><strong>Scheduler</strong><span>按 sequence、parallel 或 DAG 推进</span></div>
|
||
<div class="diagram-card"><strong>Sub-agents</strong><span>节点回到同一个 AgentLoop 执行</span></div>
|
||
<div class="diagram-card"><strong>Main Synthesis</strong><span>主 Agent 综合所有节点输出</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flow" style="margin-bottom:14px">
|
||
<div class="step"><div class="num">1</div><div><h3>先判断是否值得组队</h3><p>任务规划器只在独立调研、评审、实现切片、分阶段检查能明显提升结果时选择 team,否则保持 single agent。</p></div></div>
|
||
<div class="step"><div class="num">2</div><div><h3>生成 ExecutionGraph</h3><p>team plan 会生成 <span class="code">ExecutionGraph</span>,包含 strategy、nodes、node task、depends_on、expected_output 和 skill_query。</p></div></div>
|
||
<div class="step"><div class="num">3</div><div><h3>为节点绑定 Skill</h3><p><span class="code">TaskSkillResolver</span> 为每个 node 选择 published skill;如果没有合适 skill,就合成 one-run ephemeral guidance。</p></div></div>
|
||
<div class="step"><div class="num">4</div><div><h3>LocalAgentRunner 执行节点</h3><p>每个 sub-agent 节点都会通过 <span class="code">LocalAgentRunner</span> 调回同一个 <span class="code">AgentLoop</span>,只是 session_id、source、execution_context 不同。</p></div></div>
|
||
<div class="step"><div class="num">5</div><div><h3>主 Agent 综合输出</h3><p>Team 的 node outputs 不直接作为最终答案,而是进入主 Agent 的 synthesis context,由主 Agent 负责最终一致性、取舍和用户回答。</p></div></div>
|
||
</div>
|
||
|
||
<div class="team-grid">
|
||
<div class="strategy">
|
||
<h3>Sequence</h3>
|
||
<div class="mini-flow">
|
||
<span class="mini-node">node A</span><span class="mini-arrow">→</span><span class="mini-node">node B</span><span class="mini-arrow">→</span><span class="mini-node">main synthesis</span>
|
||
</div>
|
||
<p class="hint">上游输出作为下游 dependency output;任一节点失败,后续节点被标记 blocked。</p>
|
||
</div>
|
||
<div class="strategy">
|
||
<h3>Parallel</h3>
|
||
<div class="mini-flow">
|
||
<span class="mini-node">node A</span><span class="mini-node">node B</span><span class="mini-node">node C</span><span class="mini-arrow">→</span><span class="mini-node">synthesis</span>
|
||
</div>
|
||
<p class="hint">适合独立调研、并行检查或拆分实现面;由 <span class="code">asyncio.gather</span> 并发运行。</p>
|
||
</div>
|
||
<div class="strategy">
|
||
<h3>DAG</h3>
|
||
<div class="mini-flow">
|
||
<span class="mini-node">research</span><span class="mini-arrow">→</span><span class="mini-node">design</span><span class="mini-arrow">→</span><span class="mini-node">review</span>
|
||
</div>
|
||
<p class="hint">显式 <span class="code">depends_on</span> 控制依赖,scheduler 按 ready batch 推进并检测循环依赖。</p>
|
||
</div>
|
||
<div class="strategy">
|
||
<h3>Skill Binding</h3>
|
||
<div class="mini-flow">
|
||
<span class="mini-node">skill query</span><span class="mini-arrow">→</span><span class="mini-node">published skill</span><span class="mini-arrow">/</span><span class="mini-node">ephemeral guidance</span>
|
||
</div>
|
||
<p class="hint"><span class="code">TaskSkillResolver</span> 优先 pin 已发布 skill;缺失时合成 one-run guidance,不自动发布。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="matrix" style="margin-top:14px">
|
||
<div class="stack"><h3>Agent Team CRUD</h3><ul><li>List:<span class="code">GET /api/agents</span>、<span class="code">GET /api/subagents</span>。</li><li>Create:<span class="code">POST /api/agents</span>、<span class="code">POST /api/subagents</span>。</li><li>Update:<span class="code">PATCH /api/agents/{agent_id}</span>、<span class="code">PUT /api/subagents/{agent_id}</span>、disable。</li><li>Delete:<span class="code">DELETE /api/agents/{agent_id}</span>、<span class="code">DELETE /api/subagents/{agent_id}</span>。</li></ul></div>
|
||
<div class="stack"><h3>节点输入里有什么</h3><ul><li>父 task id 和父 run id。</li><li>节点自己的 task 和 expected output。</li><li>依赖节点的输出。</li><li>pinned skills 或 ephemeral guidance。</li><li>节点角色、约束和 skill selection context。</li></ul></div>
|
||
<div class="stack"><h3>失败如何处理</h3><ul><li>Sequence 中前序失败会阻塞后续节点。</li><li>DAG 中依赖失败会阻塞依赖它的节点。</li><li>Parallel 节点独立运行,失败会进入 team summary。</li><li>主 Agent 会看到失败摘要,再决定如何向用户说明。</li></ul></div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:Agent Team 把复杂任务拆成三个节点</h3>
|
||
<p>这个例子适合讲 sequence、parallel、DAG 之前先让用户理解“节点输出最后还要由主 Agent 综合”。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>{
|
||
"task_id": "task_002",
|
||
"user_message": "检查后端架构讲解是否完整,并补上缺口"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>关键数据格式</h4><pre>{
|
||
"mode": "team",
|
||
"strategy": "dag",
|
||
"nodes": [
|
||
{"node_id": "read", "task": "阅读现有文档", "depends_on": []},
|
||
{"node_id": "check", "task": "检查缺失主题", "depends_on": ["read"]},
|
||
{"node_id": "draft", "task": "补写讲解内容", "depends_on": ["check"]}
|
||
]
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"team_success": true,
|
||
"node_results": [
|
||
{"node_id": "read", "success": true},
|
||
{"node_id": "check", "success": true},
|
||
{"node_id": "draft", "success": true}
|
||
],
|
||
"main_synthesis": "已补充 Task、自学习、Skill/Tool、Memory/Session 和 Agent Team 示例。"
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="stage" data-stage="capabilities">
|
||
<div class="stage-title">
|
||
<div>
|
||
<h2>能力层和状态边界</h2>
|
||
<p class="hint">这些层围绕 AgentLoop 工作,但各自保持独立职责。</p>
|
||
</div>
|
||
</div>
|
||
<div class="matrix">
|
||
<div class="stack">
|
||
<h3>Skills</h3>
|
||
<ul>
|
||
<li><span class="code">SkillsLoader</span> 读取 published skill 和 selection candidates。</li>
|
||
<li><span class="code">SkillAssembler</span> 通过 embedding 召回和 LLM 决策激活 skill。</li>
|
||
<li>draft、review、publisher、learning pipeline 支持候选生成、审核和发布。</li>
|
||
</ul>
|
||
</div>
|
||
<div class="stack">
|
||
<h3>Tools</h3>
|
||
<ul>
|
||
<li><span class="code">ToolRegistry</span> 只负责注册、查找和导出 provider schema。</li>
|
||
<li><span class="code">ToolAssembler</span> 结合 always tools、skill hints 和 embedding top-k。</li>
|
||
<li><span class="code">ToolExecutor</span> 执行 tool call,结果写入 session history。</li>
|
||
</ul>
|
||
</div>
|
||
<div class="stack">
|
||
<h3>Memory</h3>
|
||
<ul>
|
||
<li>每个 run 捕获自己的 frozen memory snapshot,避免并行 team run 串扰。</li>
|
||
<li>session manager 保存消息、事件 payload、usage、归档状态。</li>
|
||
<li>run memory 保存 receipts、validation、feedback、skill effects。</li>
|
||
</ul>
|
||
</div>
|
||
<div class="stack">
|
||
<h3>Providers</h3>
|
||
<ul>
|
||
<li><span class="code">ProviderBundle</span> 分离 main、auxiliary、embedding runtime。</li>
|
||
<li>router、planner、skill selection 可走 auxiliary provider。</li>
|
||
<li>主回答、tool loop 和 synthesis 走 main provider。</li>
|
||
</ul>
|
||
</div>
|
||
<div class="stack">
|
||
<h3>Integrations</h3>
|
||
<ul>
|
||
<li>MCP manager 连接外部 MCP server,并把 tools 注册进 registry。</li>
|
||
<li>Outlook、WhatsApp、A2A、authz 保持在 integrations 边界内。</li>
|
||
<li>Web API 只暴露集成状态、连接测试和数据读取入口。</li>
|
||
</ul>
|
||
</div>
|
||
<div class="stack">
|
||
<h3>Cron</h3>
|
||
<ul>
|
||
<li><span class="code">CronService</span> 在 FastAPI lifespan 中随 AgentService 启停。</li>
|
||
<li>notification 生成通知结果,task mode 生成可反馈的 TaskRecord。</li>
|
||
<li>用户 engage 后可把定时结果转成继续编辑的任务。</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="demo">
|
||
<h3>Demo:一次运行会同时用到哪些能力</h3>
|
||
<p>适合收尾时复盘:一次 Task 运行不是只调用模型,而是多个能力层一起工作。</p>
|
||
<div class="demo-grid">
|
||
<div class="demo-block"><h4>输入</h4><pre>{
|
||
"task": "生成后端架构讲解页",
|
||
"session_id": "web-demo-001"
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>能力调用</h4><pre>{
|
||
"providers": ["main", "auxiliary", "embedding"],
|
||
"skills": ["backend-explainer"],
|
||
"tools": ["search_files", "read_file", "write_file"],
|
||
"memory": ["session_history", "run_record"],
|
||
"integrations": ["mcp_optional"]
|
||
}</pre></div>
|
||
<div class="demo-block"><h4>预期输出</h4><pre>{
|
||
"artifact": "backend-visualization.html",
|
||
"session_updated": true,
|
||
"run_memory_written": true,
|
||
"skill_effect_recorded": true
|
||
}</pre></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
|
||
</div>
|
||
</main>
|
||
|
||
<script>
|
||
const tabs = Array.from(document.querySelectorAll(".tab"));
|
||
const stages = Array.from(document.querySelectorAll(".stage"));
|
||
tabs.forEach((tab) => {
|
||
tab.addEventListener("click", () => {
|
||
const view = tab.dataset.view;
|
||
tabs.forEach((item) => item.setAttribute("aria-selected", String(item === tab)));
|
||
stages.forEach((stage) => stage.classList.toggle("active", stage.dataset.stage === view));
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll(".node").forEach((node) => {
|
||
node.addEventListener("click", () => {
|
||
document.querySelectorAll(".node").forEach((candidate) => candidate.classList.remove("selected"));
|
||
node.classList.add("selected");
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|