@andreprado/agentkit 0.1.0-alpha.5 → 0.1.0-alpha.6
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 +5 -0
- package/docs/guides/add-channel.md +25 -0
- package/docs/guides/add-knowledge.md +134 -0
- package/docs/guides/agentkit-skills-architecture.md +471 -0
- package/docs/guides/channels-production-handoff.md +2 -0
- package/docs/guides/connect-telegram.md +15 -0
- package/docs/guides/connect-whatsapp-zapster.md +16 -0
- package/docs/guides/create-agent.md +1 -1
- package/docs/llms-full.txt +90 -1
- package/docs/llms.txt +9 -2
- package/package.json +2 -1
- package/src/cli/commands/channels.ts +39 -1
- package/src/cli/commands/knowledge.ts +136 -0
- package/src/cli/deploy-readiness.ts +19 -0
- package/src/cli/help.ts +8 -0
- package/src/cli/index.ts +16 -4
- package/src/cloud/artifact.ts +92 -1
- package/src/cloud/contracts.ts +16 -0
- package/src/create-project.ts +38 -6
- package/src/index.ts +98 -0
- package/src/runtime/channel-buffer.ts +30 -0
- package/src/runtime/channels.ts +1 -0
- package/src/runtime/chat.ts +21 -2
- package/src/runtime/config.ts +167 -0
- package/src/runtime/core/manifest.ts +37 -0
- package/src/runtime/deploy-readiness.ts +12 -0
- package/src/runtime/dev-server.ts +159 -10
- package/src/runtime/inspect.ts +34 -0
- package/src/runtime/knowledge/chunk.ts +333 -0
- package/src/runtime/knowledge/config.ts +135 -0
- package/src/runtime/knowledge/embeddings.ts +133 -0
- package/src/runtime/knowledge/ingest.ts +521 -0
- package/src/runtime/knowledge/prompt-policy.ts +30 -0
- package/src/runtime/knowledge/retrieve.ts +283 -0
- package/src/runtime/knowledge/schema.ts +56 -0
- package/src/runtime/knowledge/tool.ts +64 -0
- package/src/runtime/knowledge/vector.ts +258 -0
- package/src/runtime/targets/cloudflare/build.ts +469 -4
- package/src/storage/sqlite.ts +5 -0
- package/src/templates/blank.ts +8 -4
- package/src/templates/dentista.ts +3 -1
- package/src/templates/skills/agentkit-build-agent/SKILL.md +49 -0
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -0
- package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
- package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
- package/src/templates/skills/agentkit-capsule/SKILL.md +62 -0
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +37 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +37 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +37 -0
- package/src/templates/skills/agentkit-database/SKILL.md +42 -0
- package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
- package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
- package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +31 -0
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
- package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -0
- package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
- package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
- package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
- package/src/templates/skills/agentkit-prompts/SKILL.md +45 -0
- package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
- package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
- package/src/templates/skills/agentkit-security/SKILL.md +55 -0
- package/src/templates/skills/agentkit-tools/SKILL.md +36 -0
- package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
- package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
- package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +52 -0
- package/src/templates/support.ts +8 -4
|
@@ -8,6 +8,8 @@ import { build as esbuild, type Plugin } from "esbuild";
|
|
|
8
8
|
import { loadAgentCapsule, type LoadedAgentCapsule } from "../../config";
|
|
9
9
|
import { buildSharedAgentManifest, type ManifestSchema, type SharedAgentManifest } from "../../core/manifest";
|
|
10
10
|
import { AgentKitError } from "../../errors";
|
|
11
|
+
import { resolveKnowledgeConfig } from "../../knowledge/config";
|
|
12
|
+
import { KNOWLEDGE_SCHEMA_SQL } from "../../knowledge/schema";
|
|
11
13
|
|
|
12
14
|
export type BuildTarget = "cloudflare";
|
|
13
15
|
|
|
@@ -34,6 +36,7 @@ export type AgentManifest = {
|
|
|
34
36
|
secrets: SharedAgentManifest["secrets"];
|
|
35
37
|
tools: SharedAgentManifest["tools"];
|
|
36
38
|
channels: SharedAgentManifest["channels"];
|
|
39
|
+
knowledge: SharedAgentManifest["knowledge"];
|
|
37
40
|
access: SharedAgentManifest["access"];
|
|
38
41
|
storage: {
|
|
39
42
|
driver: "agentkit";
|
|
@@ -145,6 +148,14 @@ function validateCloudflareBuild(capsule: LoadedAgentCapsule): void {
|
|
|
145
148
|
);
|
|
146
149
|
}
|
|
147
150
|
|
|
151
|
+
const knowledge = resolveKnowledgeConfig(capsule.config);
|
|
152
|
+
|
|
153
|
+
if (knowledge.enabled && capsule.config.storage.database?.driver !== "turso") {
|
|
154
|
+
throw new AgentKitError(
|
|
155
|
+
"build_knowledge_database",
|
|
156
|
+
'Cloudflare Knowledge deploy requires storage.database.driver: "turso" so chunks and retrieval state are available to the hosted runtime.',
|
|
157
|
+
);
|
|
158
|
+
}
|
|
148
159
|
}
|
|
149
160
|
|
|
150
161
|
function buildWarnings(capsule: LoadedAgentCapsule): string[] {
|
|
@@ -185,6 +196,7 @@ function buildManifest(
|
|
|
185
196
|
secrets: shared.secrets,
|
|
186
197
|
tools: shared.tools,
|
|
187
198
|
channels: shared.channels,
|
|
199
|
+
knowledge: shared.knowledge,
|
|
188
200
|
access: shared.access,
|
|
189
201
|
storage: {
|
|
190
202
|
driver: "agentkit",
|
|
@@ -210,12 +222,26 @@ function buildCloudflareFilesManifest(files: SharedAgentManifest["storage"]["fil
|
|
|
210
222
|
|
|
211
223
|
async function loadTursoSchema(capsule: LoadedAgentCapsule): Promise<ManifestSchema | null> {
|
|
212
224
|
const database = capsule.config.storage.database;
|
|
225
|
+
const knowledge = resolveKnowledgeConfig(capsule.config);
|
|
226
|
+
const sources: string[] = [];
|
|
227
|
+
|
|
228
|
+
if (!database || database.driver !== "turso") {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (database.schema) {
|
|
233
|
+
sources.push(await readFile(resolve(capsule.root, database.schema), "utf8"));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (knowledge.enabled) {
|
|
237
|
+
sources.push(KNOWLEDGE_SCHEMA_SQL);
|
|
238
|
+
}
|
|
213
239
|
|
|
214
|
-
if (
|
|
240
|
+
if (sources.length === 0) {
|
|
215
241
|
return null;
|
|
216
242
|
}
|
|
217
243
|
|
|
218
|
-
const source =
|
|
244
|
+
const source = sources.map((part) => part.trim()).filter(Boolean).join("\n\n") + "\n";
|
|
219
245
|
const digest = sha256(source);
|
|
220
246
|
return { source, sha256: digest };
|
|
221
247
|
}
|
|
@@ -361,6 +387,7 @@ export function websiteChannel(input) {
|
|
|
361
387
|
access: input.access ?? { mode: "token" },
|
|
362
388
|
secrets: normalizeChannelSecrets(input.secrets ?? ["AGENTKIT_WEBSITE_CHANNEL_TOKEN"], "website channel"),
|
|
363
389
|
...(input.limits ? { limits: input.limits } : {}),
|
|
390
|
+
...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "website channel") } : {}),
|
|
364
391
|
};
|
|
365
392
|
}
|
|
366
393
|
|
|
@@ -372,6 +399,7 @@ export function telegramChannel(input) {
|
|
|
372
399
|
secrets: normalizeChannelSecrets(input.secrets ?? ["TELEGRAM_BOT_TOKEN", "TELEGRAM_WEBHOOK_SECRET"], "telegram channel"),
|
|
373
400
|
allowedUpdates: input.allowedUpdates ?? ["message"],
|
|
374
401
|
...(input.limits ? { limits: input.limits } : {}),
|
|
402
|
+
...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, "telegram channel") } : {}),
|
|
375
403
|
};
|
|
376
404
|
}
|
|
377
405
|
|
|
@@ -382,6 +410,7 @@ export function whatsappChannel(input) {
|
|
|
382
410
|
name: input.name,
|
|
383
411
|
secrets: normalizeChannelSecrets(input.secrets ?? defaultWhatsappChannelSecrets(input.provider), \`\${input.provider} whatsapp channel\`),
|
|
384
412
|
...(input.limits ? { limits: input.limits } : {}),
|
|
413
|
+
...(input.buffer ? { buffer: normalizeChannelBuffer(input.buffer, \`\${input.provider} whatsapp channel\`) } : {}),
|
|
385
414
|
};
|
|
386
415
|
}
|
|
387
416
|
|
|
@@ -406,6 +435,36 @@ function normalizeChannelSecrets(secrets, context) {
|
|
|
406
435
|
|
|
407
436
|
return Array.from(uniqueSecrets).sort();
|
|
408
437
|
}
|
|
438
|
+
|
|
439
|
+
function normalizeChannelBuffer(input, context) {
|
|
440
|
+
if (input.mode === "off") {
|
|
441
|
+
return { mode: "off" };
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (input.mode !== undefined && input.mode !== "debounce") {
|
|
445
|
+
throw new Error(\`\${context} buffer.mode must be "off" or "debounce".\`);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const buffer = {
|
|
449
|
+
mode: "debounce",
|
|
450
|
+
quietWindowMs: input.quietWindowMs ?? 2500,
|
|
451
|
+
maxWaitMs: input.maxWaitMs ?? 12000,
|
|
452
|
+
maxMessages: input.maxMessages ?? 20,
|
|
453
|
+
maxChars: input.maxChars ?? 8000,
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
for (const field of ["quietWindowMs", "maxWaitMs", "maxMessages", "maxChars"]) {
|
|
457
|
+
if (!Number.isInteger(buffer[field]) || buffer[field] <= 0) {
|
|
458
|
+
throw new Error(\`\${context} buffer.\${field} must be a positive integer.\`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (buffer.maxWaitMs < buffer.quietWindowMs) {
|
|
463
|
+
throw new Error(\`\${context} buffer.maxWaitMs must be greater than or equal to buffer.quietWindowMs.\`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return buffer;
|
|
467
|
+
}
|
|
409
468
|
`;
|
|
410
469
|
}
|
|
411
470
|
|
|
@@ -486,7 +545,8 @@ import { getProviderAdapter } from ${providerImportPath};
|
|
|
486
545
|
|
|
487
546
|
const manifest = ${manifestJson};
|
|
488
547
|
const instructions = ${instructionsJson};
|
|
489
|
-
const
|
|
548
|
+
const hostedKnowledgeTool = manifest.knowledge.enabled ? createHostedKnowledgeSearchTool() : null;
|
|
549
|
+
const hostedTools = hostedKnowledgeTool ? [hostedKnowledgeTool, ...(agentConfig.tools ?? [])] : (agentConfig.tools ?? []);
|
|
490
550
|
const DEFAULT_TOOL_TIMEOUT_MS = 30000;
|
|
491
551
|
|
|
492
552
|
export class AgentKitConversationStore extends DurableObject {
|
|
@@ -668,6 +728,7 @@ export default {
|
|
|
668
728
|
provider: channel.provider,
|
|
669
729
|
secrets: channel.secrets,
|
|
670
730
|
})),
|
|
731
|
+
knowledge: manifest.knowledge,
|
|
671
732
|
access: manifest.access,
|
|
672
733
|
storage: manifest.storage,
|
|
673
734
|
database: inspectHostedDatabase(),
|
|
@@ -1022,7 +1083,7 @@ async function callHostedProvider(env, messages, signal) {
|
|
|
1022
1083
|
runtime: manifest.runtime,
|
|
1023
1084
|
provider: manifest.provider,
|
|
1024
1085
|
},
|
|
1025
|
-
instructions: withToolVisibilityInstructions(instructions, hostedTools),
|
|
1086
|
+
instructions: withToolVisibilityInstructions(appendHostedKnowledgePromptPolicy(instructions), hostedTools),
|
|
1026
1087
|
messages,
|
|
1027
1088
|
tools: hostedTools,
|
|
1028
1089
|
toolRuntime: createHostedProviderToolRuntime(env, signal),
|
|
@@ -1033,6 +1094,20 @@ async function callHostedProvider(env, messages, signal) {
|
|
|
1033
1094
|
return result.message.content;
|
|
1034
1095
|
}
|
|
1035
1096
|
|
|
1097
|
+
function appendHostedKnowledgePromptPolicy(baseInstructions) {
|
|
1098
|
+
if (!manifest.knowledge.enabled) {
|
|
1099
|
+
return baseInstructions;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
return \`\${baseInstructions.trimEnd()}
|
|
1103
|
+
|
|
1104
|
+
Knowledge rules:
|
|
1105
|
+
- Use agentkit_search_knowledge before answering business-specific factual questions about the user's documents, policies, prices, availability, services, procedures, or source-specific claims.
|
|
1106
|
+
- Do not invent prices, policies, legal/clinical claims, availability, or source-specific facts when Knowledge has not returned support for the answer.
|
|
1107
|
+
- Use retrieved Knowledge as context for the final answer, but do not expose raw retrieval JSON, scores, chunk IDs, or internal tool output objects to the user.
|
|
1108
|
+
\`;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1036
1111
|
function withToolVisibilityInstructions(baseInstructions, tools) {
|
|
1037
1112
|
const internalTools = tools
|
|
1038
1113
|
.filter((tool) => tool.visibility === "internal")
|
|
@@ -1052,6 +1127,396 @@ Tool visibility rules:
|
|
|
1052
1127
|
\`;
|
|
1053
1128
|
}
|
|
1054
1129
|
|
|
1130
|
+
function createHostedKnowledgeSearchTool() {
|
|
1131
|
+
return {
|
|
1132
|
+
name: manifest.knowledge.internalTool || "agentkit_search_knowledge",
|
|
1133
|
+
description: "Search the Agent Capsule's configured Knowledge sources for business-specific facts.",
|
|
1134
|
+
visibility: "internal",
|
|
1135
|
+
inputSchema: {
|
|
1136
|
+
type: "object",
|
|
1137
|
+
properties: {
|
|
1138
|
+
query: { type: "string" },
|
|
1139
|
+
topK: { type: "integer" },
|
|
1140
|
+
},
|
|
1141
|
+
required: ["query"],
|
|
1142
|
+
additionalProperties: false,
|
|
1143
|
+
},
|
|
1144
|
+
outputSchema: {
|
|
1145
|
+
type: "object",
|
|
1146
|
+
properties: {
|
|
1147
|
+
results: {
|
|
1148
|
+
type: "array",
|
|
1149
|
+
items: {
|
|
1150
|
+
type: "object",
|
|
1151
|
+
properties: {
|
|
1152
|
+
content: { type: "string" },
|
|
1153
|
+
score: { type: "number" },
|
|
1154
|
+
source: { type: "object" },
|
|
1155
|
+
},
|
|
1156
|
+
required: ["content", "score", "source"],
|
|
1157
|
+
},
|
|
1158
|
+
},
|
|
1159
|
+
},
|
|
1160
|
+
required: ["results"],
|
|
1161
|
+
additionalProperties: false,
|
|
1162
|
+
},
|
|
1163
|
+
secrets: manifest.knowledge.embedding.secret ? [manifest.knowledge.embedding.secret] : [],
|
|
1164
|
+
async execute(input, ctx) {
|
|
1165
|
+
const query = typeof input?.query === "string" ? input.query : "";
|
|
1166
|
+
const topK = normalizeKnowledgeTopK(input?.topK ?? manifest.knowledge.retrieval.topK);
|
|
1167
|
+
const results = await searchHostedKnowledge(ctx.database, query, topK, ctx.secrets ?? {});
|
|
1168
|
+
|
|
1169
|
+
return {
|
|
1170
|
+
results: results.map((result) => ({
|
|
1171
|
+
content: result.content,
|
|
1172
|
+
score: result.score,
|
|
1173
|
+
source: result.source,
|
|
1174
|
+
})),
|
|
1175
|
+
};
|
|
1176
|
+
},
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
async function searchHostedKnowledge(database, query, topK, secrets) {
|
|
1181
|
+
const normalizedQuery = String(query || "").trim();
|
|
1182
|
+
|
|
1183
|
+
if (!normalizedQuery) {
|
|
1184
|
+
throw agentKitError("knowledge_query_invalid", "Knowledge search query must be a non-empty string.");
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const hasEmbeddingProvider = manifest.knowledge.embedding.provider !== "none";
|
|
1188
|
+
const lexical =
|
|
1189
|
+
manifest.knowledge.retrieval.hybrid || !hasEmbeddingProvider
|
|
1190
|
+
? await searchHostedKnowledgeLexical(database, normalizedQuery, Math.max(topK * 4, topK))
|
|
1191
|
+
: [];
|
|
1192
|
+
const semantic = hasEmbeddingProvider
|
|
1193
|
+
? await searchHostedKnowledgeSemantic(database, normalizedQuery, topK * 4, secrets)
|
|
1194
|
+
: [];
|
|
1195
|
+
|
|
1196
|
+
return combineHostedKnowledgeResults(lexical, semantic, topK);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
async function searchHostedKnowledgeLexical(database, query, limit) {
|
|
1200
|
+
const tokens = hostedKnowledgeQueryTokens(query);
|
|
1201
|
+
|
|
1202
|
+
if (tokens.length === 0) {
|
|
1203
|
+
return [];
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
try {
|
|
1207
|
+
const result = await database.query(
|
|
1208
|
+
\`
|
|
1209
|
+
SELECT
|
|
1210
|
+
c.id AS chunk_id,
|
|
1211
|
+
c.source_id AS source_id,
|
|
1212
|
+
c.content AS content,
|
|
1213
|
+
c.title AS title,
|
|
1214
|
+
c.section AS section,
|
|
1215
|
+
c.locator AS locator,
|
|
1216
|
+
s.source_key AS source_key,
|
|
1217
|
+
rank
|
|
1218
|
+
FROM agentkit_knowledge_fts
|
|
1219
|
+
JOIN agentkit_knowledge_chunks c ON c.id = agentkit_knowledge_fts.chunk_id
|
|
1220
|
+
JOIN agentkit_knowledge_sources s ON s.id = c.source_id
|
|
1221
|
+
WHERE agentkit_knowledge_fts MATCH ?
|
|
1222
|
+
ORDER BY rank ASC
|
|
1223
|
+
LIMIT ?
|
|
1224
|
+
\`,
|
|
1225
|
+
[tokens.map((token) => \`"\${token.replace(/"/g, '""')}"\`).join(" OR "), limit],
|
|
1226
|
+
);
|
|
1227
|
+
|
|
1228
|
+
return result.rows.map((row, index) => hostedKnowledgeResultFromRow(row, hostedLexicalScore(index), null));
|
|
1229
|
+
} catch {
|
|
1230
|
+
return searchHostedKnowledgeLexicalFallback(database, query, limit);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
async function searchHostedKnowledgeLexicalFallback(database, query, limit) {
|
|
1235
|
+
const like = \`%\${query.toLowerCase()}%\`;
|
|
1236
|
+
const result = await database.query(
|
|
1237
|
+
\`
|
|
1238
|
+
SELECT
|
|
1239
|
+
c.id AS chunk_id,
|
|
1240
|
+
c.source_id AS source_id,
|
|
1241
|
+
c.content AS content,
|
|
1242
|
+
c.title AS title,
|
|
1243
|
+
c.section AS section,
|
|
1244
|
+
c.locator AS locator,
|
|
1245
|
+
s.source_key AS source_key
|
|
1246
|
+
FROM agentkit_knowledge_chunks c
|
|
1247
|
+
JOIN agentkit_knowledge_sources s ON s.id = c.source_id
|
|
1248
|
+
WHERE lower(c.content) LIKE ?
|
|
1249
|
+
OR lower(COALESCE(c.title, '')) LIKE ?
|
|
1250
|
+
OR lower(COALESCE(c.section, '')) LIKE ?
|
|
1251
|
+
ORDER BY c.created_at DESC, c.ordinal ASC
|
|
1252
|
+
LIMIT ?
|
|
1253
|
+
\`,
|
|
1254
|
+
[like, like, like, limit],
|
|
1255
|
+
);
|
|
1256
|
+
|
|
1257
|
+
return result.rows.map((row, index) => hostedKnowledgeResultFromRow(row, hostedLexicalScore(index), null));
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
async function searchHostedKnowledgeSemantic(database, query, limit, secrets) {
|
|
1261
|
+
const queryEmbedding = await createHostedKnowledgeEmbedding(query, secrets);
|
|
1262
|
+
|
|
1263
|
+
if (!queryEmbedding) {
|
|
1264
|
+
return [];
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
try {
|
|
1268
|
+
return await searchHostedKnowledgeNativeVector(database, queryEmbedding, limit);
|
|
1269
|
+
} catch {
|
|
1270
|
+
return searchHostedKnowledgeSemanticFallback(database, queryEmbedding, limit);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
async function searchHostedKnowledgeNativeVector(database, queryEmbedding, limit) {
|
|
1275
|
+
const vector = JSON.stringify(queryEmbedding);
|
|
1276
|
+
const result = await database.query(
|
|
1277
|
+
\`
|
|
1278
|
+
SELECT
|
|
1279
|
+
c.id AS chunk_id,
|
|
1280
|
+
c.source_id AS source_id,
|
|
1281
|
+
c.content AS content,
|
|
1282
|
+
c.title AS title,
|
|
1283
|
+
c.section AS section,
|
|
1284
|
+
c.locator AS locator,
|
|
1285
|
+
s.source_key AS source_key,
|
|
1286
|
+
vector_distance_cos(v.embedding, vector32(?)) AS vector_distance
|
|
1287
|
+
FROM vector_top_k('agentkit_knowledge_vectors_embedding_idx', vector32(?), ?) nearest
|
|
1288
|
+
JOIN agentkit_knowledge_vectors v ON v.id = nearest.id
|
|
1289
|
+
JOIN agentkit_knowledge_chunks c ON c.id = v.chunk_id
|
|
1290
|
+
JOIN agentkit_knowledge_sources s ON s.id = c.source_id
|
|
1291
|
+
ORDER BY vector_distance ASC
|
|
1292
|
+
\`,
|
|
1293
|
+
[vector, vector, limit],
|
|
1294
|
+
);
|
|
1295
|
+
|
|
1296
|
+
return result.rows
|
|
1297
|
+
.map((row) => {
|
|
1298
|
+
const distance = Number(row.vector_distance);
|
|
1299
|
+
const semantic = Number.isFinite(distance) ? 1 - distance : 0;
|
|
1300
|
+
return hostedKnowledgeResultFromRow(row, 0, semantic);
|
|
1301
|
+
})
|
|
1302
|
+
.filter((result) => result.match.semantic !== null && result.match.semantic > 0)
|
|
1303
|
+
.slice(0, limit);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
async function searchHostedKnowledgeSemanticFallback(database, queryEmbedding, limit) {
|
|
1307
|
+
const result = await database.query(
|
|
1308
|
+
\`
|
|
1309
|
+
SELECT
|
|
1310
|
+
c.id AS chunk_id,
|
|
1311
|
+
c.source_id AS source_id,
|
|
1312
|
+
c.content AS content,
|
|
1313
|
+
c.title AS title,
|
|
1314
|
+
c.section AS section,
|
|
1315
|
+
c.locator AS locator,
|
|
1316
|
+
c.embedding_json AS embedding_json,
|
|
1317
|
+
s.source_key AS source_key
|
|
1318
|
+
FROM agentkit_knowledge_chunks c
|
|
1319
|
+
JOIN agentkit_knowledge_sources s ON s.id = c.source_id
|
|
1320
|
+
WHERE c.embedding_json IS NOT NULL
|
|
1321
|
+
ORDER BY c.created_at DESC, c.ordinal ASC
|
|
1322
|
+
\`,
|
|
1323
|
+
);
|
|
1324
|
+
|
|
1325
|
+
return result.rows
|
|
1326
|
+
.map((row) => {
|
|
1327
|
+
const embedding = parseHostedKnowledgeEmbedding(row.embedding_json);
|
|
1328
|
+
const semantic = embedding ? hostedCosineSimilarity(queryEmbedding, embedding) : 0;
|
|
1329
|
+
return hostedKnowledgeResultFromRow(row, 0, semantic);
|
|
1330
|
+
})
|
|
1331
|
+
.filter((result) => result.match.semantic !== null && result.match.semantic > 0)
|
|
1332
|
+
.sort((left, right) => (right.match.semantic ?? 0) - (left.match.semantic ?? 0))
|
|
1333
|
+
.slice(0, limit);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
async function createHostedKnowledgeEmbedding(input, secrets) {
|
|
1337
|
+
const provider = manifest.knowledge.embedding.provider;
|
|
1338
|
+
|
|
1339
|
+
if (provider === "none") {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
if (provider === "test") {
|
|
1344
|
+
return hostedFakeEmbedding(input, manifest.knowledge.embedding.dimensions || 32);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
if (provider !== "openai") {
|
|
1348
|
+
throw agentKitError("knowledge_embedding_provider_unsupported", "Unsupported knowledge embedding provider.");
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
const secretName = manifest.knowledge.embedding.secret || "OPENAI_API_KEY";
|
|
1352
|
+
const apiKey = secrets[secretName];
|
|
1353
|
+
|
|
1354
|
+
if (!apiKey) {
|
|
1355
|
+
throw agentKitError("knowledge_embedding_secret_missing", \`Knowledge embeddings require missing secret "\${secretName}".\`);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
const response = await fetch("https://api.openai.com/v1/embeddings", {
|
|
1359
|
+
method: "POST",
|
|
1360
|
+
headers: {
|
|
1361
|
+
"content-type": "application/json",
|
|
1362
|
+
authorization: \`Bearer \${apiKey}\`,
|
|
1363
|
+
},
|
|
1364
|
+
body: JSON.stringify({
|
|
1365
|
+
model: manifest.knowledge.embedding.model || "text-embedding-3-small",
|
|
1366
|
+
input,
|
|
1367
|
+
encoding_format: "float",
|
|
1368
|
+
...(manifest.knowledge.embedding.dimensions ? { dimensions: manifest.knowledge.embedding.dimensions } : {}),
|
|
1369
|
+
}),
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
if (!response.ok) {
|
|
1373
|
+
const text = await response.text().catch(() => "");
|
|
1374
|
+
throw agentKitError(
|
|
1375
|
+
"knowledge_embedding_failed",
|
|
1376
|
+
\`OpenAI embedding request failed with HTTP \${response.status}.\${text ? " " + text.slice(0, 240) : ""}\`,
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const payload = await response.json();
|
|
1381
|
+
const embedding = payload?.data?.[0]?.embedding;
|
|
1382
|
+
|
|
1383
|
+
if (!Array.isArray(embedding) || embedding.some((value) => typeof value !== "number")) {
|
|
1384
|
+
throw agentKitError("knowledge_embedding_failed", "OpenAI embedding response did not contain a numeric vector.");
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
return embedding;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
function combineHostedKnowledgeResults(lexical, semantic, topK) {
|
|
1391
|
+
const byChunk = new Map();
|
|
1392
|
+
|
|
1393
|
+
for (const result of lexical) {
|
|
1394
|
+
byChunk.set(result.chunkId, result);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
for (const result of semantic) {
|
|
1398
|
+
const existing = byChunk.get(result.chunkId);
|
|
1399
|
+
|
|
1400
|
+
if (!existing) {
|
|
1401
|
+
byChunk.set(result.chunkId, {
|
|
1402
|
+
...result,
|
|
1403
|
+
score: hostedCombinedScore(0, result.match.semantic),
|
|
1404
|
+
});
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
byChunk.set(result.chunkId, {
|
|
1409
|
+
...existing,
|
|
1410
|
+
score: hostedCombinedScore(existing.match.lexical, result.match.semantic),
|
|
1411
|
+
match: {
|
|
1412
|
+
lexical: existing.match.lexical,
|
|
1413
|
+
semantic: result.match.semantic,
|
|
1414
|
+
},
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
return Array.from(byChunk.values())
|
|
1419
|
+
.map((result) => ({
|
|
1420
|
+
...result,
|
|
1421
|
+
score: result.score || hostedCombinedScore(result.match.lexical, result.match.semantic),
|
|
1422
|
+
}))
|
|
1423
|
+
.sort((left, right) => right.score - left.score || left.source.path.localeCompare(right.source.path))
|
|
1424
|
+
.slice(0, topK);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
function hostedKnowledgeResultFromRow(row, lexical, semantic) {
|
|
1428
|
+
return {
|
|
1429
|
+
chunkId: String(row.chunk_id),
|
|
1430
|
+
content: String(row.content),
|
|
1431
|
+
score: hostedCombinedScore(lexical, semantic),
|
|
1432
|
+
source: {
|
|
1433
|
+
id: String(row.source_id),
|
|
1434
|
+
path: String(row.source_key),
|
|
1435
|
+
title: typeof row.title === "string" ? row.title : null,
|
|
1436
|
+
section: typeof row.section === "string" ? row.section : null,
|
|
1437
|
+
locator: typeof row.locator === "string" ? row.locator : null,
|
|
1438
|
+
},
|
|
1439
|
+
match: {
|
|
1440
|
+
lexical,
|
|
1441
|
+
semantic,
|
|
1442
|
+
},
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
function normalizeKnowledgeTopK(value) {
|
|
1447
|
+
const topK = Number(value);
|
|
1448
|
+
|
|
1449
|
+
if (!Number.isInteger(topK) || topK <= 0 || topK > 50) {
|
|
1450
|
+
throw agentKitError("knowledge_query_invalid", "Knowledge search topK must be a positive integer no larger than 50.");
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
return topK;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
function hostedKnowledgeQueryTokens(query) {
|
|
1457
|
+
return Array.from(new Set(String(query).toLowerCase().match(/[\\p{L}\\p{N}_-]+/gu) ?? [])).slice(0, 12);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
function parseHostedKnowledgeEmbedding(value) {
|
|
1461
|
+
if (typeof value !== "string") {
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
try {
|
|
1466
|
+
const parsed = JSON.parse(value);
|
|
1467
|
+
return Array.isArray(parsed) && parsed.every((item) => typeof item === "number") ? parsed : null;
|
|
1468
|
+
} catch {
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
function hostedCombinedScore(lexical, semantic) {
|
|
1474
|
+
const semanticScore = semantic === null ? 0 : Math.max(0, (semantic + 1) / 2);
|
|
1475
|
+
return Number((lexical * 0.6 + semanticScore * 0.4).toFixed(6));
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
function hostedLexicalScore(index) {
|
|
1479
|
+
return Number((1 / (index + 1)).toFixed(6));
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function hostedCosineSimilarity(left, right) {
|
|
1483
|
+
if (left.length !== right.length || left.length === 0) {
|
|
1484
|
+
return 0;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
let dot = 0;
|
|
1488
|
+
let leftNorm = 0;
|
|
1489
|
+
let rightNorm = 0;
|
|
1490
|
+
|
|
1491
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
1492
|
+
dot += left[index] * right[index];
|
|
1493
|
+
leftNorm += left[index] * left[index];
|
|
1494
|
+
rightNorm += right[index] * right[index];
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
if (leftNorm === 0 || rightNorm === 0) {
|
|
1498
|
+
return 0;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
return dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
async function hostedFakeEmbedding(input, dimensions) {
|
|
1505
|
+
const values = Array.from({ length: dimensions }, () => 0);
|
|
1506
|
+
const tokens = String(input).toLowerCase().match(/[\\p{L}\\p{N}_-]+/gu) ?? [];
|
|
1507
|
+
const encoder = new TextEncoder();
|
|
1508
|
+
|
|
1509
|
+
for (const token of tokens) {
|
|
1510
|
+
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", encoder.encode(token)));
|
|
1511
|
+
const slot = hash[0] % dimensions;
|
|
1512
|
+
const sign = hash[1] % 2 === 0 ? 1 : -1;
|
|
1513
|
+
values[slot] += sign * (1 + (hash[2] % 7) / 10);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
const norm = Math.sqrt(values.reduce((sum, value) => sum + value * value, 0));
|
|
1517
|
+
return norm === 0 ? values : values.map((value) => Number((value / norm).toFixed(8)));
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1055
1520
|
async function persistFailure(env, conversationId, userMessage, error) {
|
|
1056
1521
|
await persist(env, {
|
|
1057
1522
|
conversationId,
|
package/src/storage/sqlite.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { DatabaseArgs, DatabaseResult, DatabaseRow, DatabaseStatement } fro
|
|
|
6
6
|
import type { AgentMessageRole, ProviderRunResult } from "../providers";
|
|
7
7
|
import type { LoadedAgentCapsule } from "../runtime/config";
|
|
8
8
|
import { AgentKitError } from "../runtime/errors";
|
|
9
|
+
import { KNOWLEDGE_MIGRATION_ID, KNOWLEDGE_SCHEMA_SQL } from "../runtime/knowledge/schema";
|
|
9
10
|
|
|
10
11
|
export type ConversationSummary = {
|
|
11
12
|
id: string;
|
|
@@ -153,6 +154,10 @@ const MIGRATIONS = [
|
|
|
153
154
|
ADD COLUMN visibility TEXT NOT NULL DEFAULT 'user';
|
|
154
155
|
`,
|
|
155
156
|
},
|
|
157
|
+
{
|
|
158
|
+
id: KNOWLEDGE_MIGRATION_ID,
|
|
159
|
+
sql: KNOWLEDGE_SCHEMA_SQL,
|
|
160
|
+
},
|
|
156
161
|
] as const;
|
|
157
162
|
|
|
158
163
|
export async function openCapsuleStore(capsule: LoadedAgentCapsule): Promise<SqliteAgentKitStore> {
|
package/src/templates/blank.ts
CHANGED
|
@@ -147,7 +147,7 @@ When the owner opens this folder in Codex, Claude Code, or another coding agent
|
|
|
147
147
|
|
|
148
148
|
Start building immediately:
|
|
149
149
|
|
|
150
|
-
-
|
|
150
|
+
- Start with \`skills/agentkit-capsule/SKILL.md\`, then use \`npm run agentkit -- docs llms\` as the docs router.
|
|
151
151
|
- Infer the first useful version from the owner's request.
|
|
152
152
|
- Edit \`prompts/instructions.md\` for the agent behavior.
|
|
153
153
|
- Edit \`agentkit.config.ts\` for provider, tools, secrets, access, and storage.
|
|
@@ -170,7 +170,8 @@ Start building immediately:
|
|
|
170
170
|
- \`npm run agentkit -- inspect\`: print machine-readable capsule state.
|
|
171
171
|
- \`printf %s "$VALUE" | npm run agentkit -- env set <NAME> --stdin\`: write a local secret value to ignored \`.env\` without putting it in shell history.
|
|
172
172
|
- \`npm run agentkit -- env list\`: list local secret names without printing values.
|
|
173
|
-
- \`npm run agentkit -- docs
|
|
173
|
+
- \`npm run agentkit -- docs llms\`: print the lightweight AgentKit docs router.
|
|
174
|
+
- \`npm run agentkit -- docs full\`: print the full AgentKit contract path only when a skill asks for it.
|
|
174
175
|
|
|
175
176
|
## Testing With A UI
|
|
176
177
|
|
|
@@ -305,12 +306,14 @@ If you add a tool, also run a fake-provider tool smoke test:
|
|
|
305
306
|
npm run agentkit -- tool tool_name --input '{}'
|
|
306
307
|
\`\`\`
|
|
307
308
|
|
|
308
|
-
For the
|
|
309
|
+
For the lightweight docs router, read the path printed by:
|
|
309
310
|
|
|
310
311
|
\`\`\`sh
|
|
311
|
-
npm run agentkit -- docs
|
|
312
|
+
npm run agentkit -- docs llms
|
|
312
313
|
\`\`\`
|
|
313
314
|
|
|
315
|
+
Read the full framework contract with \`npm run agentkit -- docs full\` only when a skill asks for it.
|
|
316
|
+
|
|
314
317
|
## Hosted Deploy
|
|
315
318
|
|
|
316
319
|
This capsule is hosted-deploy ready by default.
|
|
@@ -343,6 +346,7 @@ Use AgentKit conventions when editing this project.
|
|
|
343
346
|
- Production secrets must be managed secrets, not committed files.
|
|
344
347
|
- Keep required local secret names in \`.env.schema\` and values in ignored \`.env\`. AgentKit local commands load \`.env\` directly.
|
|
345
348
|
- Treat the owner's natural-language request as the brief and start implementing inside this capsule.
|
|
349
|
+
- Start with \`skills/agentkit-capsule/SKILL.md\` when the task is not obvious.
|
|
346
350
|
`,
|
|
347
351
|
},
|
|
348
352
|
{
|
|
@@ -887,7 +887,8 @@ A Clara conversa com clientes, coleta nome, email e telefone, consulta disponibi
|
|
|
887
887
|
- \`npm run eval\`: rodar evals.
|
|
888
888
|
- \`npm run dev\`: rodar a runtime local.
|
|
889
889
|
- \`npm run agentkit -- inspect\`: imprimir o estado da cápsula.
|
|
890
|
-
- \`npm run agentkit -- docs
|
|
890
|
+
- \`npm run agentkit -- docs llms\`: imprimir o roteador leve da documentação AgentKit.
|
|
891
|
+
- \`npm run agentkit -- docs full\`: imprimir o contrato completo do AgentKit somente quando uma skill pedir.
|
|
891
892
|
|
|
892
893
|
## Teste Com UI
|
|
893
894
|
|
|
@@ -903,6 +904,7 @@ A Clara conversa com clientes, coleta nome, email e telefone, consulta disponibi
|
|
|
903
904
|
- Mantenha segredos em \`.env\`, nunca em arquivos versionados.
|
|
904
905
|
- Não espere wizard ou recipe. AgentKit fornece o scaffold e o contrato; implemente diretamente conforme o brief do dono.
|
|
905
906
|
- Para trocar para um provider real, o dono deve escolher OpenRouter, OpenAI, Anthropic ou outro provider suportado. Depois edite \`agentkit.config.ts\`, atualize \`.env.schema\` e configure secrets locais/hosted.
|
|
907
|
+
- Quando a próxima ação não for óbvia, comece por \`skills/agentkit-capsule/SKILL.md\`.
|
|
906
908
|
`,
|
|
907
909
|
},
|
|
908
910
|
{
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentkit-build-agent
|
|
3
|
+
description: Use when the owner gives a natural-language brief for a new or changed AgentKit agent and expects the coding agent to turn it into a working local capsule with prompts, tools, schema, evals, and verification.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build An AgentKit Agent
|
|
7
|
+
|
|
8
|
+
Use this when the owner asks for an agent in plain language.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. Read `agentkit.config.ts`, `prompts/instructions.md`, `schema.sql`, `evals/`, and existing `tools/`.
|
|
13
|
+
2. Infer the first useful local version from the owner's brief.
|
|
14
|
+
3. Edit `prompts/instructions.md` for behavior, boundaries, intake questions, escalation rules, and tool-use policy.
|
|
15
|
+
4. Add tools only when the agent needs action, live data, authorization-sensitive data, or durable writes.
|
|
16
|
+
5. Add database tables to `schema.sql` when the agent owns records.
|
|
17
|
+
6. Add or update evals for the main flow.
|
|
18
|
+
7. Keep the capsule runnable on `test/fake` unless the owner has chosen a real provider.
|
|
19
|
+
|
|
20
|
+
## Templates
|
|
21
|
+
|
|
22
|
+
Use these only when they match the brief:
|
|
23
|
+
|
|
24
|
+
- `templates/support-agent.instructions.md`
|
|
25
|
+
- `templates/appointment-intake.instructions.md`
|
|
26
|
+
- `templates/sales-qualifier.instructions.md`
|
|
27
|
+
|
|
28
|
+
For prompt-only work, use `skills/agentkit-prompts/SKILL.md`.
|
|
29
|
+
For database-backed tools, use `skills/agentkit-database/SKILL.md`.
|
|
30
|
+
|
|
31
|
+
## Verification
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm run typecheck
|
|
35
|
+
npm run agentkit -- inspect
|
|
36
|
+
npm run chat -- --message "hello"
|
|
37
|
+
npm run eval
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If a tool was added:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npm run agentkit -- tool <tool_name> --input '<json>'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Final Response
|
|
47
|
+
|
|
48
|
+
Summarize the files changed, assumptions made, verification results, and whether the behavior was tested with `test/fake` or a real provider selected by the owner.
|
|
49
|
+
|