[{"data":1,"prerenderedAt":459},["ShallowReactive",2],{"layout-chapters":3,"sidebar-chapters":34,"chapter-factor-8":65,"all-chapters":442},[4,6,8,10,12,14,16,18,20,22,24,26,28,30,32],{"slug":5},"intro",{"slug":7},"brief-history",{"slug":9},"factor-1",{"slug":11},"factor-2",{"slug":13},"factor-3",{"slug":15},"factor-4",{"slug":17},"factor-5",{"slug":19},"factor-6",{"slug":21},"factor-7",{"slug":23},"factor-8",{"slug":25},"factor-9",{"slug":27},"factor-10",{"slug":29},"factor-11",{"slug":31},"factor-12",{"slug":33},"appendix",[35,37,39,41,43,45,47,49,51,53,55,57,59,61,63],{"slug":5,"title":36},"Introduction",{"slug":7,"title":38},"A Brief History of Software",{"slug":9,"title":40},"Factor 1: Natural Language to Tool Calls",{"slug":11,"title":42},"Factor 2: Own Your Prompts",{"slug":13,"title":44},"Factor 3: Own Your Context Window",{"slug":15,"title":46},"Factor 4: Tools Are Structured Outputs",{"slug":17,"title":48},"Factor 5: Unify Execution State",{"slug":19,"title":50},"Factor 6: Launch\u002FPause\u002FResume",{"slug":21,"title":52},"Factor 7: Contact Humans with Tools",{"slug":23,"title":54},"Factor 8: Own Your Control Flow",{"slug":25,"title":56},"Factor 9: Compact Errors into Context",{"slug":27,"title":58},"Factor 10: Small, Focused Agents",{"slug":29,"title":60},"Factor 11: Trigger from Anywhere",{"slug":31,"title":62},"Factor 12: Stateless Reducer",{"slug":33,"title":64},"Appendix: Pre-Fetch Context",{"id":66,"title":54,"body":67,"chapterIndex":195,"description":138,"extension":437,"meta":438,"navigation":155,"path":439,"seo":440,"slug":23,"stem":23,"__hash__":441},"chapters\u002Ffactor-8.md",{"type":68,"value":69,"toc":434},"minimark",[70,75,79,86,89,118,121,132,378,381,396,399,415,430],[71,72,74],"h3",{"id":73},"_8-own-your-control-flow","8. Own your control flow",[76,77,78],"p",{},"If you own your control flow, you can do lots of fun things.",[76,80,81],{},[82,83],"img",{"alt":84,"src":85},"180-control-flow","https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fimg\u002F180-control-flow.png?raw=true",[76,87,88],{},"Build your own control structures that make sense for your specific use case. Specifically, certain types of tool calls may be reason to break out of the loop and wait for a response from a human or another long-running task like a training pipeline. You may also want to incorporate custom implementation of:",[90,91,92,96,99,109,112,115],"ul",{},[93,94,95],"li",{},"summarization or caching of tool call results",[93,97,98],{},"LLM-as-judge on structured output",[93,100,101,102],{},"context window compaction or other ",[103,104,108],"a",{"href":105,"rel":106},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-03-own-your-context-window.md",[107],"nofollow","memory management",[93,110,111],{},"logging, tracing, and metrics",[93,113,114],{},"client-side rate limiting",[93,116,117],{},"durable sleep \u002F pause \u002F \"wait for event\"",[76,119,120],{},"The below example shows three possible control flow patterns:",[90,122,123,126,129],{},[93,124,125],{},"request_clarification: model asked for more info, break the loop and wait for a response from a human",[93,127,128],{},"fetch_git_tags: model asked for a list of git tags, fetch the tags, append to context window, and pass straight back to the model",[93,130,131],{},"deploy_backend: model asked to deploy a backend, this is a high-stakes thing, so break the loop and wait for human approval",[133,134,139],"pre",{"className":135,"code":136,"language":137,"meta":138,"style":138},"language-python shiki shiki-themes github-light github-dark","def handle_next_step(thread: Thread):\n\n  while True:\n    next_step = await determine_next_step(thread_to_prompt(thread))\n    \n    # inlined for clarity - in reality you could put \n    # this in a method, use exceptions for control flow, or whatever you want\n    if next_step.intent == 'request_clarification':\n      thread.events.append({\n        type: 'request_clarification',\n          data: nextStep,\n        })\n\n      await send_message_to_human(next_step)\n      await db.save_thread(thread)\n      # async step - break the loop, we'll get a webhook later\n      break\n    elif next_step.intent == 'fetch_open_issues':\n      thread.events.append({\n        type: 'fetch_open_issues',\n        data: next_step,\n      })\n\n      issues = await linear_client.issues()\n\n      thread.events.append({\n        type: 'fetch_open_issues_result',\n        data: issues,\n      })\n      # sync step - pass the new context to the LLM to determine the NEXT next step\n      continue\n    elif next_step.intent == 'create_issue':\n      thread.events.append({\n        type: 'create_issue',\n        data: next_step,\n      })\n\n      await request_human_approval(next_step)\n      await db.save_thread(thread)\n      # async step - break the loop, we'll get a webhook later\n      break\n","python","",[140,141,142,150,157,163,169,175,181,187,193,199,205,211,217,222,228,234,240,246,252,257,263,269,275,280,286,291,296,302,308,313,319,325,331,336,342,347,352,357,363,368,373],"code",{"__ignoreMap":138},[143,144,147],"span",{"class":145,"line":146},"line",1,[143,148,149],{},"def handle_next_step(thread: Thread):\n",[143,151,153],{"class":145,"line":152},2,[143,154,156],{"emptyLinePlaceholder":155},true,"\n",[143,158,160],{"class":145,"line":159},3,[143,161,162],{},"  while True:\n",[143,164,166],{"class":145,"line":165},4,[143,167,168],{},"    next_step = await determine_next_step(thread_to_prompt(thread))\n",[143,170,172],{"class":145,"line":171},5,[143,173,174],{},"    \n",[143,176,178],{"class":145,"line":177},6,[143,179,180],{},"    # inlined for clarity - in reality you could put \n",[143,182,184],{"class":145,"line":183},7,[143,185,186],{},"    # this in a method, use exceptions for control flow, or whatever you want\n",[143,188,190],{"class":145,"line":189},8,[143,191,192],{},"    if next_step.intent == 'request_clarification':\n",[143,194,196],{"class":145,"line":195},9,[143,197,198],{},"      thread.events.append({\n",[143,200,202],{"class":145,"line":201},10,[143,203,204],{},"        type: 'request_clarification',\n",[143,206,208],{"class":145,"line":207},11,[143,209,210],{},"          data: nextStep,\n",[143,212,214],{"class":145,"line":213},12,[143,215,216],{},"        })\n",[143,218,220],{"class":145,"line":219},13,[143,221,156],{"emptyLinePlaceholder":155},[143,223,225],{"class":145,"line":224},14,[143,226,227],{},"      await send_message_to_human(next_step)\n",[143,229,231],{"class":145,"line":230},15,[143,232,233],{},"      await db.save_thread(thread)\n",[143,235,237],{"class":145,"line":236},16,[143,238,239],{},"      # async step - break the loop, we'll get a webhook later\n",[143,241,243],{"class":145,"line":242},17,[143,244,245],{},"      break\n",[143,247,249],{"class":145,"line":248},18,[143,250,251],{},"    elif next_step.intent == 'fetch_open_issues':\n",[143,253,255],{"class":145,"line":254},19,[143,256,198],{},[143,258,260],{"class":145,"line":259},20,[143,261,262],{},"        type: 'fetch_open_issues',\n",[143,264,266],{"class":145,"line":265},21,[143,267,268],{},"        data: next_step,\n",[143,270,272],{"class":145,"line":271},22,[143,273,274],{},"      })\n",[143,276,278],{"class":145,"line":277},23,[143,279,156],{"emptyLinePlaceholder":155},[143,281,283],{"class":145,"line":282},24,[143,284,285],{},"      issues = await linear_client.issues()\n",[143,287,289],{"class":145,"line":288},25,[143,290,156],{"emptyLinePlaceholder":155},[143,292,294],{"class":145,"line":293},26,[143,295,198],{},[143,297,299],{"class":145,"line":298},27,[143,300,301],{},"        type: 'fetch_open_issues_result',\n",[143,303,305],{"class":145,"line":304},28,[143,306,307],{},"        data: issues,\n",[143,309,311],{"class":145,"line":310},29,[143,312,274],{},[143,314,316],{"class":145,"line":315},30,[143,317,318],{},"      # sync step - pass the new context to the LLM to determine the NEXT next step\n",[143,320,322],{"class":145,"line":321},31,[143,323,324],{},"      continue\n",[143,326,328],{"class":145,"line":327},32,[143,329,330],{},"    elif next_step.intent == 'create_issue':\n",[143,332,334],{"class":145,"line":333},33,[143,335,198],{},[143,337,339],{"class":145,"line":338},34,[143,340,341],{},"        type: 'create_issue',\n",[143,343,345],{"class":145,"line":344},35,[143,346,268],{},[143,348,350],{"class":145,"line":349},36,[143,351,274],{},[143,353,355],{"class":145,"line":354},37,[143,356,156],{"emptyLinePlaceholder":155},[143,358,360],{"class":145,"line":359},38,[143,361,362],{},"      await request_human_approval(next_step)\n",[143,364,366],{"class":145,"line":365},39,[143,367,233],{},[143,369,371],{"class":145,"line":370},40,[143,372,239],{},[143,374,376],{"class":145,"line":375},41,[143,377,245],{},[76,379,380],{},"This pattern allows you to interrupt and resume your agent's flow as needed, creating more natural conversations and workflows.",[76,382,383,387,388,391,392,395],{},[384,385,386],"strong",{},"Example"," - the number one feature request I have for every AI framework out there is we need to be able to interrupt\na working agent and resume later, ESPECIALLY between the moment of tool ",[384,389,390],{},"selection"," and the moment of tool ",[384,393,394],{},"invocation",".",[76,397,398],{},"Without this level of resumability\u002Fgranularity, there's no way to review\u002Fapprove the tool call before it runs, which means\nyou're forced to either:",[400,401,402,409,412],"ol",{},[93,403,404,405,408],{},"Pause the task in memory while waiting for the long-running thing to complete (think ",[140,406,407],{},"while...sleep",") and restart it from the beginning if the process is interrupted",[93,410,411],{},"Restrict the agent to only low-stakes, low-risk calls like research and summarization",[93,413,414],{},"Give the agent access to do bigger, more useful things, and just yolo hope it doesn't screw up",[76,416,417,418,423,424,429],{},"You may notice this is closely related to ",[103,419,422],{"href":420,"rel":421},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-05-unify-execution-state.md",[107],"factor 5 - unify execution state and business state"," and ",[103,425,428],{"href":426,"rel":427},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-06-launch-pause-resume.md",[107],"factor 6 - launch\u002Fpause\u002Fresume with simple APIs",", but can be implemented independently.",[431,432,433],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":138,"searchDepth":152,"depth":152,"links":435},[436],{"id":73,"depth":159,"text":74},"md",{},"\u002Ffactor-8",{"title":54,"description":138},"L9nyBqaU4DTF4ni3tEDIToB_BeMF_7PZfW7WVyh8Tg0",[443,445,446,447,448,449,450,451,452,453,454,455,456,457,458],{"slug":5,"title":36,"chapterIndex":444},0,{"slug":7,"title":38,"chapterIndex":146},{"slug":9,"title":40,"chapterIndex":152},{"slug":11,"title":42,"chapterIndex":159},{"slug":13,"title":44,"chapterIndex":165},{"slug":15,"title":46,"chapterIndex":171},{"slug":17,"title":48,"chapterIndex":177},{"slug":19,"title":50,"chapterIndex":183},{"slug":21,"title":52,"chapterIndex":189},{"slug":23,"title":54,"chapterIndex":195},{"slug":25,"title":56,"chapterIndex":201},{"slug":27,"title":58,"chapterIndex":207},{"slug":29,"title":60,"chapterIndex":213},{"slug":31,"title":62,"chapterIndex":219},{"slug":33,"title":64,"chapterIndex":224},1788741638422]