[{"data":1,"prerenderedAt":723},["ShallowReactive",2],{"layout-chapters":3,"sidebar-chapters":34,"chapter-factor-7":65,"all-chapters":706},[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":52,"body":67,"chapterIndex":190,"description":141,"extension":701,"meta":702,"navigation":151,"path":703,"seo":704,"slug":21,"stem":21,"__hash__":705},"chapters\u002Ffactor-7.md",{"type":68,"value":69,"toc":698},"minimark",[70,75,79,86,94,100,107,112,132,135,289,292,374,402,409,613,616,673,680,686,694],[71,72,74],"h3",{"id":73},"_7-contact-humans-with-tool-calls","7. Contact humans with tool calls",[76,77,78],"p",{},"By default, LLM APIs rely on a fundamental HIGH-STAKES token choice: Are we returning plaintext content, or are we returning structured data?",[76,80,81],{},[82,83],"img",{"alt":84,"src":85},"170-contact-humans-with-tools","https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fimg\u002F170-contact-humans-with-tools.png?raw=true",[76,87,88,89,93],{},"You're putting a lot of weight on that choice of first token, which, in the ",[90,91,92],"code",{},"the weather in tokyo"," case, is",[95,96,97],"blockquote",{},[76,98,99],{},"\"the\"",[76,101,102,103,106],{},"but in the ",[90,104,105],{},"fetch_weather"," case, it's some special token to denote the start of a JSON object.",[95,108,109],{},[76,110,111],{},"|JSON>",[76,113,114,115,119,120,123,124,127,128,131],{},"You might get better results by having the LLM ",[116,117,118],"em",{},"always"," output json, and then declare it's intent with some natural language tokens like ",[90,121,122],{},"request_human_input"," or ",[90,125,126],{},"done_for_now"," (as opposed to a \"proper\" tool like ",[90,129,130],{},"check_weather_in_city",").",[76,133,134],{},"Again, you might not get any performance boost from this, but you should experiment, and ensure you're free to try weird stuff to get the best results.",[136,137,142],"pre",{"className":138,"code":139,"language":140,"meta":141,"style":141},"language-python shiki shiki-themes github-light github-dark","\nclass Options:\n  urgency: Literal[\"low\", \"medium\", \"high\"]\n  format: Literal[\"free_text\", \"yes_no\", \"multiple_choice\"]\n  choices: List[str]\n\n# Tool definition for human interaction\nclass RequestHumanInput:\n  intent: \"request_human_input\"\n  question: str\n  context: str\n  options: Options\n\n# Example usage in the agent loop\nif nextStep.intent == 'request_human_input':\n  thread.events.append({\n    type: 'human_input_requested',\n    data: nextStep\n  })\n  thread_id = await save_state(thread)\n  await notify_human(nextStep, thread_id)\n  return # Break loop and wait for response to come back with thread ID\nelse:\n  # ... other cases\n","python","",[90,143,144,153,159,165,171,177,182,188,194,200,206,212,218,223,229,235,241,247,253,259,265,271,277,283],{"__ignoreMap":141},[145,146,149],"span",{"class":147,"line":148},"line",1,[145,150,152],{"emptyLinePlaceholder":151},true,"\n",[145,154,156],{"class":147,"line":155},2,[145,157,158],{},"class Options:\n",[145,160,162],{"class":147,"line":161},3,[145,163,164],{},"  urgency: Literal[\"low\", \"medium\", \"high\"]\n",[145,166,168],{"class":147,"line":167},4,[145,169,170],{},"  format: Literal[\"free_text\", \"yes_no\", \"multiple_choice\"]\n",[145,172,174],{"class":147,"line":173},5,[145,175,176],{},"  choices: List[str]\n",[145,178,180],{"class":147,"line":179},6,[145,181,152],{"emptyLinePlaceholder":151},[145,183,185],{"class":147,"line":184},7,[145,186,187],{},"# Tool definition for human interaction\n",[145,189,191],{"class":147,"line":190},8,[145,192,193],{},"class RequestHumanInput:\n",[145,195,197],{"class":147,"line":196},9,[145,198,199],{},"  intent: \"request_human_input\"\n",[145,201,203],{"class":147,"line":202},10,[145,204,205],{},"  question: str\n",[145,207,209],{"class":147,"line":208},11,[145,210,211],{},"  context: str\n",[145,213,215],{"class":147,"line":214},12,[145,216,217],{},"  options: Options\n",[145,219,221],{"class":147,"line":220},13,[145,222,152],{"emptyLinePlaceholder":151},[145,224,226],{"class":147,"line":225},14,[145,227,228],{},"# Example usage in the agent loop\n",[145,230,232],{"class":147,"line":231},15,[145,233,234],{},"if nextStep.intent == 'request_human_input':\n",[145,236,238],{"class":147,"line":237},16,[145,239,240],{},"  thread.events.append({\n",[145,242,244],{"class":147,"line":243},17,[145,245,246],{},"    type: 'human_input_requested',\n",[145,248,250],{"class":147,"line":249},18,[145,251,252],{},"    data: nextStep\n",[145,254,256],{"class":147,"line":255},19,[145,257,258],{},"  })\n",[145,260,262],{"class":147,"line":261},20,[145,263,264],{},"  thread_id = await save_state(thread)\n",[145,266,268],{"class":147,"line":267},21,[145,269,270],{},"  await notify_human(nextStep, thread_id)\n",[145,272,274],{"class":147,"line":273},22,[145,275,276],{},"  return # Break loop and wait for response to come back with thread ID\n",[145,278,280],{"class":147,"line":279},23,[145,281,282],{},"else:\n",[145,284,286],{"class":147,"line":285},24,[145,287,288],{},"  # ... other cases\n",[76,290,291],{},"Later, you might receive a webhook from a system that handles slack, email, sms, or other events.",[136,293,295],{"className":138,"code":294,"language":140,"meta":141,"style":141},"\n@app.post('\u002Fwebhook')\ndef webhook(req: Request):\n  thread_id = req.body.threadId\n  thread = await load_state(thread_id)\n  thread.events.push({\n    type: 'response_from_human',\n    data: req.body\n  })\n  # ... simplified for brevity, you likely don't want to block the web worker here\n  next_step = await determine_next_step(thread_to_prompt(thread))\n  thread.events.append(next_step)\n  result = await handle_next_step(thread, next_step)\n  # todo - loop or break or whatever you want\n\n  return {\"status\": \"ok\"}\n",[90,296,297,301,306,311,316,321,326,331,336,340,345,350,355,360,365,369],{"__ignoreMap":141},[145,298,299],{"class":147,"line":148},[145,300,152],{"emptyLinePlaceholder":151},[145,302,303],{"class":147,"line":155},[145,304,305],{},"@app.post('\u002Fwebhook')\n",[145,307,308],{"class":147,"line":161},[145,309,310],{},"def webhook(req: Request):\n",[145,312,313],{"class":147,"line":167},[145,314,315],{},"  thread_id = req.body.threadId\n",[145,317,318],{"class":147,"line":173},[145,319,320],{},"  thread = await load_state(thread_id)\n",[145,322,323],{"class":147,"line":179},[145,324,325],{},"  thread.events.push({\n",[145,327,328],{"class":147,"line":184},[145,329,330],{},"    type: 'response_from_human',\n",[145,332,333],{"class":147,"line":190},[145,334,335],{},"    data: req.body\n",[145,337,338],{"class":147,"line":196},[145,339,258],{},[145,341,342],{"class":147,"line":202},[145,343,344],{},"  # ... simplified for brevity, you likely don't want to block the web worker here\n",[145,346,347],{"class":147,"line":208},[145,348,349],{},"  next_step = await determine_next_step(thread_to_prompt(thread))\n",[145,351,352],{"class":147,"line":214},[145,353,354],{},"  thread.events.append(next_step)\n",[145,356,357],{"class":147,"line":220},[145,358,359],{},"  result = await handle_next_step(thread, next_step)\n",[145,361,362],{"class":147,"line":225},[145,363,364],{},"  # todo - loop or break or whatever you want\n",[145,366,367],{"class":147,"line":231},[145,368,152],{"emptyLinePlaceholder":151},[145,370,371],{"class":147,"line":237},[145,372,373],{},"  return {\"status\": \"ok\"}\n",[76,375,376,377,384,385,384,390,395,396,401],{},"The above includes patterns from ",[378,379,383],"a",{"href":380,"rel":381},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-05-unify-execution-state.md",[382],"nofollow","factor 5 - unify execution state and business state",", ",[378,386,389],{"href":387,"rel":388},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-08-own-your-control-flow.md",[382],"factor 8 - own your control flow",[378,391,394],{"href":392,"rel":393},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-03-own-your-context-window.md",[382],"factor 3 - own your context window",", and ",[378,397,400],{"href":398,"rel":399},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-04-tools-are-structured-outputs.md",[382],"factor 4 - tools are just structured outputs",", and several others.",[76,403,404,405,408],{},"If we were using the XML-y formatted from ",[378,406,394],{"href":392,"rel":407},[382],", our context window after a few turns might look like this:",[136,410,414],{"className":411,"code":412,"language":413,"meta":141,"style":141},"language-xml shiki shiki-themes github-light github-dark","\n(snipped for brevity)\n\n\u003Cslack_message>\n    From: @alex\n    Channel: #deployments\n    Text: Can you deploy backend v1.2.3 to production?\n    Thread: []\n\u003C\u002Fslack_message>\n\n\u003Crequest_human_input>\n    intent: \"request_human_input\"\n    question: \"Would you like to proceed with deploying v1.2.3 to production?\"\n    context: \"This is a production deployment that will affect live users.\"\n    options: {\n        urgency: \"high\"\n        format: \"yes_no\"\n    }\n\u003C\u002Frequest_human_input>\n\n\u003Chuman_response>\n    response: \"yes please proceed\"\n    approved: true\n    timestamp: \"2024-03-15T10:30:00Z\"\n    user: \"alex@company.com\"\n\u003C\u002Fhuman_response>\n\n\u003Cdeploy_backend>\n    intent: \"deploy_backend\"\n    tag: \"v1.2.3\"\n    environment: \"production\"\n\u003C\u002Fdeploy_backend>\n\n\u003Cdeploy_backend_result>\n    status: \"success\"\n    message: \"Deployment v1.2.3 to production completed successfully.\"\n    timestamp: \"2024-03-15T10:30:00Z\"\n\u003C\u002Fdeploy_backend_result>\n","xml",[90,415,416,420,425,429,434,439,444,449,454,459,463,468,473,478,483,488,493,498,503,508,512,517,522,527,532,538,544,549,555,561,567,573,579,584,590,596,602,607],{"__ignoreMap":141},[145,417,418],{"class":147,"line":148},[145,419,152],{"emptyLinePlaceholder":151},[145,421,422],{"class":147,"line":155},[145,423,424],{},"(snipped for brevity)\n",[145,426,427],{"class":147,"line":161},[145,428,152],{"emptyLinePlaceholder":151},[145,430,431],{"class":147,"line":167},[145,432,433],{},"\u003Cslack_message>\n",[145,435,436],{"class":147,"line":173},[145,437,438],{},"    From: @alex\n",[145,440,441],{"class":147,"line":179},[145,442,443],{},"    Channel: #deployments\n",[145,445,446],{"class":147,"line":184},[145,447,448],{},"    Text: Can you deploy backend v1.2.3 to production?\n",[145,450,451],{"class":147,"line":190},[145,452,453],{},"    Thread: []\n",[145,455,456],{"class":147,"line":196},[145,457,458],{},"\u003C\u002Fslack_message>\n",[145,460,461],{"class":147,"line":202},[145,462,152],{"emptyLinePlaceholder":151},[145,464,465],{"class":147,"line":208},[145,466,467],{},"\u003Crequest_human_input>\n",[145,469,470],{"class":147,"line":214},[145,471,472],{},"    intent: \"request_human_input\"\n",[145,474,475],{"class":147,"line":220},[145,476,477],{},"    question: \"Would you like to proceed with deploying v1.2.3 to production?\"\n",[145,479,480],{"class":147,"line":225},[145,481,482],{},"    context: \"This is a production deployment that will affect live users.\"\n",[145,484,485],{"class":147,"line":231},[145,486,487],{},"    options: {\n",[145,489,490],{"class":147,"line":237},[145,491,492],{},"        urgency: \"high\"\n",[145,494,495],{"class":147,"line":243},[145,496,497],{},"        format: \"yes_no\"\n",[145,499,500],{"class":147,"line":249},[145,501,502],{},"    }\n",[145,504,505],{"class":147,"line":255},[145,506,507],{},"\u003C\u002Frequest_human_input>\n",[145,509,510],{"class":147,"line":261},[145,511,152],{"emptyLinePlaceholder":151},[145,513,514],{"class":147,"line":267},[145,515,516],{},"\u003Chuman_response>\n",[145,518,519],{"class":147,"line":273},[145,520,521],{},"    response: \"yes please proceed\"\n",[145,523,524],{"class":147,"line":279},[145,525,526],{},"    approved: true\n",[145,528,529],{"class":147,"line":285},[145,530,531],{},"    timestamp: \"2024-03-15T10:30:00Z\"\n",[145,533,535],{"class":147,"line":534},25,[145,536,537],{},"    user: \"alex@company.com\"\n",[145,539,541],{"class":147,"line":540},26,[145,542,543],{},"\u003C\u002Fhuman_response>\n",[145,545,547],{"class":147,"line":546},27,[145,548,152],{"emptyLinePlaceholder":151},[145,550,552],{"class":147,"line":551},28,[145,553,554],{},"\u003Cdeploy_backend>\n",[145,556,558],{"class":147,"line":557},29,[145,559,560],{},"    intent: \"deploy_backend\"\n",[145,562,564],{"class":147,"line":563},30,[145,565,566],{},"    tag: \"v1.2.3\"\n",[145,568,570],{"class":147,"line":569},31,[145,571,572],{},"    environment: \"production\"\n",[145,574,576],{"class":147,"line":575},32,[145,577,578],{},"\u003C\u002Fdeploy_backend>\n",[145,580,582],{"class":147,"line":581},33,[145,583,152],{"emptyLinePlaceholder":151},[145,585,587],{"class":147,"line":586},34,[145,588,589],{},"\u003Cdeploy_backend_result>\n",[145,591,593],{"class":147,"line":592},35,[145,594,595],{},"    status: \"success\"\n",[145,597,599],{"class":147,"line":598},36,[145,600,601],{},"    message: \"Deployment v1.2.3 to production completed successfully.\"\n",[145,603,605],{"class":147,"line":604},37,[145,606,531],{},[145,608,610],{"class":147,"line":609},38,[145,611,612],{},"\u003C\u002Fdeploy_backend_result>\n",[76,614,615],{},"Benefits:",[617,618,619,627,645,651,661],"ol",{},[620,621,622,626],"li",{},[623,624,625],"strong",{},"Clear Instructions",": Tools for different types of human contact allow for more specificity from the LLM",[620,628,629,632,633,636,637,640,641,644],{},[623,630,631],{},"Inner vs Outer Loop",": Enables agents workflows ",[623,634,635],{},"outside"," of the traditional chatGPT-style interface, where the control flow and context initialization may be ",[90,638,639],{},"Agent->Human"," rather than ",[90,642,643],{},"Human->Agent"," (think, agents kicked off by a cron or an event)",[620,646,647,650],{},[623,648,649],{},"Multiple Human Access",": Can easily track and coordinate input from different humans through structured events",[620,652,653,656,657,660],{},[623,654,655],{},"Multi-Agent",": Simple abstraction can be easily extended to support ",[90,658,659],{},"Agent->Agent"," requests and responses",[620,662,663,666,667,672],{},[623,664,665],{},"Durable",": Combined with ",[378,668,671],{"href":669,"rel":670},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-06-launch-pause-resume.md",[382],"factor 6 - launch\u002Fpause\u002Fresume with simple APIs",", this makes for durable, reliable, and introspectable multiplayer workflows",[76,674,675],{},[378,676,679],{"href":677,"rel":678},"https:\u002F\u002Ftheouterloop.substack.com\u002Fp\u002Fopenais-realtime-api-is-a-step-towards",[382],"More on Outer Loop Agents over here",[76,681,682],{},[82,683],{"alt":684,"src":685},"175-outer-loop-agents","https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fimg\u002F175-outer-loop-agents.png?raw=true",[76,687,688,689],{},"Works great with ",[378,690,693],{"href":691,"rel":692},"https:\u002F\u002Fgithub.com\u002Fhumanlayer\u002F12-factor-agents\u002Fblob\u002Fmain\u002Fcontent\u002Ffactor-11-trigger-from-anywhere.md",[382],"factor 11 - trigger from anywhere, meet users where they are",[695,696,697],"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":141,"searchDepth":155,"depth":155,"links":699},[700],{"id":73,"depth":161,"text":74},"md",{},"\u002Ffactor-7",{"title":52,"description":141},"U9Sh3Krs_R64SmQYFtVhlaiHpUUz_Kax6fw0P7Ukufs",[707,709,710,711,712,713,714,715,716,717,718,719,720,721,722],{"slug":5,"title":36,"chapterIndex":708},0,{"slug":7,"title":38,"chapterIndex":148},{"slug":9,"title":40,"chapterIndex":155},{"slug":11,"title":42,"chapterIndex":161},{"slug":13,"title":44,"chapterIndex":167},{"slug":15,"title":46,"chapterIndex":173},{"slug":17,"title":48,"chapterIndex":179},{"slug":19,"title":50,"chapterIndex":184},{"slug":21,"title":52,"chapterIndex":190},{"slug":23,"title":54,"chapterIndex":196},{"slug":25,"title":56,"chapterIndex":202},{"slug":27,"title":58,"chapterIndex":208},{"slug":29,"title":60,"chapterIndex":214},{"slug":31,"title":62,"chapterIndex":220},{"slug":33,"title":64,"chapterIndex":225},1788741638421]