Skip to content

yet another redesign #485

Description

@jeremymanning

i'd like to do a complete redesign of this package. we may be able to use or salvage bits and pieces, but the fundamental design needs an overhaul.

i'm seeing three interrelated components. i haven't thought through all of the details, so there are going to be gaps and inconsistencies, and many edge cases i haven't fully thought through.

component 1: recursive problem breakdown

basic description

given a task, it should be broken down into a series of concrete steps. this is what a "pipeline" is. ideally a pipeline should small number of steps-- like aiming for on the order of 10 or fewer, but possibly (for very complex tasks) this could be potentially unbounded. pipelines may also have loops, conditionals, and goto statements; the control flow of pipelines must be a complete (albeit simple) "language".

each step in a pipeline takes one of two forms:

  1. an atomic step (see component 3) that we already know how to solve. essentially this is either (a) a simple or trivial task or (b) a complex task that we already had to solve and whose solution is stored and searchable/deemed relevant. in either case, the "point" is that the step doesn't require substantial additional work; we can just proceed by running an existing skill or pipeline, and then "smoothing over the outputs" (i.e., parsing, adapting the answer, converting it to the desired format, or other trivial operations that can be deterministic OR that might require a reasonably capable LLM to accomplish).

  2. an ambiguous step that we do NOT know how to solve. instead of trying to solve it immediately, we describe (briefly) what happens at this step (what are the inputs, what are the outputs/consequences). then it gets fed recursively into the same "break down a problem" system. in other words, the step becomes its own pipeline, that can be called and referenced from its parent pipeline when the corresponding step of the parent pipeline is reached. the parent (i.e., process storing the pipeline where the child pipeline is one of the steps) assumes responsibility for calling that child pipeline and smoothing over its outputs/consequences (same process as for atomic steps).

a critical special case, which we need to handle, is that we may not KNOW how to break the step down further. e.g., it may be its own research project. in that case, we invoke a planning tool, which may involve its own (separate) research and experimentation pipeline. it must be allowed to search the web, read existing literature, download data, and run code (simulations, real commands). doing this properly requires careful thought and planning.

other thoughts

the goal here is to come up with a GENERAL solution for solving arbitrary problems by recursively breaking them down into successively smaller and smaller pieces, until the pieces become simple enough that it's obvious how to solve them.

in some cases we might know how to solve the problem from the start-- but in other cases, breaking down a problem into a pipeline may NOT be obvious...or the "obvious" solution may not actually work in practice.

therefore, the system for constructing pipelines needs to incorporate three additional tools that will end up being used repeatedly by many parts of the toolbox:

a) critical review. this entails launching a SEPARATE subagent with access to the solution or plan, AND a high-level description of the problem to be solved. the job of the subagent is to red-team the proposed solution and identify gaps and bugs. the original authoring agent and the red-team agent continue looping until the red-team concerns come back clean. note that concerns MUST be tracked so that the scope doesn't simply drift endlessly with the problem being constantly redefined in each round. fundamental rule: no authoring agent can (ever) self-review its own work. the same model may be allowed to self-review another instance of itself, but that instance must be run in a fresh session from the one used to construct the artifact under review.

b) context recursion. when the problem or context grows too large, this needs to be managed via a system analogous to file descriptor tables on storage devices. the original problem/contents is stored in a table that points to either (i) a complete document, if it is sufficiently small to fit in no more than 25% of the model's context, or (ii) a set of summaries of documents, each of which may be prepared by other agents who themselves use file descriptor table like objects to store/summarize anything "below" their scope. the total length of the set of summaries must be small enough to fit in no more than 25% of the model's context. the "recursive" part comes in as follows:

suppose our max available context is 1M tokens. So we can use up to 250k for a document or summary. if the document is more than 250k tokens long, it MUST be broken down (considered in pieces), each no more than 250k tokens long. each piece is then summarized. then the summaries are stitched back together. but suppose the stitched-together summaries are also longer than 250k tokens. then the summaries need their own summaries, and so on. this needs to happen recursively (like for file descriptor indices and inodes, where a file may point to a location on disk where something is stored, OR another inode table) to enable documents or content of ANY arbitrary length to "fit" into no more than 25% of ANY (abritrary) model's context.

note that this entire process must be fully traceable and searchable, in case the "details" need to be retrieved later. on disk the full content must be available, with pieces (at each level of summarization) accessible on demand in a RAG database, keyword search, and similar. (need to think about the right way to do this!)

