1"use strict";(globalThis.webpackChunkgoose=globalThis.webpackChunkgoose||[]).push([[1797],{6909:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/rpi-openclaw-blogbanner-7c71d1a80441079767f7fd25b9e27385.png"},28453:(e,t,n)=>{n.d(t,{R:()=>o,x:()=>r});var s=n(96540);const a={},i=s.createContext(a);function o(e){const t=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),s.createElement(i.Provider,{value:t},e.children)}},36411:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/first-message-cfe473a84771921fd043039eeb3e47d2.png"},65456:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/altopenclaw-action-c0f148991ff2247bcbbe808eaf509cc0.png"},72416:(e,t,n)=>{n.d(t,{A:()=>s});const s=n.p+"assets/images/failure-screenshot-418cdd37e5d68e228fa57030aa9cc1bd.png"},73607:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>h});var s=n(83672),a=n(74848),i=n(28453);const o={title:"How I Used RPI to Build an OpenClaw Alternative",description:"Learn how I built a minimal, personal AI agent using goose and the RPI method.",image:"/img/blog/rpi-openclaw-blogbanner.png",authors:["rizel"]},r=void 0,l={authorsImageUrls:[void 0]},h=[{value:"Choosing RPI",id:"choosing-rpi",level:2},{value:"Research",id:"research",level:3},{value:"Plan",id:"plan",level:3},{value:"Implement",id:"implement",level:3},{value:"The Takeaway",id:"the-takeaway",level:2},{value:"Try It Out",id:"try-it-out",level:2}];function d(e){const t={a:"a",code:"code",h2:"h2",h3:"h3",hr:"hr",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components},{Head:s}=t;return s||function(e,t){throw new Error("Expected "+(t?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Head",!0),(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"How I Used RPI to Build an OpenClaw Alternative",src:n(6909).A+"",width:"1206",height:"633"})}),"\n",(0,a.jsxs)(t.p,{children:["Everyone on Tech Twitter has been buying Mac Minis, so they could run a local agentic tool called ",(0,a.jsx)(t.a,{href:"https://openclaw.ai/",children:"OpenClaw"}),". OpenClaw is a messaging-based AI assistant that connects to platforms such as Discord and Telegram allowing you to interact with an AI agent through DMs or @mentions. Under the hood, it uses an agent called Pi to execute tasks, browse the web, write code, and more."]}),"\n",(0,a.jsxs)(t.p,{children:["Seeing the hype made me want to get my hands dirty. I wanted to see if I could build a lite version for myself. I wanted something minimal that used ",(0,a.jsx)(t.a,{href:"https://github.com/aaif-goose/goose",children:"goose"})," as the engine instead of Pi. I tentatively dubbed it AltOpenClaw."]}),"\n",(0,a.jsx)(t.h2,{id:"choosing-rpi",children:"Choosing RPI"}),"\n",(0,a.jsx)(t.p,{children:"My usual move is to just jump in, start breaking things, and refactor as I go. I actually prefer the back and forth conversation with an agent because it helps me learn how the project works in real time. But when I tried that here, I hit a wall fast. goose did not naturally know what OpenClaw was, and it kept hallucinating how to use its own backend. It would forget context mid-conversation or suggest API calls that simply did not exist."}),"\n",(0,a.jsxs)(t.p,{children:["I realized I needed to change my approach. While I love the iterative learning process, I needed a way to give the agent a better foundation so our pair programming sessions actually made progress. I decided to try the ",(0,a.jsx)(t.a,{href:"/docs/tutorials/rpi",children:"RPI method (Research, Plan, Implement)"}),". This is a framework introduced by ",(0,a.jsx)(t.a,{href:"https://humanlayer.dev/",children:"HumanLayer"})," that trades raw speed for predictability. It is built into goose as a series of recipes. Since I did not fully understand the technical landscape myself, this investment in structure felt like the right move to help us both get on the same page."]}),"\n",(0,a.jsx)(t.hr,{}),"\n",(0,a.jsx)(t.h3,{id:"research",children:"Research"}),"\n",(0,a.jsx)(t.p,{children:"First, I needed goose to understand what I was building and whether it was even possible. I kicked things off with a detailed research prompt:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{children:"/research_codebase topic=\"learn what openclaw is, how people use it, \nand how it works. learn if goose can actually be used as a backend \nor if that's not yet possible; understand the port issues especially \nif you have an instance of goose that's running to help you build \nan agent that uses goose as a backend. learn if there will be any \nauth issues\"\n"})}),"\n",(0,a.jsx)(t.p,{children:"goose spawned multiple parallel subagents to investigate."}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.strong,{children:"Key findings from the research:"})}),"\n",(0,a.jsxs)(t.ul,{children:["\n",(0,a.jsxs)(t.li,{children:[(0,a.jsx)(t.strong,{children:"OpenClaw uses its own embedded agent runtime (Pi)"}),", not goose. This meant there was no existing integration to copy."]}),"\n",(0,a.jsxs)(t.li,{children:[(0,a.jsx)(t.strong,{children:"goose CAN be used as a backend!"})," The ",(0,a.jsx)(t.code,{children:"goosed"})," server exposes a full HTTP API."]}),"\n",(0,a.jsxs)(t.li,{children:[(0,a.jsx)(t.strong,{children:"Port conflicts are manageable."})," We just needed to run on a different port with ",(0,a.jsx)(t.code,{children:"GOOSE_PORT=3001"}),"."]}),"\n",(0,a.jsxs)(t.li,{children:[(0,a.jsx)(t.strong,{children:"Authentication is simple."})," We could pass a secret key in the ",(0,a.jsx)(t.code,{children:"X-Secret-Key"})," header."]}),"\n"]}),"\n",(0,a.jsxs)(t.p,{children:["The research also mapped out all the relevant API endpoints, such as ",(0,a.jsx)(t.code,{children:"POST /sessions"})," to create a new session and ",(0,a.jsx)(t.code,{children:"POST /sessions/{id}/reply"})," to handle the actual messaging."]}),"\n",(0,a.jsx)(t.hr,{}),"\n",(0,a.jsx)(t.h3,{id:"plan",children:"Plan"}),"\n",(0,a.jsx)(t.p,{children:"With the research complete, I asked goose to create an implementation plan. This is where we defined the personality and security of the bot:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{children:"/create_plan ticket-or-context=\"I want to build a Discord MCP server \nfor goose that replicates the popular features of OpenClaw but with \nbetter security. Core Features: Users can DM the bot or @ it in a \nchannel to give goose tasks. goose responds in Discord with results. \nSecurity requirements: Allowlist (only specific Discord user IDs can \ninteract), Approval flow (before goose executes any tool/action, the \nbot posts what it wants to do and waits for user approval), \nNon-allowlisted users get a polite 'you don't have access'\"\n"})}),"\n",(0,a.jsx)(t.p,{children:"goose analyzed the requirements and produced a detailed plan with four phases:"}),"\n",(0,a.jsxs)(t.ol,{children:["\n",(0,a.jsx)(t.li,{children:"Phase 1: Project Setup (Discord.js skeleton and allowlist)"}),"\n",(0,a.jsx)(t.li,{children:"Phase 2: goose HTTP Client (Connecting to the API and handling SSE streaming)"}
1),"\n",(0,a.jsx)(t.li,{children:"Phase 3: Tool Approval Flow (The UI for \u2705/\u274c reactions)"}),"\n",(0,a.jsx)(t.li,{children:"Phase 4: Polish & Error Handling (Slash commands and session management)"}),"\n"]}),"\n",(0,a.jsx)(t.p,{children:"I liked this phased approach because it gave us less to debug at each step. We could handle features in chunks rather than trying to fix everything at once."}),"\n",(0,a.jsx)(t.hr,{}),"\n",(0,a.jsx)(t.h3,{id:"implement",children:"Implement"}),"\n",(0,a.jsx)(t.p,{children:"With the plan in place, I gave the signal to start building:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{children:"/implement_plan start building\n"})}),"\n",(0,a.jsx)(t.p,{children:"The first two phases were surprisingly smooth. Within an hour, the bot was online and I could actually DM it. Seeing a Discord message trigger a goose session for the first time was a massive win."}),"\n",(0,a.jsx)(t.p,{children:"First, we tested if AltOpenClaw could respond to me with a joke!"}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"First successful message to the bot",src:n(36411).A+"",width:"1354",height:"998"})}),"\n",(0,a.jsx)(t.p,{children:"However, as every developer knows, it was not all perfect. We still ran into some classic real-world hurdles during implementation:"}),"\n",(0,a.jsxs)(t.ul,{children:["\n",(0,a.jsx)(t.li,{children:"The SSE (Server-Sent Events) format was different than we expected. We spent a good chunk of time debugging why the messages were not appearing until we realized the event structure was nested deeper than anticipated."}),"\n",(0,a.jsx)(t.li,{children:"My local path did not have npm properly mapped, which led to a brief detour."}),"\n",(0,a.jsx)(t.li,{children:"Discord has a strict limit on message length. If goose wrote a long script, the bot would just crash. We had to implement a chunking system on the fly."}),"\n"]}),"\n",(0,a.jsx)(t.p,{children:"Currently, the tool approval feature is still a work in progress. I actually got so excited that the core part of the project was working that I sat down to write this post before finishing the UI for the reactions."}),"\n",(0,a.jsx)(t.h2,{id:"the-takeaway",children:"The Takeaway"}),"\n",(0,a.jsx)(t.p,{children:"The RPI method felt like a superpower, even if it didn't magically delete every bug from the project. There is a big difference between fighting a hallucination and fighting a real technical challenge."}),"\n",(0,a.jsx)(t.p,{children:"When I didn't use RPI, goose hallucinated nonexistent endpoints and tried to build a complex MCP server when a simple HTTP API was all we needed. Those are the kinds of bugs that waste hours because you are chasing ghosts."}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"Before RPI: Debugging failures and hallucinations",src:n(72416).A+"",width:"1166",height:"646"})}),"\n",(0,a.jsx)(t.p,{children:"Instead, RPI helped us clear the conceptual fog so we could focus on real implementation details like SSE parsing and character limits."}),"\n",(0,a.jsx)(t.p,{children:"By forcing the agent to research first, it built up the context it was missing. It is a bit slower at the start (which I barely have patience for), but it turns the agent into a much more capable partner for that back and forth learning process I enjoy."}),"\n",(0,a.jsxs)(t.p,{children:["I even had AltOpenClaw push its own ",(0,a.jsx)(t.a,{href:"https://github.com/blackgirlbytes/discord-goose-bot",children:"repository"})," to GitHub."]}),"\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.img,{alt:"AltOpenClaw in action, completing a task",src:n(65456).A+"",width:"1988",height:"1558"})}),"\n",(0,a.jsx)(t.h2,{id:"try-it-out",children:"Try It Out"}),"\n",(0,a.jsxs)(t.p,{children:["If you want more reliability from your agent, give the ",(0,a.jsx)(t.a,{href:"/docs/tutorials/rpi",children:"RPI recipes"})," in goose a shot:"]}),"\n",(0,a.jsxs)(t.ul,{children:["\n",(0,a.jsx)(t.li,{children:(0,a.jsx)(t.code,{children:"/research_codebase"})}),"\n",(0,a.jsx)(t.li,{children:(0,a.jsx)(t.code,{children:"/create_plan"})}),"\n",(0,a.jsx)(t.li,{children:(0,a.jsx)(t.code,{children:"/implement_plan"})}),"\n",(0,a.jsx)(t.li,{children:(0,a.jsx)(t.code,{children:"/iterate_plan"})}),"\n"]}),"\n",(0,a.jsx)(t.p,{children:"Happy hacking!"}),"\n",(0,a.jsxs)(s,{children:[(0,a.jsx)("meta",{property:"og:title",content:"How I Used RPI to Build an OpenClaw Alternative"}),(0,a.jsx)("meta",{property:"og:type",content:"article"}),(0,a.jsx)("meta",{property:"og:url",content:"https://goose-docs.ai/blog/2026/02/06/rpi-openclaw-alternative"}),(0,a.jsx)("meta",{property:"og:description",content:"Learn how I built a minimal, personal AI agent using goose and the RPI method."}),(0,a.jsx)("meta",{property:"og:image",content:"https://goose-docs.ai/assets/images/blogbanner-7c71d1a80441079767f7fd25b9e27385.png"}),(0,a.jsx)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,a.jsx)("meta",{property:"twitter:domain",content:"goose-docs.ai"}),(0,a.jsx)("meta",{name:"twitter:title",content:"How I Used RPI to Build an OpenClaw Alternative"}),(0,a.jsx)("meta",{name:"twitter:description",content:"Learn how I built a minimal, personal AI agent using goose and the RPI method."}),(0,a.jsx)("meta",{name:"twitter:image",content:"https://goose-docs.ai/assets/images/blogbanner-7c71d1a80441079767f7fd25b9e27385.png"})]})]})}function c(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},83672:e=>{e.exports=JSON.parse('{"permalink":"/blog/2026/02/06/rpi-openclaw-alternative","source":"@site/blog/2026-02-06-rpi-openclaw-alternative/index.md","title":"How I Used RPI to Build an OpenClaw Alternative","description":"Learn how I built a minimal, personal AI agent using goose and the RPI method.","date":"2026-02-06T00:00:00.000Z","tags":[],"readingTime":6.24,"hasTruncateMarker":true,"authors":[{"name":"Rizel Scarlett","title":"Staff Developer Advocate","page":{"permalink":"/blog/authors/rizel"},"socials":{"x":"https://x.com/blackgirlbytes","github":"https://github.com/blackgirlbytes","bluesky":"https://bsky.app/profile/blackgirlbytes.bsky.social","linkedin":"https://www.linkedin.com/in/rizel-bobb-semple/"},"imageURL":"https://avatars.githubusercontent.com/u/22990146?v=4","key":"rizel"}],"frontMatter":{"title":"How I Used RPI to Build an OpenClaw Alternative","description":"Learn how I built a minimal, personal AI agent using goose and the RPI method.","image":"/img/blog/rpi-openclaw-blogbanner.png","authors":["rizel"]},"unlisted":false,"prevItem":{"title":"Level Up Your AI Game with rp-why","permalink":"/blog/2026/02/06/rp-why-skill"},"nextItem":{"title":"5 Tips for Building MCP Apps That Work","permalink":"/blog/2026/01/30/5-tips-building-mcp-apps"}}')}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.