Polyweave

@polyweave/feedback-descent (1.0.5)

Published 2026-07-11 15:24:43 +00:00 by Dvorak

Installation

@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/
npm install @polyweave/feedback-descent@1.0.5
"@polyweave/feedback-descent": "1.0.5"

About this package

@polyweave/feedback-descent

Feedback Descent optimization loop as a bt-harness duplex tool. Iteratively improves text artifacts through propose→evaluate cycles with accumulated critique.

Published as @polyweave/feedback-descent.

Synopsis

import { createFeedbackDescentTool } from '@polyweave/feedback-descent';

const tool = createFeedbackDescentTool({
  apiKey: process.env.API_KEY,
  model: 'deepseek-v4-flash',
  initialArtifact: 'Draft version of text...',
  maxIterations: 10,
  patience: 3
});

tool.sink.write({ type: 'TOOL_CALL', content: {} });
tool.source.pipe(mySink);

Install

npm install @polyweave/feedback-descent

Why

LLM-generated artifacts (code, prose, configuration) often need iterative refinement beyond a single generation pass. Feedback Descent gives you a structured optimize-evaluate loop: an agent proposes an improved version, an evaluator judges which is better, and the best artifact is promoted. Critique accumulates across iterations, so the agent sees what was wrong before. This avoids prompt-engineering one-shots and instead applies the proven gradient-free optimization pattern of propose→evaluate→promote with stagnation-based termination.

API

createFeedbackDescentTool(options)

Creates a Polyweave frame duplex tool implementing the Feedback Descent loop. The tool is driven by TOOL_CALL initialization and TICK frames for step-by-step advancement. Under the hood, it creates a bt-harness with the FD_SPEC behavior tree and an in-memory document store.

createFeedbackDescentTool(options?: Object): IFrameDuplex
Name Type Required Default Description
options.apiKey string No API key for LLM adapter calls
options.model string No 'deepseek-v4-flash' Model name for agent and evaluator
options.initialArtifact string No '' Starting artifact text
options.maxIterations number No 10 Maximum promote cycles
options.patience number No 3 Stagnation threshold before termination
options.createAdapter Function No Custom adapter factory function
options.adapterConfig Object No Configuration passed to adapter factory
options.debug boolean No false Enable debug output (stored as _debug)
options.name string No null Adapter name

Returns: A IFrameDuplex with additional properties:

  • source — push-stream source (emits TOOL_RESULT, ERROR, DESCRIPTION)
  • sink — push-stream sink (see frame protocol below)
  • _toolName'feedback_descent'
  • getBest() — Returns the current best artifact text synchronously from the document store
  • close() — Closes the internal document store
  • name — string, adapter name

Frame Protocol

Input frames (sink receives):

Frame Type Description
TOOL_CALL Initiates the optimization loop. Content may include { initialArtifact, maxIterations, patience } to override constructor options. Rejects with ERROR if already running.
TICK Advances the bt-harness by one tick. Forwarded to the current harness.
CANCEL Calls cancel() on the current harness, marks not-running, clears harness reference
ABORT Calls abort() on the current harness, marks not-running, clears harness reference
DESCRIBE Responds with DESCRIPTION frame containing tool metadata, capabilities ['write', 'execute'], schema

Output frames (source emits):

Frame Type Description
TOOL_RESULT Optimization completed successfully. Content: { artifact: string, iterations: number }
TOOL_RESULT Optimization failed (bt:tree_failed). Content: { artifact: string, iterations: 0, failed: true }
ERROR Error during execution. Content: { message: string }. Emitted for: harness validation errors (spec parse failure), bt:error events, or "Already running" rejections
DESCRIPTION Tool description response (capabilities, schema)

Internal tool calls exposed to agents (in the bt-harness):

Tool Arguments Description
fd_read { sectionId } Reads a document section. Default section is 'fd/best'
fd_propose { text, sectionId } Stores a proposal. Default section is 'fd/candidate'. Rejects with { error: 'Missing text' } if text is empty or missing
fd_evaluate { preferred, critique } Records evaluation. preferred is boolean. critique is stored in 'fd/critique'. Updates symbolic state @preferred

Error Conditions

The tool emits ERROR frames (never throws) for:

  • Harness validation failure — when parseBtSpec(FD_SPEC) fails or harness reports !valid; message includes joined harness.errors
  • Already running — when TOOL_CALL received while a previous loop is in progress; message: 'Already running'
  • bt:error — when the behavior tree emits a bt:error frame; message from frame.content.message or 'fd error'

FD_SPEC

The behavior tree specification string (exported for inspection or modification before passing to parseBtSpec).

Symbols:

  • @iteration (number, starts 0) — increments on each evaluate action
  • @stagnant (number, starts 0) — increments when candidate does not improve
  • @preferred (boolean, starts false) — set by fd_evaluate
  • @done (boolean, starts false) — set after evaluate completes, cleared after promote/increment

Postcondition: @stagnant >= @patience

Tree structure:

root = sequence {
  propose (fd_read + fd_propose, runs when @done == false)
  evaluate (fd_read + fd_evaluate, sets @done = true, @iteration++)
  selector {
    sequence { condition improved (@preferred == true), promote_best }
    sequence { condition stagnating (@preferred == false), increment_stagnant (@stagnant++) }
  }
}

Examples

// With custom initial artifact and patience
const tool = createFeedbackDescentTool({
  apiKey: process.env.API_KEY,
  initialArtifact: 'function add(a, b) { return a + b; }',
  maxIterations: 20,
  patience: 5
});

tool.sink.write({ type: 'TOOL_CALL', content: { maxIterations: 15, patience: 3 } });

const sink = createFrameSink((frame) => {
  if (frame.type === 'TOOL_RESULT') {
    console.log('Final artifact:', frame.content.artifact);
  }
});
tool.source.pipe(sink);
// Tick-driven operation
tool.sink.write({ type: 'TOOL_CALL', content: {} });
tool.sink.write({ type: 'TICK' }); // advance one step
tool.sink.write({ type: 'CANCEL' }); // cancel
// Inspect the FD_SPEC
import { FD_SPEC, parseBtSpec } from '@polyweave/bt-harness';
const spec = parseBtSpec(FD_SPEC);
console.log(spec.actions.length); // 4 actions

Tests

npm test

Runs: node --test test/*.test.js

Covers: tool creation with various options, propose-evaluate-promote cycle, stagnation-driven termination, custom maxIterations/patience from TOOL_CALL args, getBest() during and after execution, CANCEL/ABORT handling, error propagation, and harness validation failure.

Requirements

  • Node.js >= 22.0.0
  • ESM only ("type": "module")
  • Peer dependencies: @polyweave/core
  • Dependencies: @polyweave/bt-harness, @polyweave/state, @polyweave/deepseek

Caveats

  • Requires a valid apiKey or createAdapter for LLM model access. Without both, the harness will fail to initialize.
  • The behavior tree uses maxTreeLoops: iterLimit + 5 and relies on the postcondition (@stagnant >= @patience) to terminate.
  • Results are stored in an in-memory document store and are not persisted across restarts.

Dependencies

Dependencies

ID Version
@polyweave/bt-harness *
@polyweave/harness-base *

Development Dependencies

ID Version
@rigor/core *

Peer Dependencies

ID Version
@polyweave/core *

Keywords

polyweave feedback-descent behavior-tree optimization llm
Details
npm
2026-07-11 15:24:43 +00:00
88
John Dvorak
SEE LICENSE IN LICENSE
latest
6.1 KiB
Assets (1)
Versions (3) View all
1.0.5 2026-07-11
1.0.2 2026-07-10
1.0.1 2026-07-10