refactor: Business logic and dependencies updates

- 核心服务代码更新 (db, server, auth, proxy)
- Agent 相关模块更新 (mindspace, experience)
- 前端组件和 hooks 更新
- 数据库 schema 更新
- 依赖版本更新
This commit is contained in:
john
2026-06-27 08:25:02 +08:00
parent f1220a7905
commit 25f8223253
20 changed files with 1458 additions and 116 deletions
+49 -4
View File
@@ -305,6 +305,7 @@ export function createMindSpaceAgentRunner({
apiSecret,
userAuth,
agentJobService,
experienceService = null,
executeSessionReply = defaultExecuteSessionReply,
apiFetchImpl = null,
}) {
@@ -324,11 +325,15 @@ export function createMindSpaceAgentRunner({
});
});
const runJob = async (jobId) => {
// preClaimed lets a polling worker that already atomically claimed the job
// (via claimNextJob + SKIP LOCKED) hand the claim payload straight in, instead
// of claiming a second time. The HTTP-triggered path calls runJob(jobId) with
// no preClaimed and claims here as before.
const runJob = async (jobId, preClaimed = null) => {
let claim = null;
let sessionId = null;
try {
claim = await agentJobService.claimJob(jobId);
claim = preClaimed ?? (await agentJobService.claimJob(jobId));
const gate = await userAuth.canUseChat(claim.userId);
if (!gate.ok) {
throw runnerError(gate.message || '当前用户无法执行 Agent 任务', 'worker_unavailable');
@@ -377,7 +382,11 @@ export function createMindSpaceAgentRunner({
const localAsset = await agentJobService.getAssetForJob(jobId, claim.jobToken, asset.assetId);
assetContexts.push(await readAssetContext(localAsset));
}
const prompt = buildAgentJobPrompt(claim, assetContexts);
let prompt = buildAgentJobPrompt(claim, assetContexts);
// Inject shared experience (etat C): relevant lessons all instances learned.
// Best-effort — retrieval must never block or fail a job.
const relevant = await retrieveExperience(claim.instruction);
if (relevant) prompt = `${relevant}\n\n${prompt}`;
const requestId = crypto.randomUUID();
const reply = await executeSessionReply(apiFetch, sessionId, requestId, prompt);
const parsed = normalizeStructuredResult(extractJsonObject(reply.text));
@@ -386,7 +395,7 @@ export function createMindSpaceAgentRunner({
await userAuth.billSessionUsage(claim.userId, sessionId, reply.tokenState, requestId);
}
return agentJobService.completeJob(jobId, claim.jobToken, {
const completed = await agentJobService.completeJob(jobId, claim.jobToken, {
title: parsed.title,
summary: parsed.summary,
content: parsed.content,
@@ -395,6 +404,9 @@ export function createMindSpaceAgentRunner({
templateId: parsed.templateId,
sourceAssetIds: claim.allowedAssets.map((asset) => asset.assetId),
});
// Record the outcome as shared experience so other instances benefit.
await recordExperience(claim, sessionId, parsed);
return completed;
} catch (error) {
if (claim?.jobToken) {
await agentJobService
@@ -409,6 +421,39 @@ export function createMindSpaceAgentRunner({
}
};
// Returns a prompt-ready block of relevant prior experience, or '' if none /
// disabled / on any error. Never throws.
async function retrieveExperience(instruction) {
if (!experienceService || !instruction) return '';
try {
const hits = await experienceService.search(instruction, { limit: 3 });
if (!hits.length) return '';
const lines = hits.map((h) => `- ${h.title}: ${h.body}`).join('\n');
return `# 相关经验(供参考,来自历史任务)\n${lines}`;
} catch {
return '';
}
}
// Persists a one-line lesson from a completed job. Best-effort; never throws.
async function recordExperience(claim, sessionId, parsed) {
if (!experienceService) return;
try {
const title = String(parsed?.title ?? claim.instruction ?? '').slice(0, 200);
const summary = String(parsed?.summary ?? '').trim();
if (!title || !summary) return;
await experienceService.record({
kind: 'task_outcome',
title,
body: summary,
sourceSessionId: sessionId,
sourceUserId: claim.userId,
});
} catch {
// swallow — experience recording is non-critical
}
}
return { runJob };
}