Context processing is an ordinary Workflow node
Integrate selection, trimming, and compression through normal nodes and routes; Core records and schedules them uniformly.
Support provides a complete processing node plus separately configurable selection and single-step reduction nodes; the final model call reuses the same PreparedModelCall, and measurement and sending share one transport preparation.
createContextProcessingNode selects the policy and completes assembly; createContextSelectionNode only returns allowed stepIds and does not execute reductions automatically; createContextReductionNode executes exactly one registered stepId. They are all WorkflowNode, not another execution framework, and the App uses normal routes to decide when to enter.
Nodes have this request's signal, deadline, and the same model port. Model calls used by selection and compression also consume ancestor quota and do not create new free requests; over-limit, protected-fragment changes, unknown steps, and late results are still rejected. Custom nodes can implement the same WorkflowNode contract without being constrained by Support algorithms.
Progressive examples for the same business case
Validate and assemble only, without compression yet
Beginner · Node.js
import { AgentCoreEngine, defineWorkflow, route } from '@jangts/agent-core/engine';
import { Support } from '@jangts/agent-core';
import type { BuiltContext, PreparedModelCall } from '@jangts/agent-core/types';
type State = { question: string; input?: BuiltContext; prepared?: PreparedModelCall; answer?: string };
const builder = Support.defineContextFragments<State>([
{ id: 'question', sourceId: 'request', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 1000, format: state => state.question },
], { maxInputChars: 2000 });
const prepare = Support.createContextProcessingNode<State, string>({
id: 'prepare', description: '装配并校验输入', startsRound: true,
plan: state => builder.plan({ input: state }),
update: result => ({ input: result.input, prepared: result.prepared }),
routes: [route.to('reply')],
});
const workflow = defineWorkflow<State, string>({ id: 'chat', nodes: [prepare, {
id: 'reply', description: '使用同次准备回答', requires: ['input', 'prepared'],
async run(state, { model }) { const { value } = await model.invoke({ input: state.input!, prepared: state.prepared! }); return { answer: String(value) }; },
routes: [route.deliver('answer')],
}] });
const agent = new AgentCoreEngine({ workflow, model: { provider: { async *stream() { yield '服务正常'; } } } });
console.log(await agent.run({ question: '状态?' }));import { AgentCoreEngine, defineWorkflow, route } from '@jangts/agent-core/engine';
import { Support } from '@jangts/agent-core';
const builder = Support.defineContextFragments([
{ id: 'question', sourceId: 'request', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 1000, format: state => state.question },
], { maxInputChars: 2000 });
const prepare = Support.createContextProcessingNode({
id: 'prepare', description: '装配并校验输入', startsRound: true,
plan: state => builder.plan({ input: state }),
update: result => ({ input: result.input, prepared: result.prepared }),
routes: [route.to('reply')],
});
const workflow = defineWorkflow({ id: 'chat', nodes: [prepare, {
id: 'reply', description: '使用同次准备回答', requires: ['input', 'prepared'],
async run(state, { model }) { const { value } = await model.invoke({ input: state.input, prepared: state.prepared }); return { answer: String(value) }; },
routes: [route.deliver('answer')],
}] });
const agent = new AgentCoreEngine({ workflow, model: { provider: { async *stream() { yield '服务正常'; } } } });
console.log(await agent.run({ question: '状态?' }));Add a history trimming policy to the same node contract
Configuration · Node.js
Put this node back into the same Workflow as the previous example; each run just adds history to State, and the model reply node does not change its interface. In a complete flow the reply target must exist.
import { Support } from '@jangts/agent-core';
import { route } from '@jangts/agent-core/engine';
import type { BuiltContext, PreparedModelCall } from '@jangts/agent-core/types';
type State = { question: string; history: { role: string; content: string }[]; input?: BuiltContext; prepared?: PreparedModelCall };
const builder = Support.defineContextFragments<State>([
{ id: 'question', sourceId: 'request', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 1000, format: state => state.question },
{ id: 'history', sourceId: 'conversation', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 10000, format: state => ({ messages: state.history }) },
], { maxInputChars: 1200 });
const prepare = Support.createContextProcessingNode<State, string>({
id: 'prepare', description: '按需要裁切旧历史', startsRound: true,
plan: state => builder.plan({ input: state }),
policy: { mode: 'reduce', steps: [{ id: 'recent', kind: 'trim', fragmentId: 'history', field: 'messages',
targetChars: 700, grouping: { kind: 'user-turn' }, keepGroups: 1 }] },
update: result => ({ input: result.input, prepared: result.prepared }),
routes: [route.to('reply')],
});
console.log(prepare.id);import { Support } from '@jangts/agent-core';
import { route } from '@jangts/agent-core/engine';
const builder = Support.defineContextFragments([
{ id: 'question', sourceId: 'request', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 1000, format: state => state.question },
{ id: 'history', sourceId: 'conversation', trust: 'user', lifetime: 'turn', role: 'user', maxChars: 10000, format: state => ({ messages: state.history }) },
], { maxInputChars: 1200 });
const prepare = Support.createContextProcessingNode({
id: 'prepare', description: '按需要裁切旧历史', startsRound: true,
plan: state => builder.plan({ input: state }),
policy: { mode: 'reduce', steps: [{ id: 'recent', kind: 'trim', fragmentId: 'history', field: 'messages',
targetChars: 700, grouping: { kind: 'user-turn' }, keepGroups: 1 }] },
update: result => ({ input: result.input, prepared: result.prepared }),
routes: [route.to('reply')],
});
console.log(prepare.id);