c.) data-driven design. we can't know how to solve a problem that has never been solved before. but we can say: IF each piece of the problem looks like THIS, and if each step behaves like THAT, does the full proposed solution hold together logically? we can turn the proposed solution into a series of deterministic operations that are applied to a set of concrete objects with the assumed form. for each object, we also need criteria for determining (when we see real data or try the thing out in practice) whether it's going to match our expectations or not- and if not, we may need to re-evaluate our assumptions and possibly re-design a solution! we also need a system for writing down (in advance) expectations about how the solution should behave (e.g., what are its results, what other observable patterns should we see in the behavior). then that simulation should be written and run, and (via the system "a" described above) red-teamed and critically evaluated, in a fully traceable way, by independent agents (i.e., agents that didn't produce the original artifacts or predictions). again, this needs super careful planning and designing. essentially we're trying to automated the process of quickly generating "estimated solutions" as runnable code that can be checked deterministically against the assumptions, as a means of evaluating the strengths or weaknesses of the approach.

component 2: organization tree

each "step" of a pipeline (including the full pipeline itself-- which is a single step: "run pipeline X") is a "node" in a tree that represents the structure of the problem. each node is an agent (LLM instance):

  • if the corresponding "step" is atomic (known solution), the node is a "leaf". the agent simply runs its step, red-teams the outputs for quality control, and then passes the results (in the desired format) to its parent node (or the user, if the node is the root of the tree).
  • if the corresponding "step" is ambiguous (NO known solution), the node's children are the pipeline steps represented by the broken-down version of that problem. to "run" that node's step, it needs all of its children to run their steps, in the proper sequence. the node must take responsibility for steering the process, stitching together results from its children, and then passing the result to its parent node (or the user, if the node is at the root of the tree).

nodes in the org tree must also maintain an ability to send, receive, and route messages:

a. SENDING a message means: compose a new message, based on the node's current scope of work, and send it to either that node's parent (including the user, if needed, and if the node is the root of the tree) or an immediate child.
b. RECEIVING a message means: interrupt ongoing operations to process the message content. this may alter how a problem is processed, how the pipeline the node is running is administered, it may be simply ignored, or anything else the node deems necessary or appropriate given its current task.
c. ROUTING a message means: pass along a received message that the node can't handle itself because it is outside of the scope it is immediately handling. there are two ways to route a message: pass it to an immediate parent (if it is HIGHER level than the current node's responsibilities) or pass it to an immediate child (in which case, select the specific child or children who is/are most likely to benefit from or be affected by the message.

component 3: shared resources and communication

shared scratchpad of ALL thinking from EVERY agent

ALL agents take notes in the shared scratchpad, whose access is controlled by a semaphore (lock/mutex). before executing a step, wait for the lock to become available. then grab the lock. then read recent messages and consider whether it alters the current plans/action. if YES, make a quick note about what you were planning to do BEFORE and why recent info means you should alter those plans. if you know how to proceed now (in a different way), say that. if NO, just write down your plans. then release the lock.

the last [5% of the reading model's context length] tokens should be read directly from the scratchpad. use the context recursion strategy above to summarize anything else so that it uses no more than an additional 10% of the reading model's context length. the summarizations should be cached.

an important special case is: my plans might be affected by some other thought that was written down previously, but it's no longer in the latest "direct read" portion of the scratchpad. in this case, since context recursion always must preserve the original text (and/or lower level summaries), the agent is allowed to replace its [15% of its total context length] tokens of "scratchpad context" with a "focused" set of text that is more directly relevant to the agent's current goals. accomplishing this needs careful thought and planning.

shared insights across agents

whereas the scratchpad provides a largely undiluted "stream of consciousness" view of the team's thinking, a seperate searchable system (RAG database?) should be used to track "insights" from the agents. when any agent gains a special insight into the problem-- something non-obvious, something non-trivial, correcting a critical misunderstanding, etc.--it should go into the insights database. each insight is its own entity. agents should use RAG (or similar) to seed its context with relevant insights (using up to 10% of additional context, and using context recursion to summarize as needed), prior to starting a next chunk of work.

special cases:

  • if an agent wants to contribute a NEW insight, it MUST be red-teamed by an independent agent to verify relevance and accuracy. only after a red-team pass may it be added to the pool
  • if an agent thinks an insight should be REMOVED from the pool, that decision needs to be red-teamed by an independent agent who reviews the insight + reasoning for removal and makes a decision. only after red-teaming may an insight be removed from the pool.

shared tools and skills

agents must organize themselves using principles of "compound engineering." initially, we seed a common pool of tools and skills with some useful simple operations that cover a variety of common tasks: browsing the web, web search, text extraction, executing code in a sandboxed virtual environment (in a way that properly handles dependencies), reading/writing/downloading files, and so on. (research is needed to come up with this pool.) but as a pipeline proceeds, NEW skills and tools-- either comprised of existing tools/skills, or entirely new tools/skills constructed from scratch, or combinations thereof--get added to the pool. once added (and red-teamed by an independent agent that must USE the tool/skill and evaluate the quality of the output, fix issues, etc.), anything in the pool may be used by any agent from that point onward.

each tool and skill must be attached to a complete history of when and how it was called.

if a tool or skill is discovered to NOT work as expected by a given agent, a request for review must be initated. effectively, this is a bug report. it must be processed by a DIFFERENT agent.

  • describe the use case
  • describe the expected outcome
  • describe the observed outcome
  • provide any other relevant context or notes

when the reviewer agent picks up a bug report, it must examine the bug report AND the history of that tool/skill's use. the reviewer must then decide to fix the skill/tool, modify it (e.g., clarify where/when/how it should be used), or remove the skill/tool from the pool (if it is broken). removal should happen only if there is sufficient evidence that it is broken, per the log file.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions