refactor: extractPrompt out of ExtractContext, into ExtractFn parameter

ExtractFn = (schema, prompt, ctx) => Promise<T>
extractPrompt stays in RoleDefinition (definition layer), not in context (state layer).
Callers pass their own prompt — engine uses roleDef.extractPrompt, cursor agent uses its own.

小橘 <xiaoju@shazhou.work>
This commit is contained in:
2026-05-07 01:27:12 +00:00
parent d472de1247
commit 43e1f82303
5 changed files with 16 additions and 9 deletions
@@ -5,6 +5,7 @@ import { createCursorAgent, validateCursorAgentConfig } from "../src/index.js";
const testExtract: ExtractFn = async <T extends Record<string, unknown>>( const testExtract: ExtractFn = async <T extends Record<string, unknown>>(
_schema: z.ZodType<T>, _schema: z.ZodType<T>,
_prompt: string,
_ctx: ExtractContext, _ctx: ExtractContext,
): Promise<T> => ({ workspace: "/tmp" }) as unknown as T; ): Promise<T> => ({ workspace: "/tmp" }) as unknown as T;
+5 -3
View File
@@ -48,10 +48,12 @@ export function createCursorAgent(config: CursorAgentConfig): AgentFn {
const extractCtx: ExtractContext = { const extractCtx: ExtractContext = {
...ctx, ...ctx,
agentContent: "", agentContent: "",
extractPrompt:
"From the thread context, determine the absolute filesystem path where the project/repository is located.",
}; };
const { workspace } = await config.extract(cursorWorkspaceSchema, extractCtx); const { workspace } = await config.extract(
cursorWorkspaceSchema,
"From the thread context, determine the absolute filesystem path where the project/repository is located.",
extractCtx,
);
const fullPrompt = buildAgentPrompt(ctx); const fullPrompt = buildAgentPrompt(ctx);
const args = [ const args = [
"-p", "-p",
+5 -2
View File
@@ -88,10 +88,13 @@ export function createWorkflow<M extends RoleMeta>(
const extractCtx: ExtractContext<M> = { const extractCtx: ExtractContext<M> = {
...agentCtx, ...agentCtx,
agentContent: raw, agentContent: raw,
extractPrompt: roleDef.extractPrompt,
}; };
const meta = await extract(roleDef.schema, extractCtx as unknown as ExtractContext); const meta = await extract(
roleDef.schema,
roleDef.extractPrompt,
extractCtx as unknown as ExtractContext,
);
const ts = Date.now(); const ts = Date.now();
const step = { const step = {
+4 -2
View File
@@ -5,16 +5,18 @@ import type { ExtractContext, LlmProvider } from "./types.js";
export type ExtractFn = <T extends Record<string, unknown>>( export type ExtractFn = <T extends Record<string, unknown>>(
schema: z.ZodType<T>, schema: z.ZodType<T>,
prompt: string,
ctx: ExtractContext, ctx: ExtractContext,
) => Promise<T>; ) => Promise<T>;
/** /**
* Create an ExtractFn backed by an LLM provider. * Create an ExtractFn backed by an LLM provider.
* Builds prompt text from {@link ExtractContext} and calls structured extraction. * Builds prompt text from {@link ExtractContext} plus `prompt` and calls structured extraction.
*/ */
export function createExtract(provider: LlmProvider): ExtractFn { export function createExtract(provider: LlmProvider): ExtractFn {
return async <T extends Record<string, unknown>>( return async <T extends Record<string, unknown>>(
schema: z.ZodType<T>, schema: z.ZodType<T>,
prompt: string,
ctx: ExtractContext, ctx: ExtractContext,
): Promise<T> => { ): Promise<T> => {
const lines: string[] = []; const lines: string[] = [];
@@ -37,7 +39,7 @@ export function createExtract(provider: LlmProvider): ExtractFn {
lines.push(ctx.agentContent); lines.push(ctx.agentContent);
lines.push(""); lines.push("");
lines.push("## Extraction Instruction"); lines.push("## Extraction Instruction");
lines.push(ctx.extractPrompt); lines.push(prompt);
const text = lines.join("\n"); const text = lines.join("\n");
const result = await llmExtractWithRetry({ text, schema, provider }); const result = await llmExtractWithRetry({ text, schema, provider });
+1 -2
View File
@@ -73,10 +73,9 @@ export type AgentContext<M extends RoleMeta = RoleMeta> = ModeratorContext<M> &
}; };
}; };
/** Phase 3: Extractor runs — has agent output and extract instruction. */ /** Phase 3: Extractor runs — has agent output; the extraction instruction is a separate argument to the extract function. */
export type ExtractContext<M extends RoleMeta = RoleMeta> = AgentContext<M> & { export type ExtractContext<M extends RoleMeta = RoleMeta> = AgentContext<M> & {
agentContent: string; agentContent: string;
extractPrompt: string;
}; };
/** Alias — most external consumers see the agent-phase context. */ /** Alias — most external consumers see the agent-phase context. */