aitable-workflow-cli 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -6
- package/dist/{chunk-IGB6WDLR.js → chunk-UNPV6DTX.js} +1 -1
- package/dist/cli.js +47 -47
- package/dist/config-set-FBOQBRSJ.js +13 -0
- package/dist/{config-ui-server-CORDAOYI.js → config-ui-server-IUSACAOT.js} +1 -1
- package/package.json +3 -3
- package/dist/config-set-EGLLIMVJ.js +0 -13
- package/templates/support-qa/.env.example +0 -16
- package/templates/support-qa/aitable-workflow.config.yml +0 -46
- package/templates/support-qa/coding-agents.config.json +0 -18
- package/templates/support-qa/lib/__tests__/declarative-matcher.test.ts +0 -240
- package/templates/support-qa/lib/__tests__/dingtalk-group-membership.test.ts +0 -144
- package/templates/support-qa/lib/__tests__/memory-events.test.ts +0 -90
- package/templates/support-qa/lib/__tests__/sandbox-agent.test.ts +0 -382
- package/templates/support-qa/lib/__tests__/thread-adapter.test.ts +0 -242
- package/templates/support-qa/lib/__tests__/thread-observe-step.test.ts +0 -391
- package/templates/support-qa/lib/__tests__/thread-observer-handler.test.ts +0 -274
- package/templates/support-qa/lib/action-dispatch.ts +0 -262
- package/templates/support-qa/lib/contract.ts +0 -253
- package/templates/support-qa/lib/declarative-matcher.ts +0 -398
- package/templates/support-qa/lib/dingtalk-group-membership.ts +0 -158
- package/templates/support-qa/lib/guards.ts +0 -18
- package/templates/support-qa/lib/memory-events.ts +0 -113
- package/templates/support-qa/lib/owner-resolver.ts +0 -94
- package/templates/support-qa/lib/preflight.ts +0 -294
- package/templates/support-qa/lib/profile.ts +0 -51
- package/templates/support-qa/lib/sandbox-agent.ts +0 -653
- package/templates/support-qa/lib/thread-adapter.ts +0 -281
- package/templates/support-qa/lib/thread-digest.ts +0 -144
- package/templates/support-qa/npmrc +0 -1
- package/templates/support-qa/package.json +0 -27
- package/templates/support-qa/profiles/aitable/profile/escalation/contacts.json +0 -37
- package/templates/support-qa/profiles/aitable/profile/escalation/owner-map.json +0 -18
- package/templates/support-qa/profiles/aitable/profile/fastpath/known-answers.yml +0 -561
- package/templates/support-qa/profiles/aitable/profile/profile.yml +0 -219
- package/templates/support-qa/profiles/aitable/profile/prompts/kb-editor.md +0 -3
- package/templates/support-qa/profiles/aitable/profile/prompts/persona.md +0 -49
- package/templates/support-qa/profiles/aitable/profile/prompts/troubleshooter.md +0 -5
- package/templates/support-qa/profiles/aitable/profile/troubleshoot/classifiers.yml +0 -36
- package/templates/support-qa/profiles/aitable/skills/troubleshooting/SKILL.md +0 -58
- package/templates/support-qa/profiles/aitable/skills/troubleshooting/aitable-import-troubleshooter/SKILL.md +0 -485
- package/templates/support-qa/profiles/aitable/skills/troubleshooting/aitable-openapi-troubleshooter/SKILL.md +0 -183
- package/templates/support-qa/profiles/aitable/skills/troubleshooting/import-openapi-e2e-troubleshooter/SKILL.md +0 -459
- package/templates/support-qa/profiles/aitable/skills/troubleshooting/notable-datasource-troubleshooter/SKILL.md +0 -1286
- package/templates/support-qa/profiles/aitable/skills/troubleshooting/notable-field-troubleshooter/SKILL.md +0 -673
- package/templates/support-qa/profiles/aitable/skills/troubleshooting/spreadsheet-datasync-troubleshooter/SKILL.md +0 -322
- package/templates/support-qa/profiles/default/profile/fastpath/known-answers.yml +0 -32
- package/templates/support-qa/profiles/default/profile/profile.yml +0 -217
- package/templates/support-qa/profiles/default/profile/troubleshoot/classifiers.yml +0 -37
- package/templates/support-qa/profiles/default/wiki/support//345/270/270/350/247/201/351/227/256/351/242/230.md +0 -10
- package/templates/support-qa/tsconfig.json +0 -16
- package/templates/support-qa/vitest.config.ts +0 -9
- package/templates/support-qa/workflows/admin-kb-update/recipes/kb-refine.recipe.ts +0 -137
- package/templates/support-qa/workflows/admin-kb-update/recipes/kb_sync_handler.recipe.ts +0 -119
- package/templates/support-qa/workflows/admin-kb-update/workflow.yml +0 -96
- package/templates/support-qa/workflows/config-sync/recipes/config_sync.recipe.ts +0 -668
- package/templates/support-qa/workflows/config-sync/recipes/config_sync_handler.recipe.ts +0 -16
- package/templates/support-qa/workflows/config-sync/workflow.yml +0 -62
- package/templates/support-qa/workflows/daily-review/recipes/daily_review.recipe.ts +0 -602
- package/templates/support-qa/workflows/daily-review/recipes/daily_review_handler.recipe.ts +0 -26
- package/templates/support-qa/workflows/daily-review/workflow.yml +0 -65
- package/templates/support-qa/workflows/group-qa/recipes/answer.recipe.ts +0 -231
- package/templates/support-qa/workflows/group-qa/recipes/conversation_handler.recipe.ts +0 -394
- package/templates/support-qa/workflows/group-qa/recipes/fastpath.recipe.ts +0 -72
- package/templates/support-qa/workflows/group-qa/recipes/on-action.recipe.ts +0 -225
- package/templates/support-qa/workflows/group-qa/recipes/reply-to-group.recipe.ts +0 -202
- package/templates/support-qa/workflows/group-qa/recipes/troubleshoot.recipe.ts +0 -273
- package/templates/support-qa/workflows/group-qa/workflow.yml +0 -268
- package/templates/support-qa/workflows/thread-observer/README.md +0 -96
- package/templates/support-qa/workflows/thread-observer/recipes/thread_observe.recipe.ts +0 -415
- package/templates/support-qa/workflows/thread-observer/recipes/thread_observer_handler.recipe.ts +0 -213
- package/templates/support-qa/workflows/thread-observer/workflow.yml +0 -185
- package/templates/support-qa/workspaces/daily-review/AGENTS.md +0 -233
- package/templates/support-qa/workspaces/kb-editor/AGENTS.md +0 -32
- package/templates/support-qa/workspaces/support/AGENTS.md +0 -156
- package/templates/support-qa/workspaces/thread-observer/AGENTS.md +0 -156
- package/templates/support-qa/workspaces/troubleshooter/AGENTS.md +0 -71
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { EventHandlerRecipeDefinition, HandlerInitContext, HandlerInstance } from "aitable-workflow-core";
|
|
2
|
-
|
|
3
|
-
/** cron tick → 建一条每日复盘工单(实际复盘逻辑在 daily_review step recipe) */
|
|
4
|
-
const recipe: EventHandlerRecipeDefinition = {
|
|
5
|
-
meta: { id: "daily_review_handler", name: "每日复盘触发器", description: "cron tick → 建一条每日复盘工单" },
|
|
6
|
-
create(ctx: HandlerInitContext): HandlerInstance {
|
|
7
|
-
return {
|
|
8
|
-
onEvent(event) {
|
|
9
|
-
const firedAt = String(event.payload?.firedAt ?? new Date().toISOString());
|
|
10
|
-
// cron 事件携带调度时区与预定时刻;按时区取运行日期,避免 UTC 运行环境下标题日期错位
|
|
11
|
-
const timezone = typeof event.payload?.timezone === "string" ? (event.payload.timezone as string) : "Asia/Shanghai";
|
|
12
|
-
const scheduledFor = String(event.payload?.scheduledFor ?? firedAt);
|
|
13
|
-
let dateStr: string;
|
|
14
|
-
try {
|
|
15
|
-
dateStr = new Intl.DateTimeFormat("en-CA", { timeZone: timezone, year: "numeric", month: "2-digit", day: "2-digit" }).format(new Date(scheduledFor));
|
|
16
|
-
} catch {
|
|
17
|
-
dateStr = scheduledFor.slice(0, 10); // 时区非法时回退
|
|
18
|
-
}
|
|
19
|
-
ctx.log(`每日复盘触发 @ ${firedAt} (tz=${timezone}, 运行日=${dateStr})`);
|
|
20
|
-
ctx.pushEvent({ action: "create_record", fields: { "标题": `每日复盘 ${dateStr}` } });
|
|
21
|
-
},
|
|
22
|
-
stop() {},
|
|
23
|
-
};
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
export default recipe;
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
meta:
|
|
2
|
-
name: "每日复盘"
|
|
3
|
-
version: "1.0.0"
|
|
4
|
-
domain: daily_review
|
|
5
|
-
description: "每日凌晨读取各群昨日短期记忆,coding agent 压缩/统计/需求Bug 抽取,写长期记忆+每日统计表+产品需求池;有活动才留痕(不发群)"
|
|
6
|
-
|
|
7
|
-
notification:
|
|
8
|
-
channel: "none"
|
|
9
|
-
|
|
10
|
-
# ─── 复盘留痕表(每个有活动的复盘日留一行运行记录;table_id 留空 setup 自动建表回写)──
|
|
11
|
-
# ⚠️ 占位:部署前替换为你自己的 base_id/table_id;不可与 group-qa 同表
|
|
12
|
-
tracker:
|
|
13
|
-
label: "每日复盘留痕表"
|
|
14
|
-
description: "记录每日复盘运行:日期/处理群数/需求Bug数/摘要"
|
|
15
|
-
base_id: ""
|
|
16
|
-
table_id: ""
|
|
17
|
-
states:
|
|
18
|
-
initial: "待处理"
|
|
19
|
-
active: [ "处理中" ]
|
|
20
|
-
terminal: [ "已完成", "已关闭" ]
|
|
21
|
-
blocked: "已阻塞"
|
|
22
|
-
fields:
|
|
23
|
-
state: "状态"
|
|
24
|
-
title: "标题"
|
|
25
|
-
current_step: "当前步骤"
|
|
26
|
-
event_log: "事件日志"
|
|
27
|
-
field_definitions:
|
|
28
|
-
- name: 复盘日期
|
|
29
|
-
type: text
|
|
30
|
-
- name: 处理群数
|
|
31
|
-
type: text
|
|
32
|
-
- name: 需求Bug数
|
|
33
|
-
type: text
|
|
34
|
-
- name: 摘要
|
|
35
|
-
type: text
|
|
36
|
-
views:
|
|
37
|
-
- id: default_grid
|
|
38
|
-
type: Grid
|
|
39
|
-
name: 表格
|
|
40
|
-
field_order: [ 标题, 复盘日期, 处理群数, 需求Bug数, 摘要, 状态, 当前步骤, 事件日志 ]
|
|
41
|
-
description: 每日复盘留痕
|
|
42
|
-
|
|
43
|
-
# ─── 事件源:cron 每日触发,on_demand(无活动不建行)──
|
|
44
|
-
event_sources:
|
|
45
|
-
- name: 每日复盘调度
|
|
46
|
-
type: cron_scheduler
|
|
47
|
-
handler: daily_review_handler
|
|
48
|
-
record_policy: on_demand
|
|
49
|
-
|
|
50
|
-
steps:
|
|
51
|
-
- id: daily_review
|
|
52
|
-
name: "每日复盘"
|
|
53
|
-
recipe: daily_review
|
|
54
|
-
auto: true
|
|
55
|
-
output_mapping:
|
|
56
|
-
review_date: "复盘日期"
|
|
57
|
-
group_count: "处理群数"
|
|
58
|
-
needs_bug_count: "需求Bug数"
|
|
59
|
-
summary: "摘要"
|
|
60
|
-
|
|
61
|
-
edges:
|
|
62
|
-
- from: __start__
|
|
63
|
-
to: daily_review
|
|
64
|
-
- from: daily_review
|
|
65
|
-
to: __end__
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import type { RecipeDefinition, RecipeContext, RecipeResult } from "aitable-workflow-core";
|
|
5
|
-
import { contractPromptBlock, extractSupportReply, type SupportReply } from "../../../lib/contract.js";
|
|
6
|
-
import { runSandboxAgent } from "../../../lib/sandbox-agent.js";
|
|
7
|
-
import { SUPPORT_DECISION_CONTENT, SUPPORT_MEMORY_KINDS, SUPPORT_MEMORY_ROLES, questionKey, supportEntry, supportScope } from "../../../lib/memory-events.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 单 agent 应答(support-qa L0)
|
|
11
|
-
*
|
|
12
|
-
* 一次 sandbox-agent.run 让 Aone Sandbox 内的 QoderRemoteAgent 依据知识库 +
|
|
13
|
-
* 近期对话上下文,按契约返回严格 JSON:
|
|
14
|
-
* { action, reason, reply?, module? }(契约单一事实源见 lib/contract.ts)
|
|
15
|
-
*
|
|
16
|
-
* 拍平 action 契约(spec 2026-07-28):
|
|
17
|
-
* 1. 单一 action 取代 shouldReply + silenceCode 的组合歧义(Bug/Feature 不再坠入 kb_gap);
|
|
18
|
-
* 2. agent 失败/解析失败先重试 1 次,仍失败 → action=agent_failure 且写短期记忆
|
|
19
|
-
* (故障与"判定型静默"分开审计,绝不混入 kb_gap 知识待办);
|
|
20
|
-
* 3. 追问补位闸门参数(profile gates.standby)动态注入 prompt;
|
|
21
|
-
* 4. 本步对静默类 action 一律 tracker:false —— 留痕与否由下游 on_action 按 profile 裁决。
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const MAX_AGENT_ATTEMPTS = 2; // 1 次正常 + 1 次重试
|
|
25
|
-
|
|
26
|
-
async function runAgentOnce(
|
|
27
|
-
ctx: RecipeContext,
|
|
28
|
-
prompt: string,
|
|
29
|
-
workDir: string,
|
|
30
|
-
attempt: number,
|
|
31
|
-
): Promise<SupportReply | null> {
|
|
32
|
-
try {
|
|
33
|
-
const result = await runSandboxAgent(prompt, workDir, ctx.log);
|
|
34
|
-
if (!result.response) return null;
|
|
35
|
-
const decision = extractSupportReply(result.response);
|
|
36
|
-
if (!decision) {
|
|
37
|
-
ctx.log(
|
|
38
|
-
`agent 第 ${attempt} 次输出不符合契约 sandboxId=${result.sandboxId} sessionId=${result.sessionId} responseLength=${result.response.length}: ${JSON.stringify(result.response)}`,
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
return decision;
|
|
42
|
-
} catch (error) {
|
|
43
|
-
ctx.log(`agent 第 ${attempt} 次调用失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const recipe: RecipeDefinition = {
|
|
49
|
-
meta: {
|
|
50
|
-
id: "answer",
|
|
51
|
-
name: "单 agent 应答",
|
|
52
|
-
role: "support",
|
|
53
|
-
description: "一次 agent 调用产出契约 JSON(action/reason/reply/module)",
|
|
54
|
-
version: "1.0.0",
|
|
55
|
-
requiresAgent: true,
|
|
56
|
-
timeoutMs: 420_000,
|
|
57
|
-
config: z.object({
|
|
58
|
-
standby: z.object({
|
|
59
|
-
enabled: z.boolean().describe("是否启用追问补位"),
|
|
60
|
-
wait_minutes: z.number().describe("等待时间(分钟)"),
|
|
61
|
-
}).optional().describe("追问补位(L3):用户 @真人后超时无人回且用户追问时放行"),
|
|
62
|
-
}),
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
async execute(ctx: RecipeContext): Promise<RecipeResult> {
|
|
66
|
-
const { input, toolkit, log } = ctx;
|
|
67
|
-
const content = String(input.content ?? "");
|
|
68
|
-
const context = String(input.context ?? "").trim();
|
|
69
|
-
const senderName = String(input.senderName ?? "").trim();
|
|
70
|
-
const priorAction = String(input.action ?? "").trim();
|
|
71
|
-
const conversationId = String(input.conversationId ?? "").trim();
|
|
72
|
-
|
|
73
|
-
// 上游 Recipe(fastpath/troubleshoot 等 L3 包)已产出 action → 透传
|
|
74
|
-
if (priorAction) {
|
|
75
|
-
log(`已由上游 Recipe 处理(action=${priorAction}),answer 跳过`);
|
|
76
|
-
return { status: "success", output: {}, tracker: false, logs: [`透传:${priorAction}`] };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!content.trim()) {
|
|
80
|
-
return {
|
|
81
|
-
status: "success",
|
|
82
|
-
output: { action: "no_question", module: "", answer: "", reason: "空消息" },
|
|
83
|
-
tracker: false,
|
|
84
|
-
logs: ["空消息 → 静默"],
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// profile 校验已前移到启动期(core preflight 扩展点加载 lib/preflight.ts):
|
|
89
|
-
// 配置错在 `aitable-workflow run` 启动瞬间就报,不再每条消息判一次再降级静默。
|
|
90
|
-
const roleId = ctx.roleContext?.id?.trim() || "support";
|
|
91
|
-
const workDir = path.resolve(ctx.configDir, "workspaces", roleId);
|
|
92
|
-
|
|
93
|
-
// ─── 长期记忆注入(写入工作目录供 agent 按需 Read/Grep,仅辅助上下文) ───
|
|
94
|
-
let memSection = "";
|
|
95
|
-
try {
|
|
96
|
-
const longTerm = await toolkit.memory.readLongTerm(supportScope()).catch(() => "");
|
|
97
|
-
if (longTerm && longTerm.trim()) {
|
|
98
|
-
fs.mkdirSync(workDir, { recursive: true });
|
|
99
|
-
fs.writeFileSync(path.join(workDir, ".long-term.md"), longTerm, "utf8");
|
|
100
|
-
memSection = `\n## 长期记忆(可用,仅作辅助上下文,不作事实依据)
|
|
101
|
-
- 近期群聊复盘沉淀的高频问题与趋势已写入工作目录下的 .long-term.md,可按需 Read/Grep 搜索以理解语境;一切事实依据仍只能来自知识库,不得据此作答。\n`;
|
|
102
|
-
log(`已写入长期记忆到 ${path.join(workDir, ".long-term.md")} (${longTerm.length} 字符)`);
|
|
103
|
-
}
|
|
104
|
-
} catch (err) {
|
|
105
|
-
log(`写入长期记忆失败(不影响主流程): ${err}`);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ─── 追问补位闸门参数(profile 驱动,动态注入;AGENTS.md 含规则正文) ───
|
|
109
|
-
const sbCfg = ctx.config.standby as { enabled?: boolean; wait_minutes?: number } | undefined;
|
|
110
|
-
const standby = { enabled: sbCfg?.enabled ?? false, waitMinutes: sbCfg?.wait_minutes ?? 30 };
|
|
111
|
-
const standbyNote = standby.enabled
|
|
112
|
-
? `\n**追问补位参数**:本实例已启用"@真人未响应补位"(闸门规则 7):用户 @过真人负责人、且距该消息已超过 ${standby.waitMinutes} 分钟(依据对话历史 [hh:mm] 时间戳判断)、真人未回复、当前消息是用户对同一问题的追问 → 允许回答,reply 开头用一句补位语气(如"看到相关同学暂时没回,先提供帮助文档里的信息:")。\n`
|
|
113
|
-
: "\n**追问补位参数**:本实例未启用补位(闸门规则 7 不适用,用户 @真人后一律礼让静默)。\n";
|
|
114
|
-
|
|
115
|
-
const hasImage = /!\[图片\]\([^)]+\)/.test(content);
|
|
116
|
-
const imageNote = hasImage
|
|
117
|
-
? "\n**本条消息包含图片**(已内联为 ``):请先用 Read 打开该图片,结合图片内容(多为报错截图/操作截图)理解用户的真实问题后再作答。\n"
|
|
118
|
-
: "";
|
|
119
|
-
|
|
120
|
-
// 前置拦截(非法 module 根治):把 owner-map 的合法模块清单直接写进契约 prompt,
|
|
121
|
-
// 解析后再做出口校验——清单外的 module 在 producer 就地置空,不流入 on_action。
|
|
122
|
-
let moduleKeys: string[] = [];
|
|
123
|
-
try {
|
|
124
|
-
const ownerMap = JSON.parse(fs.readFileSync(path.join(workDir, "owner-map.json"), "utf8")) as Record<string, unknown>;
|
|
125
|
-
if (ownerMap && typeof ownerMap === "object" && !Array.isArray(ownerMap)) moduleKeys = Object.keys(ownerMap);
|
|
126
|
-
} catch {
|
|
127
|
-
// owner-map 缺失(无转人工配置)→ 退回通用契约描述,不阻断主流程
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const assistantName = ctx.roleContext?.name ?? "答疑助手";
|
|
131
|
-
const prompt = `开始处理前,必须先使用 Read 读取并完整遵循本工作目录下的 AGENTS.md,将其作为本次答疑的完整业务执行规范。
|
|
132
|
-
|
|
133
|
-
你是「${assistantName}」。用户在钉钉群中发出了以下消息,请按 AGENTS.md 的闸门规则先判定是否需要回复,再基于知识库为用户提供准确解答。
|
|
134
|
-
|
|
135
|
-
在对话历史中,标记为"(答疑机器人/即你自己之前的回复)"的消息就是你之前发出的回复;标记为"(真人同事)"的是真人值班同事。
|
|
136
|
-
${memSection}${standbyNote}
|
|
137
|
-
${contractPromptBlock(moduleKeys)}
|
|
138
|
-
|
|
139
|
-
示例(仅用于说明格式,内容请按用户的真实问题作答):
|
|
140
|
-
{"action":"reply","reason":"知识库有据","reply":"Hi 小明,单表上限 20 万行。\\n\\n> 来源:https://example.com/xxx"}
|
|
141
|
-
{"action":"no_question","reason":"用户表示感谢,非有效提问"}
|
|
142
|
-
{"action":"kb_gap","reason":"普通资源咨询,知识库无据"}
|
|
143
|
-
{"action":"bug","module":"数据源","reason":"确认是异常但文档无明确结论","reply":"Hi 小明,当前能确认的是……;你描述的现象还需要进一步排查。"}
|
|
144
|
-
|
|
145
|
-
要求:
|
|
146
|
-
1. reply 开头先用「Hi ${senderName || "用户"},」打招呼,然后直接给出解答内容,不要寒暄复述。
|
|
147
|
-
2. 引用出处按 AGENTS.md 的来源规则附在 reply 末尾。
|
|
148
|
-
${context ? `\n${context}\n` : ""}
|
|
149
|
-
${imageNote}用户消息(发送者:${senderName || "未知"}):
|
|
150
|
-
${content}`;
|
|
151
|
-
|
|
152
|
-
log(`answer prompt 就绪(workDir=${workDir}): ${content.slice(0, 60)}`);
|
|
153
|
-
|
|
154
|
-
// ─── 执行(失败重试 1 次;G9) ───
|
|
155
|
-
let decision: SupportReply | null = null;
|
|
156
|
-
for (let attempt = 1; attempt <= MAX_AGENT_ATTEMPTS && !decision; attempt++) {
|
|
157
|
-
if (attempt > 1) log(`agent 无有效响应/解析失败,重试第 ${attempt - 1} 次`);
|
|
158
|
-
decision = await runAgentOnce(ctx, prompt, workDir, attempt);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (!decision) {
|
|
162
|
-
log("agent 重试后仍失败 → agent_failure 兜底静默(写记忆,交 on_action 处置)");
|
|
163
|
-
if (conversationId) {
|
|
164
|
-
void toolkit.memory.append(
|
|
165
|
-
supportScope(conversationId),
|
|
166
|
-
supportEntry({
|
|
167
|
-
kind: SUPPORT_MEMORY_KINDS.agentFailure,
|
|
168
|
-
content: content.slice(0, 200),
|
|
169
|
-
sender: "agent",
|
|
170
|
-
role: SUPPORT_MEMORY_ROLES.agent,
|
|
171
|
-
conversationId,
|
|
172
|
-
summary: `agent 失败兜底静默: ${content.slice(0, 80)}`,
|
|
173
|
-
meta: { action: "agent_failure", attempts: MAX_AGENT_ATTEMPTS, question: questionKey(content) },
|
|
174
|
-
}),
|
|
175
|
-
).catch((err) => log(`memory.append(agent_failure) 失败(不影响主流程): ${err}`));
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
status: "success",
|
|
179
|
-
output: { action: "agent_failure", module: "", answer: "", reason: "agent 重试后仍无有效响应" },
|
|
180
|
-
tracker: false, // 留痕与否由 on_action 按 profile 裁决
|
|
181
|
-
logs: ["agent_failure → 交 on_action 处置"],
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const action = decision.action;
|
|
186
|
-
const isReplyBearing = action === "reply" || action === "bug" || action === "feature";
|
|
187
|
-
const reason = String(decision.reason ?? "");
|
|
188
|
-
const reply = String(decision.reply ?? "");
|
|
189
|
-
let module = String(decision.module ?? "");
|
|
190
|
-
// 出口校验:清单外的 module 视同未给出(on_action 会走降级发送,不会拿非法 key 去查 Owner)
|
|
191
|
-
if (module && moduleKeys.length > 0 && !moduleKeys.includes(module)) {
|
|
192
|
-
log(`invalid_module_stripped: 「${module}」不在 owner-map 清单中,已置空`);
|
|
193
|
-
module = "";
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const output: Record<string, unknown> = {
|
|
197
|
-
action,
|
|
198
|
-
module,
|
|
199
|
-
reason,
|
|
200
|
-
answer: isReplyBearing ? reply : "",
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// 决策写短期记忆(发送事实由 reply-to-group 在发送成功后另记)
|
|
204
|
-
if (conversationId) {
|
|
205
|
-
void toolkit.memory.append(
|
|
206
|
-
supportScope(conversationId),
|
|
207
|
-
supportEntry({
|
|
208
|
-
kind: SUPPORT_MEMORY_KINDS.agentDecision,
|
|
209
|
-
// 正文只记占位:话术真相在发送事实(agent_reply_sent/owner_escalation_sent),不在决策里存半成品副本
|
|
210
|
-
content: isReplyBearing ? SUPPORT_DECISION_CONTENT.reply : SUPPORT_DECISION_CONTENT.silent,
|
|
211
|
-
sender: "agent",
|
|
212
|
-
role: SUPPORT_MEMORY_ROLES.agent,
|
|
213
|
-
conversationId,
|
|
214
|
-
summary: `答疑决策: ${action}${module ? `[${module}]` : ""}${reason ? ` - ${reason.slice(0, 80)}` : ""}`,
|
|
215
|
-
meta: { action, module, reason, question: questionKey(content) },
|
|
216
|
-
}),
|
|
217
|
-
).catch((err) => log(`memory.append(agent_decision) 失败(不影响主流程): ${err}`));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
log(`决策: ${action}${module ? `[${module}]` : ""}${reason ? `(${reason})` : ""}`);
|
|
221
|
-
return {
|
|
222
|
-
status: "success",
|
|
223
|
-
output,
|
|
224
|
-
// reply/bug/feature → 建行留痕;静默类 → 本步不表态(tracker:false),由 on_action 按 profile 裁决
|
|
225
|
-
...(isReplyBearing ? {} : { tracker: false }),
|
|
226
|
-
logs: [`决策 ${action}`, reason].filter(Boolean),
|
|
227
|
-
};
|
|
228
|
-
},
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
export default recipe;
|