Keep document bodies out of the cacheable system prompt
buildDocumentsSection was inlining up to 5 document bodies at 2000 chars each into the system prompt. Any edit to any of the listed documents between wakes invalidated the Anthropic prompt cache — the prefix that buildTaskPrompt spent real effort to keep stable. Emit metadata only (id, type, title, author, status, version, updatedAt) and teach agents to GET the single-document endpoint when they actually need the body. The single-doc route did not exist, so add it; update the API doc block in the system prompt accordingly.
This commit is contained in:
1 parent
3a02b4d0a6
commit
9c65291973
3 files changed
+35
-14
No files matched your search
@@ -118,6 +118,24 @@ export function registerFileRoutes(server: AgentApiServer) {
|
||||
server.json(res, 200, docs);
|
||||
});
|
||||
|
||||
server.addRoute("GET", "/api/companies/:companyId/documents/:documentId", (_req, res, params, token) => {
|
||||
if (params.companyId !== token.companyId) {
|
||||
server.json(res, 403, { error: "Company mismatch" });
|
||||
return;
|
||||
}
|
||||
const documentId = params.documentId;
|
||||
if (!documentId) {
|
||||
server.json(res, 400, { error: "Missing documentId" });
|
||||
return;
|
||||
}
|
||||
const doc = db.getDocumentForUpdate(documentId, token.companyId);
|
||||
if (!doc) {
|
||||
server.json(res, 404, { error: "Document not found in this company" });
|
||||
return;
|
||||
}
|
||||
server.json(res, 200, doc);
|
||||
});
|
||||
|
||||
server.addRoute("PATCH", "/api/companies/:companyId/documents/:documentId", (_req, res, params, token, body) => {
|
||||
if (params.companyId !== token.companyId) {
|
||||
server.json(res, 403, { error: "Company mismatch" });
|
||||
|
||||
@@ -18,6 +18,12 @@ Body: {
|
||||
### List Company Documents
|
||||
GET /api/companies/{{companyId}}/documents
|
||||
|
||||
### Fetch a Single Document (by id)
|
||||
GET /api/companies/{{companyId}}/documents/{documentId}
|
||||
Returns the full document record including `content`. The system prompt
|
||||
only lists document metadata to keep the cacheable prefix stable — call
|
||||
this endpoint when you need the actual text.
|
||||
|
||||
### Update Document (with conflict detection)
|
||||
PATCH /api/companies/{{companyId}}/documents/{documentId}
|
||||
Body: {
|
||||
|
||||
@@ -146,23 +146,20 @@ interface DocumentsSectionCtx {
|
||||
export function buildDocumentsSection(ctx: DocumentsSectionCtx): string | null {
|
||||
if (ctx.recentDocuments.length === 0) return null;
|
||||
|
||||
const docLines = ctx.recentDocuments.slice(0, 5).map((d) => {
|
||||
// Intentionally emit metadata only. Inlining document content (even
|
||||
// truncated to 2000 chars × 5 documents) invalidates the Anthropic
|
||||
// prompt cache on every wake where any listed document is edited —
|
||||
// the exact opposite of what buildTaskPrompt's cache-aware layout
|
||||
// promises. Agents fetch the body with
|
||||
// GET /api/companies/{companyId}/documents/{id}
|
||||
// when they actually need it.
|
||||
const docLines = ctx.recentDocuments.slice(0, 10).map((d) => {
|
||||
const author = d.authorAgentId ? (ctx.allAgents.find(a => a.id === d.authorAgentId)?.name ?? "Unknown") : "System";
|
||||
const header = `- [${d.type}] ${d.title} — by ${author}, status: ${d.status}, updated: ${d.updatedAt}`;
|
||||
if (d.content && d.content.length > 0) {
|
||||
const contentLimit = 2000;
|
||||
const excerpt = d.content.length > contentLimit
|
||||
? d.content.slice(0, contentLimit) + "..."
|
||||
: d.content;
|
||||
return `${header}\n Content:\n ${excerpt.split("\n").join("\n ")}`;
|
||||
}
|
||||
return header;
|
||||
return `- [${d.id}] ${d.type}: "${d.title}" — by ${author}, status: ${d.status}, v${d.version}, updated ${d.updatedAt}`;
|
||||
});
|
||||
|
||||
return `# Recent Documents
|
||||
${docLines.join("\n\n")}
|
||||
|
||||
Reference these documents when relevant to your current work. Create new documents for important work artifacts.
|
||||
return `# Recent Documents (metadata only — fetch content via GET /api/companies/{companyId}/documents/{id})
|
||||
${docLines.join("\n")}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user