agent-architect / index.html
willian166's picture
Upload folder using huggingface_hub
e268813 verified
Raw
History Blame Contribute Delete
12 kB
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Agent 架构师</title>
<style>
:root {
color-scheme: light;
--bg: #f7f8fb;
--panel: #ffffff;
--text: #20242c;
--muted: #667085;
--line: #d9dee8;
--accent: #176b87;
--accent-2: #2f8f6f;
--user: #e8f4f8;
--assistant: #ffffff;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
background: var(--bg);
color: var(--text);
font-family: Arial, "Microsoft YaHei", sans-serif;
}
.app {
display: grid;
grid-template-columns: minmax(0, 1fr) 360px;
min-height: 100vh;
}
main {
display: flex;
flex-direction: column;
min-width: 0;
}
header {
padding: 22px 28px 14px;
border-bottom: 1px solid var(--line);
background: var(--panel);
}
h1 {
margin: 0 0 6px;
font-size: 24px;
font-weight: 700;
letter-spacing: 0;
}
.subtitle {
margin: 0;
color: var(--muted);
font-size: 14px;
line-height: 1.5;
}
#chat {
flex: 1;
overflow-y: auto;
padding: 24px 28px;
}
.message {
max-width: 860px;
margin-bottom: 14px;
padding: 14px 16px;
border: 1px solid var(--line);
border-radius: 8px;
line-height: 1.65;
white-space: pre-wrap;
}
.assistant {
background: var(--assistant);
}
.user {
margin-left: auto;
background: var(--user);
border-color: #b9dce8;
}
.composer {
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
gap: 10px;
padding: 16px 28px 22px;
border-top: 1px solid var(--line);
background: var(--panel);
}
textarea {
width: 100%;
min-height: 48px;
max-height: 160px;
resize: vertical;
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: 8px;
color: var(--text);
font: inherit;
}
button,
a.button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 48px;
padding: 0 16px;
border: 1px solid var(--accent);
border-radius: 8px;
background: var(--accent);
color: #fff;
font: inherit;
text-decoration: none;
cursor: pointer;
}
button.secondary {
background: #fff;
color: var(--accent);
}
aside {
border-left: 1px solid var(--line);
background: var(--panel);
padding: 22px 18px;
}
h2 {
margin: 0 0 14px;
font-size: 16px;
}
.status {
display: grid;
gap: 10px;
}
.step {
padding: 10px 12px;
border: 1px solid var(--line);
border-radius: 8px;
color: var(--muted);
font-size: 14px;
}
.step.active {
border-color: var(--accent);
color: var(--accent);
background: #eef8fb;
}
.step.done {
border-color: #b8dfcf;
color: var(--accent-2);
background: #f0faf5;
}
#download {
width: 100%;
margin-top: 18px;
display: none;
}
@media (max-width: 860px) {
.app {
grid-template-columns: 1fr;
}
aside {
border-left: 0;
border-top: 1px solid var(--line);
}
.composer {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="app">
<main>
<header>
<h1>Agent 架构师</h1>
<p class="subtitle">通过 6 轮访谈,把一句话需求整理成可下载的 Agent 岗位卡。</p>
</header>
<section id="chat" aria-live="polite"></section>
<section class="composer">
<textarea id="input" placeholder="请输入你的回答,例如:我想做一个抖音文案 Agent"></textarea>
<button id="send">发送</button>
<button id="reset" class="secondary">重新开始</button>
</section>
</main>
<aside>
<h2>访谈进度</h2>
<div id="steps" class="status"></div>
<a id="download" class="button" download="agent-card.md">下载 Markdown</a>
</aside>
</div>
<script>
const questions = [
["input", "第1轮:输入", "这个工作的输入是什么?从哪里来?什么格式?"],
["workflow", "第2轮:处理动作", "拿到输入后,具体要做哪几步?请描述主要动作,我会帮你拆成 3-8 个步骤。"],
["output", "第3轮:输出", "做完之后输出什么?放哪里?什么格式?"],
["success", "第4轮:成功标准", "怎么判断做对了?怎么判断做错了?"],
["fallback", "第5轮:人工兜底", "什么情况需要人工介入?你希望在哪个环节检查?"],
["outOfScope", "第6轮:本期不做", "有什么是这个 Agent 现在明确不应该做的?请列 3-5 条。"]
];
const state = {
step: 0,
answers: {},
done: false,
card: ""
};
const chat = document.querySelector("#chat");
const input = document.querySelector("#input");
const send = document.querySelector("#send");
const reset = document.querySelector("#reset");
const steps = document.querySelector("#steps");
const download = document.querySelector("#download");
function addMessage(role, text) {
const div = document.createElement("div");
div.className = `message ${role}`;
div.textContent = text;
chat.appendChild(div);
chat.scrollTop = chat.scrollHeight;
}
function renderSteps() {
steps.innerHTML = "";
questions.forEach((question, index) => {
const div = document.createElement("div");
div.className = "step";
if (index < state.step || state.done) div.classList.add("done");
if (index === state.step && !state.done) div.classList.add("active");
div.textContent = question[1];
steps.appendChild(div);
});
}
function summarize(key, value) {
const labels = {
input: "输入",
workflow: "处理动作",
output: "输出",
success: "成功标准",
fallback: "人工兜底",
outOfScope: "本期不做"
};
return `${labels[key]}${value.trim().replace(/\s+/g, " ")}`;
}
function splitItems(text, fallback) {
const items = text
.replace(/[;;。]/g, "\n")
.split("\n")
.map((item) => item.replace(/^[\s\-\d.、]+/, "").trim())
.filter(Boolean);
return items.length > 1 ? items.slice(0, 8) : [text.trim() || fallback];
}
function agentName() {
const seed = `${state.answers.input || ""} ${state.answers.output || ""}`;
if (seed.includes("文案")) return "文案架构师";
if (seed.includes("图片") || seed.includes("提示词")) return "提示词助手";
if (seed.includes("客服")) return "客服助手";
if (seed.includes("抖音")) return "抖音助手";
return "岗位卡助手";
}
function buildCard() {
const workflow = splitItems(state.answers.workflow || "", "根据用户回答执行任务");
const outOfScope = splitItems(state.answers.outOfScope || "", "不做超出本期范围的事项").slice(0, 5);
while (outOfScope.length < 3) outOfScope.push("不在信息不清楚时编造细节");
const workflowMd = workflow.map((item, index) => `${index + 1}. ${item}`).join("\n");
const scopeMd = outOfScope.map((item, index) => `- 不做 ${index + 1}${item}`).join("\n");
return `## Agent 岗位卡
### 岗位名称
${agentName()}
### 一句话岗位定义
当收到用户需求时,自动分析并追问关键信息,生成可直接交给 Agent 使用的岗位卡,并提供 Markdown 文件下载。
### 输入
- 输入 1:${state.answers.input || "用户在对话中提交的自然语言需求。"}
### 处理动作
${workflowMd}
### 输出
- 输出 1:${state.answers.output || "完整的 Agent 岗位卡;在 Hugging Face Spaces 页面中提供下载按钮;格式为 .md Markdown 文件。"}
### 成功标准
- 做对了:${state.answers.success || "岗位卡完整、清晰、可执行,格式符合 Markdown,用户下载后可以直接使用。"}
- 做错了:信息缺失、没有追问清楚就生成、内容空泛、格式不符合 Markdown,或下载后不能直接用于指导 Agent 工作。
### 人工兜底
- 介入条件:${state.answers.fallback || "用户需求模糊、前后矛盾、涉及高风险内容,或多轮追问后仍无法确认关键信息。"}
- 检查环节:每轮用户回答后、生成岗位卡前、用户提出修改意见后。
### 本期不做
${scopeMd}
`;
}
function updateDownload() {
if (!state.card) return;
const blob = new Blob([state.card], { type: "text/markdown;charset=utf-8" });
const url = URL.createObjectURL(blob);
const now = new Date();
const stamp = now.toISOString().slice(0, 19).replace("T", "_").replace(/:/g, "-");
download.href = url;
download.download = `${stamp}.md`;
download.style.display = "inline-flex";
}
function handleSend() {
const value = input.value.trim();
if (!value) return;
addMessage("user", value);
input.value = "";
if (state.done) {
if (["OK", "可以了", "没有", "没了", "定稿"].includes(value.toUpperCase()) || ["可以了", "没有", "没了", "定稿"].includes(value)) {
addMessage("assistant", "好的,这份 Agent 岗位卡就定稿。");
return;
}
state.card = value;
updateDownload();
addMessage("assistant", "我已按你的反馈更新岗位卡,并重新生成了可下载文件。这份岗位卡有哪里需要调整吗?");
return;
}
const question = questions[state.step];
const key = question[0];
state.answers[key] = value;
const summary = summarize(key, value);
if (value.length < 4) {
addMessage("assistant", `我先归纳为:${summary}\n\n这个回答还比较短,可以再具体一点吗?`);
return;
}
state.step += 1;
renderSteps();
if (state.step < questions.length) {
const next = questions[state.step];
addMessage("assistant", `归纳确认:${summary}\n\n${next[1]}\n\n${next[2]}`);
return;
}
state.done = true;
state.card = buildCard();
renderSteps();
updateDownload();
addMessage("assistant", `归纳确认:${summary}\n\n${state.card}\n\n这份岗位卡有哪里需要调整吗?`);
}
function boot() {
chat.innerHTML = "";
state.step = 0;
state.answers = {};
state.done = false;
state.card = "";
download.style.display = "none";
download.removeAttribute("href");
renderSteps();
addMessage("assistant", `${questions[0][1]}\n\n${questions[0][2]}`);
}
send.addEventListener("click", handleSend);
input.addEventListener("keydown", (event) => {
if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) handleSend();
});
reset.addEventListener("click", boot);
boot();
</script>
</body>
</html>