{"id":694,"date":"2026-02-06T17:19:39","date_gmt":"2026-02-06T09:19:39","guid":{"rendered":"https:\/\/blog.itsmygo.uk\/2026\/02\/06\/nanoclaw-%e5%8e%9f%e5%a7%8b%e7%a2%bc%e8%a7%a3%e8%ae%80%ef%bc%9a%e7%94%a8%e6%a5%b5%e7%b0%a1%e6%9e%b6%e6%a7%8b%e6%89%93%e9%80%a0%e5%ae%89%e5%85%a8%e7%9a%84%e5%80%8b%e4%ba%ba-ai-%e5%8a%a9%e7%90%86\/"},"modified":"2026-02-06T17:19:39","modified_gmt":"2026-02-06T09:19:39","slug":"nanoclaw-%e5%8e%9f%e5%a7%8b%e7%a2%bc%e8%a7%a3%e8%ae%80%ef%bc%9a%e7%94%a8%e6%a5%b5%e7%b0%a1%e6%9e%b6%e6%a7%8b%e6%89%93%e9%80%a0%e5%ae%89%e5%85%a8%e7%9a%84%e5%80%8b%e4%ba%ba-ai-%e5%8a%a9%e7%90%86","status":"publish","type":"post","link":"https:\/\/blog.itsmygo.uk\/wordpress\/2026\/02\/06\/nanoclaw-%e5%8e%9f%e5%a7%8b%e7%a2%bc%e8%a7%a3%e8%ae%80%ef%bc%9a%e7%94%a8%e6%a5%b5%e7%b0%a1%e6%9e%b6%e6%a7%8b%e6%89%93%e9%80%a0%e5%ae%89%e5%85%a8%e7%9a%84%e5%80%8b%e4%ba%ba-ai-%e5%8a%a9%e7%90%86\/","title":{"rendered":"NanoClaw \u539f\u59cb\u78bc\u89e3\u8b80\uff1a\u7528\u6975\u7c21\u67b6\u69cb\u6253\u9020\u5b89\u5168\u7684\u500b\u4eba AI \u52a9\u7406"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">NanoClaw \u662f\u4e00\u500b\u53ea\u6709\u5e7e\u500b\u6838\u5fc3\u6a94\u6848\u7684\u500b\u4eba Claude \u52a9\u7406\uff0c\u900f\u904e WhatsApp \u8207\u4f60\u5c0d\u8a71\uff0c\u6bcf\u6b21\u5c0d\u8a71\u90fd\u5728\u7368\u7acb\u7684 Linux Container \u4e2d\u57f7\u884c\u3002\u5b83\u7684\u8a2d\u8a08\u54f2\u5b78\u76f4\u63a5\u6311\u6230\u4e86\u300c\u529f\u80fd\u8d8a\u591a\u8d8a\u597d\u300d\u7684\u5e38\u898b\u601d\u7dad\u2014\u2014\u6574\u500b\u7cfb\u7d71\u53ea\u6709\u4e00\u500b Node.js process\u3001\u4e00\u500b SQLite \u8cc7\u6599\u5eab\u3001\u4e00\u500b polling loop\uff0c\u537b\u5be6\u73fe\u4e86\u591a\u7fa4\u7d44\u9694\u96e2\u3001\u6392\u7a0b\u4efb\u52d9\u3001\u6a94\u6848\u7cfb\u7d71\u5b89\u5168\u7b49\u4f01\u696d\u7d1a\u529f\u80fd\u3002\u8b80\u5b8c\u9019\u4efd\u539f\u59cb\u78bc\uff0c\u4f60\u6703\u5b78\u5230\u5982\u4f55\u7528 Container \u9694\u96e2\u53d6\u4ee3\u61c9\u7528\u5c64\u6b0a\u9650\u6aa2\u67e5\u3001\u5982\u4f55\u900f\u904e filesystem-based IPC \u8b93\u6c99\u7bb1\u5167\u7684 agent \u8207\u5916\u754c\u5b89\u5168\u901a\u8a0a\uff0c\u4ee5\u53ca\u300cSkills over Features\u300d\u7684 AI-native \u64f4\u5c55\u6a21\u5f0f\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\">NanoClaw GitHub Repository<\/a> \u2014 \u5c08\u6848\u539f\u59cb\u78bc\uff0c\u5305\u542b\u672c\u6587\u89e3\u8b80\u7684\u6240\u6709\u8a2d\u8a08\u5be6\u4f5c<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5c08\u6848\u6982\u89bd<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">NanoClaw \u662f\u4e00\u500b\u904b\u884c\u5728 macOS\/Linux \u4e0a\u7684\u500b\u4eba Claude \u52a9\u7406\u3002\u4f7f\u7528\u8005\u900f\u904e WhatsApp \u50b3\u9001\u8a0a\u606f\uff0c\u7cfb\u7d71\u5c07\u8a0a\u606f\u8def\u7531\u5230 Claude Agent SDK\uff0c\u5728 Apple Container \u6216 Docker \u5bb9\u5668\u4e2d\u57f7\u884c agent \u56de\u8986\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u6280\u8853\u68e7<\/strong>\uff1aTypeScript \/ Node.js\u3001SQLite\uff08better-sqlite3\uff09\u3001WhatsApp Web\uff08baileys\uff09\u3001Claude Agent SDK\u3001Apple Container \/ Docker<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u898f\u6a21<\/strong>\uff1a\u6838\u5fc3 host process \u50c5 8 \u500b TypeScript \u6a94\u6848\uff08<code>src\/<\/code> \u76ee\u9304\uff09\uff0c\u5bb9\u5668\u5167 agent runner \u50c5 2 \u500b\u6a94\u6848\u3002\u6574\u500b codebase \u7684\u908f\u8f2f\u7a0b\u5f0f\u78bc\u4e0d\u8d85\u904e 1500 \u884c\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u67b6\u69cb\u7e3d\u89bd<\/h2>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><span><code class=\"hljs\">WhatsApp (baileys) \u2500\u2500\u25b6 SQLite \u2500\u2500\u25b6 Polling Loop \u2500\u2500\u25b6 Container (Claude Agent SDK) \u2500\u2500\u25b6 Response\n                                                        \u2502\n                                                        \u25bc\n                                                  IPC via Filesystem\n                                                        \u2502\n                                                        \u25bc\n                                              Host \u8655\u7406 messages\/tasks\n<\/code><\/span><\/pre><\/div>\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">nanoclaw\/\n\u251c\u2500\u2500 src\/                        <span class=\"hljs-comment\"># Host process\uff08\u5168\u90e8\u6838\u5fc3\u908f\u8f2f\uff09<\/span>\n\u2502   \u251c\u2500\u2500 index.ts                <span class=\"hljs-comment\"># \u5165\u53e3\uff1aWhatsApp \u9023\u7dda\u3001\u8a0a\u606f\u8def\u7531\u3001IPC watcher<\/span>\n\u2502   \u251c\u2500\u2500 container-runner.ts     <span class=\"hljs-comment\"># \u5bb9\u5668\u751f\u547d\u9031\u671f\uff1amount \u7d44\u88dd\u3001spawn\u3001\u8f38\u51fa\u89e3\u6790<\/span>\n\u2502   \u251c\u2500\u2500 db.ts                   <span class=\"hljs-comment\"># SQLite schema \u8207\u6240\u6709\u67e5\u8a62<\/span>\n\u2502   \u251c\u2500\u2500 task-scheduler.ts       <span class=\"hljs-comment\"># \u6392\u7a0b\u4efb\u52d9\u5f15\u64ce<\/span>\n\u2502   \u251c\u2500\u2500 mount-security.ts       <span class=\"hljs-comment\"># \u639b\u8f09\u5b89\u5168\u9a57\u8b49\uff08allowlist \u6a5f\u5236\uff09<\/span>\n\u2502   \u251c\u2500\u2500 config.ts               <span class=\"hljs-comment\"># \u5e38\u6578\u8207\u74b0\u5883\u8b8a\u6578<\/span>\n\u2502   \u251c\u2500\u2500 types.ts                <span class=\"hljs-comment\"># TypeScript \u578b\u5225\u5b9a\u7fa9<\/span>\n\u2502   \u2514\u2500\u2500 utils.ts                <span class=\"hljs-comment\"># JSON \u8b80\u5beb\u5de5\u5177<\/span>\n\u251c\u2500\u2500 container\/                  <span class=\"hljs-comment\"># \u5bb9\u5668\u6620\u50cf<\/span>\n\u2502   \u251c\u2500\u2500 Dockerfile              <span class=\"hljs-comment\"># Agent \u57f7\u884c\u74b0\u5883<\/span>\n\u2502   \u2514\u2500\u2500 agent-runner\/src\/       <span class=\"hljs-comment\"># \u5bb9\u5668\u5167\u7a0b\u5f0f\u78bc<\/span>\n\u2502       \u251c\u2500\u2500 index.ts            <span class=\"hljs-comment\"># Agent \u555f\u52d5\u5668\uff08Claude Agent SDK query\uff09<\/span>\n\u2502       \u2514\u2500\u2500 ipc-mcp.ts          <span class=\"hljs-comment\"># MCP Server\uff1aagent \u7684\u5c0d\u5916\u901a\u8a0a\u4ecb\u9762<\/span>\n\u251c\u2500\u2500 groups\/                     <span class=\"hljs-comment\"># \u6bcf\u500b\u7fa4\u7d44\u7684\u9694\u96e2\u8a18\u61b6\u7a7a\u9593<\/span>\n\u2502   \u251c\u2500\u2500 main\/CLAUDE.md          <span class=\"hljs-comment\"># \u4e3b\u983b\u9053 prompt\uff08\u6709\u7ba1\u7406\u54e1\u6b0a\u9650\uff09<\/span>\n\u2502   \u2514\u2500\u2500 <span class=\"hljs-keyword\">global<\/span>\/CLAUDE.md        <span class=\"hljs-comment\"># \u5168\u57df\u5171\u4eab prompt<\/span>\n\u2514\u2500\u2500 .claude\/skills\/             <span class=\"hljs-comment\"># Claude Code Skills\uff08\u64f4\u5c55\u6a5f\u5236\uff09<\/span>\n    \u251c\u2500\u2500 setup\/SKILL.md\n    \u251c\u2500\u2500 customize\/SKILL.md\n    \u251c\u2500\u2500 debug\/SKILL.md\n    \u2514\u2500\u2500 add-gmail\/SKILL.md\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">\u6838\u5fc3\u8a2d\u8a08\u89e3\u6790<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u4e00\uff1aSingle Process + Polling Loop \u53d6\u4ee3\u4e8b\u4ef6\u9a45\u52d5\u67b6\u69cb<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">NanoClaw \u7684\u6838\u5fc3\u662f\u4e00\u500b\u6975\u70ba\u7c21\u55ae\u7684 polling loop\uff1a\u6bcf 2 \u79d2\u5f9e SQLite \u6488\u53d6\u65b0\u8a0a\u606f\uff0c\u4f9d\u5e8f\u8655\u7406\u3002\u9019\u500b\u8a2d\u8a08\u523b\u610f\u907f\u958b\u4e86 event-driven \u67b6\u69cb\u7684\u8907\u96dc\u5ea6\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/src\/index.ts\"><code>src\/index.ts<\/code><\/a> \u2014 \u4e3b\u5165\u53e3\uff0c\u5305\u542b\u8a0a\u606f\u8def\u7531\u3001WhatsApp \u9023\u7dda\u548c IPC watcher \u4e09\u5927\u6838\u5fc3\u908f\u8f2f<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">startMessageLoop<\/span>(<span class=\"hljs-params\"><\/span>): <span class=\"hljs-title\">Promise<\/span>&lt;<span class=\"hljs-title\">void<\/span>&gt; <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (messageLoopRunning) {\n    logger.debug(<span class=\"hljs-string\">'Message loop already running, skipping duplicate start'<\/span>);\n    <span class=\"hljs-keyword\">return<\/span>;\n  }\n  messageLoopRunning = <span class=\"hljs-literal\">true<\/span>;\n\n  <span class=\"hljs-keyword\">while<\/span> (<span class=\"hljs-literal\">true<\/span>) {\n    <span class=\"hljs-keyword\">try<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> jids = <span class=\"hljs-built_in\">Object<\/span>.keys(registeredGroups);\n      <span class=\"hljs-keyword\">const<\/span> { messages } = getNewMessages(jids, lastTimestamp, ASSISTANT_NAME);\n\n      <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> msg <span class=\"hljs-keyword\">of<\/span> messages) {\n        <span class=\"hljs-keyword\">try<\/span> {\n          <span class=\"hljs-keyword\">await<\/span> processMessage(msg);\n          <span class=\"hljs-comment\">\/\/ Only advance timestamp after successful processing for at-least-once delivery<\/span>\n          lastTimestamp = msg.timestamp;\n          saveState();\n        } <span class=\"hljs-keyword\">catch<\/span> (err) {\n          logger.error({ err, <span class=\"hljs-attr\">msg<\/span>: msg.id }, <span class=\"hljs-string\">'Error processing message, will retry'<\/span>);\n          <span class=\"hljs-keyword\">break<\/span>;  <span class=\"hljs-comment\">\/\/ Stop processing this batch - failed message will be retried next loop<\/span>\n        }\n      }\n    } <span class=\"hljs-keyword\">catch<\/span> (err) {\n      logger.error({ err }, <span class=\"hljs-string\">'Error in message loop'<\/span>);\n    }\n    <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve<\/span>) =&gt;<\/span> setTimeout(resolve, POLL_INTERVAL));\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u9019\u6bb5\u7a0b\u5f0f\u78bc\u4e2d\u6709\u5e7e\u500b\u503c\u5f97\u6ce8\u610f\u7684\u8a2d\u8a08\u6c7a\u7b56\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n\n<li><strong>At-least-once delivery<\/strong>\uff1a<code>lastTimestamp<\/code> \u53ea\u5728\u8a0a\u606f\u6210\u529f\u8655\u7406\u5f8c\u624d\u524d\u9032\u3002\u5982\u679c\u8655\u7406\u5931\u6557\uff0c\u4e0b\u6b21 loop \u6703\u91cd\u8a66\u540c\u4e00\u5247\u8a0a\u606f\u3002<\/li>\n\n\n<li><strong>Guard flag \u9632\u6b62\u91cd\u8907\u555f\u52d5<\/strong>\uff1a<code>messageLoopRunning<\/code> \u78ba\u4fdd WhatsApp \u91cd\u9023\u6642\u4e0d\u6703\u7522\u751f\u591a\u500b polling loop\u3002<\/li>\n\n\n<li><strong>Sequential processing<\/strong>\uff1a\u8a0a\u606f\u56b4\u683c\u4f9d\u5e8f\u8655\u7406\uff0c<code>break<\/code> \u4e2d\u65b7\u6574\u500b batch \u800c\u975e\u8df3\u904e\u5931\u6557\u8a0a\u606f\u2014\u2014\u4fdd\u8b49\u8a0a\u606f\u9806\u5e8f\u3002<\/li>\n\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">\u9019\u7a2e\u300c\u7b28\u65b9\u6cd5\u300d\u7684\u53ef\u9760\u6027\u9060\u9ad8\u65bc\u8907\u96dc\u7684 message queue \u7cfb\u7d71\uff0c\u56e0\u70ba\u72c0\u614b\u6a5f\u975e\u5e38\u7c21\u55ae\uff1a\u53ea\u6709\u4e00\u500b timestamp \u548c\u4e00\u500b while loop\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u4e8c\uff1aContainer \u9694\u96e2\u4f5c\u70ba\u4e3b\u8981\u5b89\u5168\u908a\u754c<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">NanoClaw \u6700\u6838\u5fc3\u7684\u8a2d\u8a08\u7406\u5ff5\u662f\uff1a<strong>\u5b89\u5168\u4e0d\u9760\u61c9\u7528\u5c64\u6b0a\u9650\u6aa2\u67e5\uff0c\u9760 OS \u7d1a\u9694\u96e2<\/strong>\u3002\u6bcf\u6b21 agent \u57f7\u884c\u90fd\u5728\u4e00\u500b\u5168\u65b0\u7684 Container \u4e2d\uff0c\u53ea\u6709\u660e\u78ba mount \u7684\u76ee\u9304\u624d\u5c0d agent \u53ef\u898b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/src\/container-runner.ts\"><code>src\/container-runner.ts<\/code><\/a> \u2014 \u5bb9\u5668\u751f\u547d\u9031\u671f\u7ba1\u7406\uff0c\u7d44\u88dd volume mounts \u4e26 spawn \u5bb9\u5668 process<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><span><code class=\"hljs\">function buildVolumeMounts(\n  group: RegisteredGroup,\n  isMain: boolean,\n): VolumeMount&#91;] {\n  const mounts: VolumeMount&#91;] = &#91;];\n\n  if (isMain) {\n    \/\/ Main gets the entire project root mounted\n    mounts.push({\n      hostPath: projectRoot,\n      containerPath: '\/workspace\/project',\n      readonly: false,\n    });\n    mounts.push({\n      hostPath: path.join(GROUPS_DIR, group.folder),\n      containerPath: '\/workspace\/group',\n      readonly: false,\n    });\n  } else {\n    \/\/ Other groups only get their own folder\n    mounts.push({\n      hostPath: path.join(GROUPS_DIR, group.folder),\n      containerPath: '\/workspace\/group',\n      readonly: false,\n    });\n    \/\/ Global memory directory (read-only for non-main)\n    const globalDir = path.join(GROUPS_DIR, 'global');\n    if (fs.existsSync(globalDir)) {\n      mounts.push({\n        hostPath: globalDir,\n        containerPath: '\/workspace\/global',\n        readonly: true,\n      });\n    }\n  }\n\n  \/\/ Per-group IPC namespace: each group gets its own IPC directory\n  const groupIpcDir = path.join(DATA_DIR, 'ipc', group.folder);\n  mounts.push({\n    hostPath: groupIpcDir,\n    containerPath: '\/workspace\/ipc',\n    readonly: false,\n  });\n  \/\/ ...\n}\n<\/code><\/span><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u9019\u88e1\u7684\u8a2d\u8a08\u5dee\u7570\u975e\u5e38\u9bae\u660e\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>Main group<\/strong>\uff1a\u639b\u8f09\u6574\u500b\u5c08\u6848\u76ee\u9304\uff0c\u6709\u5b8c\u6574\u8b80\u5beb\u6b0a\u9650\u2014\u2014\u56e0\u70ba\u5b83\u662f\u4f7f\u7528\u8005\u81ea\u5df1\u7684 self-chat\uff0c\u662f trusted<\/li>\n\n\n<li><strong>Non-main groups<\/strong>\uff1a\u53ea\u80fd\u770b\u5230\u81ea\u5df1\u7684\u8cc7\u6599\u593e\u548c read-only \u7684\u5168\u57df\u8a18\u61b6\u2014\u2014\u56e0\u70ba\u7fa4\u7d44\u4e2d\u53ef\u80fd\u6709\u5176\u4ed6\u4eba\uff0c\u662f untrusted<\/li>\n\n\n<li><strong>IPC \u76ee\u9304\u6309\u7fa4\u7d44\u9694\u96e2<\/strong>\uff1a\u6bcf\u500b\u7fa4\u7d44\u6709\u81ea\u5df1\u7684 <code>\/workspace\/ipc<\/code>\uff0c\u7121\u6cd5\u8de8\u7fa4\u7d44\u901a\u8a0a<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u9019\u500b\u5206\u5c64\u4fe1\u4efb\u6a21\u578b\u4e0d\u9700\u8981\u4efb\u4f55\u61c9\u7528\u5c64\u7684 <code>if (user.hasPermission(...))<\/code> \u6aa2\u67e5\u3002Agent \u5728\u5bb9\u5668\u88e1\u60f3\u505a\u4ec0\u9ebc\u90fd\u884c\u2014\u2014\u4f46\u5b83\u7684\u4e16\u754c\u5c31\u53ea\u6709\u90a3\u5e7e\u500b mount point\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u4e09\uff1aFilesystem-based IPC\u2014\u2014\u8b93\u6c99\u7bb1 Agent \u5b89\u5168\u5730\u8207\u5916\u754c\u901a\u8a0a<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Container \u5167\u7684 agent \u7121\u6cd5\u76f4\u63a5\u5b58\u53d6 host \u4e0a\u7684 WhatsApp \u9023\u7dda\u6216 SQLite \u8cc7\u6599\u5eab\u3002NanoClaw \u8a2d\u8a08\u4e86\u4e00\u5957\u57fa\u65bc filesystem \u7684 IPC \u5354\u8b70\uff1aagent \u5c07\u64cd\u4f5c\u5beb\u6210 JSON \u6a94\u6848\u5230 IPC \u76ee\u9304\uff0chost process \u5b9a\u671f\u6383\u63cf\u4e26\u57f7\u884c\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/container\/agent-runner\/src\/ipc-mcp.ts\"><code>container\/agent-runner\/src\/ipc-mcp.ts<\/code><\/a> \u2014 \u5bb9\u5668\u5167\u7684 MCP Server\uff0c\u5c07 agent \u7684\u64cd\u4f5c\u610f\u5716\u5e8f\u5217\u5316\u70ba JSON \u6a94\u6848<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">writeIpcFile<\/span>(<span class=\"hljs-params\">dir: string, data: object<\/span>): <span class=\"hljs-title\">string<\/span> <\/span>{\n  fs.mkdirSync(dir, { <span class=\"hljs-attr\">recursive<\/span>: <span class=\"hljs-literal\">true<\/span> });\n  <span class=\"hljs-keyword\">const<\/span> filename = <span class=\"hljs-string\">`<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">Date<\/span>.now()}<\/span>-<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">Math<\/span>.random().toString(<span class=\"hljs-number\">36<\/span>).slice(<span class=\"hljs-number\">2<\/span>, <span class=\"hljs-number\">8<\/span>)}<\/span>.json`<\/span>;\n  <span class=\"hljs-keyword\">const<\/span> filepath = path.join(dir, filename);\n\n  <span class=\"hljs-comment\">\/\/ Atomic write: temp file then rename<\/span>\n  <span class=\"hljs-keyword\">const<\/span> tempPath = <span class=\"hljs-string\">`<span class=\"hljs-subst\">${filepath}<\/span>.tmp`<\/span>;\n  fs.writeFileSync(tempPath, <span class=\"hljs-built_in\">JSON<\/span>.stringify(data, <span class=\"hljs-literal\">null<\/span>, <span class=\"hljs-number\">2<\/span>));\n  fs.renameSync(tempPath, filepath);\n  <span class=\"hljs-keyword\">return<\/span> filename;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u800c host \u7aef\u7684 IPC watcher \u6703\u9a57\u8b49\u6bcf\u500b\u64cd\u4f5c\u7684\u6388\u6b0a\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/src\/index.ts\"><code>src\/index.ts<\/code><\/a> \u2014 IPC watcher \u4ee5\u7fa4\u7d44\u76ee\u9304\u540d\u7a31\u70ba\u8eab\u4efd\u8b58\u5225\uff0c\u9a57\u8b49\u64cd\u4f5c\u6388\u6b0a<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ Authorization: verify this group can send to this chatJid<\/span>\n<span class=\"hljs-keyword\">const<\/span> targetGroup = registeredGroups&#91;data.chatJid];\n<span class=\"hljs-keyword\">if<\/span> (isMain || (targetGroup &amp;&amp; targetGroup.folder === sourceGroup)) {\n  <span class=\"hljs-keyword\">await<\/span> sendMessage(data.chatJid, <span class=\"hljs-string\">`<span class=\"hljs-subst\">${ASSISTANT_NAME}<\/span>: <span class=\"hljs-subst\">${data.text}<\/span>`<\/span>);\n} <span class=\"hljs-keyword\">else<\/span> {\n  logger.warn(\n    { <span class=\"hljs-attr\">chatJid<\/span>: data.chatJid, sourceGroup },\n    <span class=\"hljs-string\">'Unauthorized IPC message attempt blocked'<\/span>,\n  );\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u9019\u500b\u8a2d\u8a08\u7684\u7cbe\u5999\u4e4b\u8655\u5728\u65bc\uff1a<strong>\u7fa4\u7d44\u7684\u8eab\u4efd\u4e0d\u7531 IPC payload \u6c7a\u5b9a\uff0c\u800c\u7531 IPC \u76ee\u9304\u8def\u5f91\u6c7a\u5b9a<\/strong>\u3002\u56e0\u70ba\u6bcf\u500b\u7fa4\u7d44\u7684 IPC \u76ee\u9304\u662f\u5206\u958b mount \u7684\uff0cagent \u7121\u6cd5\u507d\u9020\u8eab\u4efd\u2014\u2014\u5b83\u53ea\u80fd\u5beb\u5165 <code>\/workspace\/ipc<\/code>\uff0c\u800c host \u77e5\u9053\u9019\u500b\u76ee\u9304\u5c0d\u61c9\u54ea\u500b\u7fa4\u7d44\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u56db\uff1aSentinel Markers \u89e3\u6c7a\u5bb9\u5668\u8f38\u51fa\u89e3\u6790\u554f\u984c<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Container \u7684 stdout \u88e1\u6df7\u5408\u4e86 agent \u7684 debug log \u548c\u6700\u7d42\u7d50\u679c\u3002NanoClaw \u7528\u4e00\u5c0d\u300c\u54e8\u5175\u6a19\u8a18\u300d\u4f86\u53ef\u9760\u5730\u63d0\u53d6 JSON \u8f38\u51fa\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/src\/container-runner.ts\"><code>src\/container-runner.ts<\/code><\/a> \u2014 \u7528 sentinel markers \u5f9e\u6df7\u96dc\u7684 stdout \u4e2d\u7cbe\u78ba\u63d0\u53d6\u7d50\u69cb\u5316\u8f38\u51fa<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> OUTPUT_START_MARKER = <span class=\"hljs-string\">'---NANOCLAW_OUTPUT_START---'<\/span>;\n<span class=\"hljs-keyword\">const<\/span> OUTPUT_END_MARKER = <span class=\"hljs-string\">'---NANOCLAW_OUTPUT_END---'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Extract JSON between sentinel markers for robust parsing<\/span>\n<span class=\"hljs-keyword\">const<\/span> startIdx = stdout.indexOf(OUTPUT_START_MARKER);\n<span class=\"hljs-keyword\">const<\/span> endIdx = stdout.indexOf(OUTPUT_END_MARKER);\n\n<span class=\"hljs-keyword\">let<\/span> jsonLine: string;\n<span class=\"hljs-keyword\">if<\/span> (startIdx !== <span class=\"hljs-number\">-1<\/span> &amp;&amp; endIdx !== <span class=\"hljs-number\">-1<\/span> &amp;&amp; endIdx &gt; startIdx) {\n  jsonLine = stdout.slice(startIdx + OUTPUT_START_MARKER.length, endIdx).trim();\n} <span class=\"hljs-keyword\">else<\/span> {\n  <span class=\"hljs-comment\">\/\/ Fallback: last non-empty line (backwards compatibility)<\/span>\n  <span class=\"hljs-keyword\">const<\/span> lines = stdout.trim().split(<span class=\"hljs-string\">'\\n'<\/span>);\n  jsonLine = lines&#91;lines.length - <span class=\"hljs-number\">1<\/span>];\n}\n\n<span class=\"hljs-keyword\">const<\/span> output: ContainerOutput = <span class=\"hljs-built_in\">JSON<\/span>.parse(jsonLine);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u9019\u662f\u4e00\u500b\u52d9\u5be6\u7684\u5de5\u7a0b\u6c7a\u7b56\uff1a\u8207\u5176\u8981\u6c42\u5bb9\u5668\u5167\u6240\u6709 log \u8d70 stderr\uff08\u5f88\u96e3\u63a7\u5236\u7b2c\u4e09\u65b9\u5eab\u7684\u884c\u70ba\uff09\uff0c\u4e0d\u5982\u5728 stdout \u4e2d\u7528\u660e\u78ba\u7684\u754c\u7dda\u6a19\u8a18\u4f86\u5340\u5206\u300c\u96dc\u8a0a\u300d\u548c\u300c\u8a0a\u865f\u300d\u3002\u540c\u6642\u4fdd\u7559\u4e86 fallback \u6a5f\u5236\u78ba\u4fdd\u5411\u5f8c\u76f8\u5bb9\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u4e94\uff1a\u5916\u90e8 Allowlist \u7684\u639b\u8f09\u5b89\u5168\u6a21\u578b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">NanoClaw \u7684 mount security \u8a2d\u8a08\u4e86\u4e00\u500b\u9632\u7be1\u6539\u6a5f\u5236\uff1aallowlist \u5b58\u653e\u5728 <code>~\/.config\/nanoclaw\/mount-allowlist.json<\/code>\u2014\u2014\u5728\u5c08\u6848\u76ee\u9304\u4e4b\u5916\uff0c\u6c38\u9060\u4e0d\u6703\u88ab mount \u9032\u4efb\u4f55\u5bb9\u5668\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/src\/mount-security.ts\"><code>src\/mount-security.ts<\/code><\/a> \u2014 \u639b\u8f09\u5b89\u5168\u9a57\u8b49\uff0c\u5305\u542b symlink \u89e3\u6790\u3001blocked patterns \u548c\u591a\u5c64\u6388\u6b0a\u908f\u8f2f<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> DEFAULT_BLOCKED_PATTERNS = &#91;\n  <span class=\"hljs-string\">'.ssh'<\/span>, <span class=\"hljs-string\">'.gnupg'<\/span>, <span class=\"hljs-string\">'.gpg'<\/span>, <span class=\"hljs-string\">'.aws'<\/span>, <span class=\"hljs-string\">'.azure'<\/span>, <span class=\"hljs-string\">'.gcloud'<\/span>,\n  <span class=\"hljs-string\">'.kube'<\/span>, <span class=\"hljs-string\">'.docker'<\/span>, <span class=\"hljs-string\">'credentials'<\/span>, <span class=\"hljs-string\">'.env'<\/span>, <span class=\"hljs-string\">'.netrc'<\/span>,\n  <span class=\"hljs-string\">'.npmrc'<\/span>, <span class=\"hljs-string\">'.pypirc'<\/span>, <span class=\"hljs-string\">'id_rsa'<\/span>, <span class=\"hljs-string\">'id_ed25519'<\/span>,\n  <span class=\"hljs-string\">'private_key'<\/span>, <span class=\"hljs-string\">'.secret'<\/span>,\n];\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">findAllowedRoot<\/span>(<span class=\"hljs-params\">realPath: string, allowedRoots: AllowedRoot&#91;]<\/span>): <span class=\"hljs-title\">AllowedRoot<\/span> | <span class=\"hljs-title\">null<\/span> <\/span>{\n  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> root <span class=\"hljs-keyword\">of<\/span> allowedRoots) {\n    <span class=\"hljs-keyword\">const<\/span> expandedRoot = expandPath(root.path);\n    <span class=\"hljs-keyword\">const<\/span> realRoot = getRealPath(expandedRoot);\n    <span class=\"hljs-keyword\">if<\/span> (realRoot === <span class=\"hljs-literal\">null<\/span>) <span class=\"hljs-keyword\">continue<\/span>;\n\n    <span class=\"hljs-comment\">\/\/ Check if realPath is under realRoot<\/span>\n    <span class=\"hljs-keyword\">const<\/span> relative = path.relative(realRoot, realPath);\n    <span class=\"hljs-keyword\">if<\/span> (!relative.startsWith(<span class=\"hljs-string\">'..'<\/span>) &amp;&amp; !path.isAbsolute(relative)) {\n      <span class=\"hljs-keyword\">return<\/span> root;\n    }\n  }\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">null<\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u9a57\u8b49\u6d41\u7a0b\u6709\u4e09\u5c64\u9632\u8b77\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n\n<li><strong>Blocked patterns<\/strong>\uff1a\u7121\u8ad6\u8a2d\u5b9a\u5982\u4f55\uff0c<code>.ssh<\/code>\u3001<code>.env<\/code> \u7b49\u654f\u611f\u8def\u5f91\u6c38\u9060\u88ab\u963b\u64cb<\/li>\n\n\n<li><strong>Symlink resolution<\/strong>\uff1a\u7528 <code>fs.realpathSync()<\/code> \u89e3\u6790\u771f\u5be6\u8def\u5f91\uff0c\u9632\u6b62 symlink traversal attack<\/li>\n\n\n<li><strong>Allowed roots<\/strong>\uff1a\u53ea\u6709\u5728\u5141\u8a31\u7684\u6839\u76ee\u9304\u4e0b\u7684\u8def\u5f91\u624d\u80fd\u88ab mount<\/li>\n\n\n<li><strong>Non-main read-only<\/strong>\uff1a\u975e\u4e3b\u7fa4\u7d44\u7684\u984d\u5916\u639b\u8f09\u5f37\u5236\u70ba read-only<\/li>\n\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">\u9019\u500b\u8a2d\u8a08\u7684\u6838\u5fc3\u539f\u5247\u662f\uff1a<strong>\u5b89\u5168\u8a2d\u5b9a\u653e\u5728 agent \u4e0d\u53ef\u89f8\u53ca\u7684\u5730\u65b9<\/strong>\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u516d\uff1aMCP Server \u4f5c\u70ba Agent \u7684\u80fd\u529b\u908a\u754c<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u5bb9\u5668\u5167\u7684 agent \u900f\u904e Claude Agent SDK \u7684 MCP\uff08Model Context Protocol\uff09\u6a5f\u5236\u7372\u5f97\u80fd\u529b\u3002NanoClaw \u7528 <code>createSdkMcpServer<\/code> \u5b9a\u7fa9\u4e86\u4e00\u7d44\u7cbe\u5fc3\u8a2d\u8a08\u7684\u5de5\u5177\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/container\/agent-runner\/src\/ipc-mcp.ts\"><code>container\/agent-runner\/src\/ipc-mcp.ts<\/code><\/a> \u2014 \u5b9a\u7fa9 agent \u53ef\u7528\u7684 MCP tools\uff0c\u6bcf\u500b tool \u90fd\u5e36\u6709\u8a73\u7d30\u7684\u4f7f\u7528\u6307\u5f15<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createIpcMcp<\/span>(<span class=\"hljs-params\">ctx: IpcMcpContext<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> createSdkMcpServer({\n    <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'nanoclaw'<\/span>,\n    <span class=\"hljs-attr\">version<\/span>: <span class=\"hljs-string\">'1.0.0'<\/span>,\n    <span class=\"hljs-attr\">tools<\/span>: &#91;\n      tool(<span class=\"hljs-string\">'send_message'<\/span>, <span class=\"hljs-string\">'Send a message to the current WhatsApp group...'<\/span>, {\n        <span class=\"hljs-attr\">text<\/span>: z.string().describe(<span class=\"hljs-string\">'The message text to send'<\/span>)\n      }, <span class=\"hljs-keyword\">async<\/span> (args) =&gt; {\n        <span class=\"hljs-keyword\">const<\/span> data = { <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">'message'<\/span>, chatJid, <span class=\"hljs-attr\">text<\/span>: args.text, ... };\n        <span class=\"hljs-keyword\">const<\/span> filename = writeIpcFile(MESSAGES_DIR, data);\n        <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">content<\/span>: &#91;{ <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">'text'<\/span>, <span class=\"hljs-attr\">text<\/span>: <span class=\"hljs-string\">`Message queued for delivery (<span class=\"hljs-subst\">${filename}<\/span>)`<\/span> }] };\n      }),\n\n      tool(<span class=\"hljs-string\">'schedule_task'<\/span>,\n        <span class=\"hljs-string\">`Schedule a recurring or one-time task...\n         CONTEXT MODE - Choose based on task type:\n         \u2022 \"group\": Task runs in the group's conversation context...\n         \u2022 \"isolated\": Task runs in a fresh session...`<\/span>,\n        { <span class=\"hljs-attr\">prompt<\/span>: z.string(), <span class=\"hljs-attr\">schedule_type<\/span>: z.enum(&#91;<span class=\"hljs-string\">'cron'<\/span>, <span class=\"hljs-string\">'interval'<\/span>, <span class=\"hljs-string\">'once'<\/span>]), ... },\n        <span class=\"hljs-keyword\">async<\/span> (args) =&gt; { <span class=\"hljs-comment\">\/* validation + IPC write *\/<\/span> }\n      ),\n      <span class=\"hljs-comment\">\/\/ list_tasks, pause_task, resume_task, cancel_task, register_group...<\/span>\n    ]\n  });\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u6ce8\u610f <code>schedule_task<\/code> \u5de5\u5177\u7684 description \u6709\u591a\u9577\u2014\u2014\u5b83\u5305\u542b\u4e86 context mode \u7684\u9078\u64c7\u6307\u5f15\u3001cron \u8868\u9054\u5f0f\u683c\u5f0f\u8aaa\u660e\u3001\u6642\u5340\u8655\u7406\u6ce8\u610f\u4e8b\u9805\u3002\u9019\u4e9b description \u672c\u8cea\u4e0a\u662f\u5beb\u7d66 Claude \u770b\u7684\u300c\u4f7f\u7528\u624b\u518a\u300d\uff0c\u76f4\u63a5\u5f71\u97ff agent \u7684\u884c\u70ba\u54c1\u8cea\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u800c agent runner \u7684 <code>allowedTools<\/code> \u8a2d\u5b9a\u7cbe\u78ba\u63a7\u5236\u4e86 agent \u53ef\u7528\u7684\u80fd\u529b\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/container\/agent-runner\/src\/index.ts\"><code>container\/agent-runner\/src\/index.ts<\/code><\/a> \u2014 Agent \u555f\u52d5\u908f\u8f2f\uff0c\u914d\u7f6e Claude Agent SDK \u7684 query \u53c3\u6578<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">for<\/span> <span class=\"hljs-keyword\">await<\/span> (<span class=\"hljs-keyword\">const<\/span> message <span class=\"hljs-keyword\">of<\/span> query({\n  prompt,\n  <span class=\"hljs-attr\">options<\/span>: {\n    <span class=\"hljs-attr\">cwd<\/span>: <span class=\"hljs-string\">'\/workspace\/group'<\/span>,\n    <span class=\"hljs-attr\">resume<\/span>: input.sessionId,\n    <span class=\"hljs-attr\">allowedTools<\/span>: &#91;\n      <span class=\"hljs-string\">'Bash'<\/span>, <span class=\"hljs-string\">'Read'<\/span>, <span class=\"hljs-string\">'Write'<\/span>, <span class=\"hljs-string\">'Edit'<\/span>, <span class=\"hljs-string\">'Glob'<\/span>, <span class=\"hljs-string\">'Grep'<\/span>,\n      <span class=\"hljs-string\">'WebSearch'<\/span>, <span class=\"hljs-string\">'WebFetch'<\/span>,\n      <span class=\"hljs-string\">'mcp__nanoclaw__*'<\/span>\n    ],\n    <span class=\"hljs-attr\">permissionMode<\/span>: <span class=\"hljs-string\">'bypassPermissions'<\/span>,\n    <span class=\"hljs-attr\">allowDangerouslySkipPermissions<\/span>: <span class=\"hljs-literal\">true<\/span>,\n    <span class=\"hljs-attr\">settingSources<\/span>: &#91;<span class=\"hljs-string\">'project'<\/span>],\n    <span class=\"hljs-attr\">mcpServers<\/span>: { <span class=\"hljs-attr\">nanoclaw<\/span>: ipcMcp },\n    <span class=\"hljs-attr\">hooks<\/span>: { <span class=\"hljs-attr\">PreCompact<\/span>: &#91;{ <span class=\"hljs-attr\">hooks<\/span>: &#91;createPreCompactHook()] }] }\n  }\n})) { ... }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\"><code>bypassPermissions<\/code> + <code>allowDangerouslySkipPermissions<\/code> \u770b\u8d77\u4f86\u5f88\u5371\u96aa\u2014\u2014\u4f46\u56e0\u70ba agent \u5728\u5bb9\u5668\u5167\uff0cBash \u547d\u4ee4\u53ea\u80fd\u5f71\u97ff\u5bb9\u5668\u74b0\u5883\u3002\u9019\u662f\u300cContainer \u9694\u96e2\u53d6\u4ee3\u6b0a\u9650\u6aa2\u67e5\u300d\u7406\u5ff5\u7684\u76f4\u63a5\u9ad4\u73fe\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u4e03\uff1aPreCompact Hook \u5be6\u73fe\u5c0d\u8a71\u8a18\u61b6\u6b78\u6a94<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">NanoClaw \u5229\u7528 Claude Agent SDK \u7684 Hook \u6a5f\u5236\uff0c\u5728\u5c0d\u8a71\u88ab\u58d3\u7e2e\u524d\u81ea\u52d5\u6b78\u6a94\u5b8c\u6574 transcript\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/container\/agent-runner\/src\/index.ts\"><code>container\/agent-runner\/src\/index.ts<\/code><\/a> \u2014 PreCompact Hook \u5c07\u5b8c\u6574\u5c0d\u8a71\u6b78\u6a94\u70ba Markdown \u6a94\u6848<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createPreCompactHook<\/span>(<span class=\"hljs-params\"><\/span>): <span class=\"hljs-title\">HookCallback<\/span> <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">async<\/span> (input, _toolUseId, _context) =&gt; {\n    <span class=\"hljs-keyword\">const<\/span> preCompact = input <span class=\"hljs-keyword\">as<\/span> PreCompactHookInput;\n    <span class=\"hljs-keyword\">const<\/span> transcriptPath = preCompact.transcript_path;\n\n    <span class=\"hljs-keyword\">if<\/span> (!transcriptPath || !fs.existsSync(transcriptPath)) <span class=\"hljs-keyword\">return<\/span> {};\n\n    <span class=\"hljs-keyword\">const<\/span> content = fs.readFileSync(transcriptPath, <span class=\"hljs-string\">'utf-8'<\/span>);\n    <span class=\"hljs-keyword\">const<\/span> messages = parseTranscript(content);\n\n    <span class=\"hljs-keyword\">const<\/span> summary = getSessionSummary(sessionId, transcriptPath);\n    <span class=\"hljs-keyword\">const<\/span> name = summary ? sanitizeFilename(summary) : generateFallbackName();\n\n    <span class=\"hljs-keyword\">const<\/span> conversationsDir = <span class=\"hljs-string\">'\/workspace\/group\/conversations'<\/span>;\n    fs.mkdirSync(conversationsDir, { <span class=\"hljs-attr\">recursive<\/span>: <span class=\"hljs-literal\">true<\/span> });\n\n    <span class=\"hljs-keyword\">const<\/span> date = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Date<\/span>().toISOString().split(<span class=\"hljs-string\">'T'<\/span>)&#91;<span class=\"hljs-number\">0<\/span>];\n    <span class=\"hljs-keyword\">const<\/span> filename = <span class=\"hljs-string\">`<span class=\"hljs-subst\">${date}<\/span>-<span class=\"hljs-subst\">${name}<\/span>.md`<\/span>;\n    fs.writeFileSync(filePath, markdown);\n    <span class=\"hljs-keyword\">return<\/span> {};\n  };\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u9019\u89e3\u6c7a\u4e86\u4e00\u500b\u5be6\u969b\u554f\u984c\uff1aClaude \u7684 context window \u6709\u9650\uff0c\u9577\u5c0d\u8a71\u6703\u88ab\u58d3\u7e2e\u6458\u8981\u3002\u4f46\u4f7f\u7528\u8005\u53ef\u80fd\u60f3\u56de\u9867\u5b8c\u6574\u5c0d\u8a71\u5167\u5bb9\u3002PreCompact Hook \u5728\u58d3\u7e2e\u89f8\u767c\u524d\u5c07\u5b8c\u6574\u5c0d\u8a71\u4ee5 Markdown \u683c\u5f0f\u5b58\u6a94\uff0c\u672a\u4f86\u53ef\u88ab agent \u641c\u7d22\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u516b\uff1aPer-Group CLAUDE.md \u4f5c\u70ba Agent Persona \u8207\u8a18\u61b6<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6bcf\u500b\u7fa4\u7d44\u6709\u81ea\u5df1\u7684 <code>CLAUDE.md<\/code>\uff0cagent \u5728\u8a72\u7fa4\u7d44\u7684\u5bb9\u5668\u4e2d\u4ee5\u6b64\u70ba system prompt\u3002\u9019\u662f NanoClaw \u7684\u300c\u8a18\u61b6\u300d\u6a5f\u5236\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/groups\/global\/CLAUDE.md\"><code>groups\/global\/CLAUDE.md<\/code><\/a> \u2014 \u5168\u57df prompt\uff0c\u5b9a\u7fa9 agent \u57fa\u790e\u4eba\u683c\u8207\u884c\u70ba\u898f\u7bc4<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># Andy<\/span>\n\nYou are Andy, a personal assistant...\n\n<span class=\"hljs-comment\">## Long Tasks<\/span>\n<span class=\"hljs-keyword\">If<\/span> a request requires significant work, <span class=\"hljs-keyword\">use<\/span> `<span class=\"hljs-title\">mcp__nanoclaw__send_message<\/span>` <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">acknowledge<\/span> <span class=\"hljs-title\">first<\/span>:\n1. <span class=\"hljs-title\">Send<\/span> <span class=\"hljs-title\">a<\/span> <span class=\"hljs-title\">brief<\/span> <span class=\"hljs-title\">message<\/span>: <span class=\"hljs-title\">what<\/span> <span class=\"hljs-title\">you<\/span> <span class=\"hljs-title\">understood<\/span> <span class=\"hljs-title\">and<\/span> <span class=\"hljs-title\">what<\/span> <span class=\"hljs-title\">you<\/span>'<span class=\"hljs-title\">ll<\/span> <span class=\"hljs-title\">do<\/span>\n2. <span class=\"hljs-title\">Do<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">work<\/span>\n3. <span class=\"hljs-title\">Exit<\/span> <span class=\"hljs-title\">with<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">final<\/span> <span class=\"hljs-title\">answer<\/span>\n\n## <span class=\"hljs-title\">Memory<\/span>\n<span class=\"hljs-title\">The<\/span> `<span class=\"hljs-title\">conversations<\/span>\/` <span class=\"hljs-title\">folder<\/span> <span class=\"hljs-title\">contains<\/span> <span class=\"hljs-title\">searchable<\/span> <span class=\"hljs-title\">history<\/span> <span class=\"hljs-title\">of<\/span> <span class=\"hljs-title\">past<\/span> <span class=\"hljs-title\">conversations<\/span>.\n<span class=\"hljs-title\">When<\/span> <span class=\"hljs-title\">you<\/span> <span class=\"hljs-title\">learn<\/span> <span class=\"hljs-title\">something<\/span> <span class=\"hljs-title\">important<\/span>:\n- <span class=\"hljs-title\">Create<\/span> <span class=\"hljs-title\">files<\/span> <span class=\"hljs-title\">for<\/span> <span class=\"hljs-title\">structured<\/span> <span class=\"hljs-title\">data<\/span> (<span class=\"hljs-title\">e<\/span>.<span class=\"hljs-title\">g<\/span>., `<span class=\"hljs-title\">customers<\/span>.<span class=\"hljs-title\">md<\/span>`, `<span class=\"hljs-title\">preferences<\/span>.<span class=\"hljs-title\">md<\/span>`)\n- <span class=\"hljs-title\">Add<\/span> <span class=\"hljs-title\">recurring<\/span> <span class=\"hljs-title\">context<\/span> <span class=\"hljs-title\">directly<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">this<\/span> <span class=\"hljs-title\">CLAUDE<\/span>.<span class=\"hljs-title\">md<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/groups\/main\/CLAUDE.md\"><code>groups\/main\/CLAUDE.md<\/code><\/a> \u2014 \u4e3b\u983b\u9053 prompt\uff0c\u5305\u542b\u7ba1\u7406\u54e1\u7279\u6b0a\u6307\u5f15\u548c\u7fa4\u7d44\u7ba1\u7406\u64cd\u4f5c\u6d41\u7a0b<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u4e3b\u7fa4\u7d44\u7684 <code>CLAUDE.md<\/code> \u5247\u984d\u5916\u5305\u542b\u7ba1\u7406\u54e1\u64cd\u4f5c\u6307\u5f15\u2014\u2014\u5982\u4f55\u67e5\u8a62\u53ef\u7528\u7fa4\u7d44\u3001\u5982\u4f55\u8a3b\u518a\u65b0\u7fa4\u7d44\u3001\u5982\u4f55\u8a2d\u5b9a\u984d\u5916\u7684\u76ee\u9304\u639b\u8f09\u3002\u9019\u4e9b\u300c\u64cd\u4f5c\u624b\u518a\u300d\u76f4\u63a5\u5beb\u5728 prompt \u88e1\uff0cagent \u5c31\u300c\u77e5\u9053\u300d\u5982\u4f55\u57f7\u884c\u7ba1\u7406\u4efb\u52d9\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u9019\u500b\u8a2d\u8a08\u8b93 NanoClaw \u7684\u300c\u8a2d\u5b9a\u6a94\u300d\u5c31\u662f\u81ea\u7136\u8a9e\u8a00\u3002\u60f3\u6539\u8b8a agent \u884c\u70ba\uff1f\u76f4\u63a5\u4fee\u6539 CLAUDE.md\u3002\u60f3\u8b93 agent \u8a18\u4f4f\u67d0\u4ef6\u4e8b\uff1f\u544a\u8a34\u5b83\u300cremember this\u300d\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u4e5d\uff1aSkills over Features\u2014\u2014\u4ee5 Skill \u6a94\u6848\u53d6\u4ee3 Feature Branch<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">NanoClaw \u6700\u5927\u81bd\u7684\u8a2d\u8a08\u6c7a\u7b56\u662f\uff1a<strong>\u4e0d\u63a5\u53d7\u65b0\u529f\u80fd\u7684 PR\uff0c\u53ea\u63a5\u53d7 Skill \u7684 PR<\/strong>\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/.claude\/skills\/customize\/SKILL.md\"><code>.claude\/skills\/customize\/SKILL.md<\/code><\/a> \u2014 Customize skill\uff0c\u6559 Claude Code \u5982\u4f55\u4e92\u52d5\u5f0f\u5730\u4fee\u6539 NanoClaw \u7684\u884c\u70ba<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">---\nname: customize\ndescription: Add <span class=\"hljs-keyword\">new<\/span> capabilities <span class=\"hljs-keyword\">or<\/span> modify NanoClaw behavior...\n---\n\n<span class=\"hljs-comment\"># NanoClaw Customization<\/span>\n\n<span class=\"hljs-comment\">## Key Files<\/span>\n| File | Purpose |\n|------|---------|\n| `src\/config.ts` | Assistant name, trigger pattern, directories |\n| `src\/index.ts` | Message routing, WhatsApp connection, agent invocation |\n\n<span class=\"hljs-comment\">## Common Customization Patterns<\/span>\n\n<span class=\"hljs-comment\">### Adding a New Input Channel (e.g., Telegram, Slack, Email)<\/span>\n\nQuestions to ask:\n- Which channel?\n- Same trigger word <span class=\"hljs-keyword\">or<\/span> different?\n- Same memory hierarchy <span class=\"hljs-keyword\">or<\/span> separate?\n\nImplementation pattern:\n<span class=\"hljs-number\">1.<\/span> Find\/add MCP server <span class=\"hljs-keyword\">for<\/span> the channel\n<span class=\"hljs-number\">2.<\/span> Add connection <span class=\"hljs-keyword\">and<\/span> message handling in `src\/index.ts`\n<span class=\"hljs-number\">3.<\/span> Store messages in the database\n<span class=\"hljs-number\">4.<\/span> Ensure responses route back to correct channel\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">Skill \u4e0d\u662f\u7a0b\u5f0f\u78bc\uff0c\u800c\u662f<strong>\u6559 Claude Code \u5982\u4f55\u4fee\u6539\u7a0b\u5f0f\u78bc\u7684\u6307\u5f15<\/strong>\u3002\u4f7f\u7528\u8005\u57f7\u884c <code>\/add-telegram<\/code>\uff0cClaude Code \u8b80\u53d6 Skill \u6587\u4ef6\uff0c\u7136\u5f8c\u76f4\u63a5\u4fee\u6539\u4f7f\u7528\u8005 fork \u4e2d\u7684\u7a0b\u5f0f\u78bc\u3002\u6700\u7d42\u6bcf\u500b\u4f7f\u7528\u8005\u7684 codebase \u53ea\u5305\u542b\u4ed6\u5011\u771f\u6b63\u9700\u8981\u7684\u529f\u80fd\u2014\u2014\u6c92\u6709 Telegram \u7a0b\u5f0f\u78bc\u6b98\u7559\u5728\u4e00\u500b\u53ea\u7528 WhatsApp \u7684\u7cfb\u7d71\u4e2d\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8a2d\u8a08\u5341\uff1aCredential Filtering \u8207\u6700\u5c0f\u6b0a\u9650\u66b4\u9732<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u5bb9\u5668\u9700\u8981 Claude API \u8a8d\u8b49\u624d\u80fd\u57f7\u884c\uff0c\u4f46 NanoClaw \u4e0d\u6703\u628a\u6574\u500b <code>.env<\/code> \u7d66\u5bb9\u5668\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/src\/container-runner.ts\"><code>src\/container-runner.ts<\/code><\/a> \u2014 \u53ea\u904e\u6ffe\u51fa agent \u9700\u8981\u7684\u74b0\u5883\u8b8a\u6578\uff0c\u4ee5 read-only \u65b9\u5f0f\u639b\u8f09<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> envContent = fs.readFileSync(envFile, <span class=\"hljs-string\">'utf-8'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> allowedVars = &#91;<span class=\"hljs-string\">'CLAUDE_CODE_OAUTH_TOKEN'<\/span>, <span class=\"hljs-string\">'ANTHROPIC_API_KEY'<\/span>];\n<span class=\"hljs-keyword\">const<\/span> filteredLines = envContent.split(<span class=\"hljs-string\">'\\n'<\/span>).filter(<span class=\"hljs-function\">(<span class=\"hljs-params\">line<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> trimmed = line.trim();\n  <span class=\"hljs-keyword\">if<\/span> (!trimmed || trimmed.startsWith(<span class=\"hljs-string\">'#'<\/span>)) <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">false<\/span>;\n  <span class=\"hljs-keyword\">return<\/span> allowedVars.some(<span class=\"hljs-function\">(<span class=\"hljs-params\">v<\/span>) =&gt;<\/span> trimmed.startsWith(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${v}<\/span>=`<\/span>));\n});\n\n<span class=\"hljs-keyword\">if<\/span> (filteredLines.length &gt; <span class=\"hljs-number\">0<\/span>) {\n  fs.writeFileSync(path.join(envDir, <span class=\"hljs-string\">'env'<\/span>), filteredLines.join(<span class=\"hljs-string\">'\\n'<\/span>) + <span class=\"hljs-string\">'\\n'<\/span>);\n  mounts.push({\n    <span class=\"hljs-attr\">hostPath<\/span>: envDir,\n    <span class=\"hljs-attr\">containerPath<\/span>: <span class=\"hljs-string\">'\/workspace\/env-dir'<\/span>,\n    <span class=\"hljs-attr\">readonly<\/span>: <span class=\"hljs-literal\">true<\/span>,\n  });\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u6ce8\u610f\u662f <code>readonly: true<\/code>\u2014\u2014\u5bb9\u5668\u4e0d\u80fd\u4fee\u6539\u8a8d\u8b49\u8cc7\u8a0a\u3002\u800c Dockerfile \u4e2d\u7684 entrypoint \u8ca0\u8cac\u5f9e\u9019\u500b\u76ee\u9304\u8b80\u53d6\u74b0\u5883\u8b8a\u6578\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/container\/Dockerfile\"><code>container\/Dockerfile<\/code><\/a> \u2014 \u5bb9\u5668\u6620\u50cf\u5b9a\u7fa9\uff0centrypoint \u5f9e mount \u76ee\u9304\u8f09\u5165\u74b0\u5883\u8b8a\u6578<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># Sources env from mounted \/workspace\/env-dir\/env if it exists<\/span>\n<span class=\"hljs-comment\"># (workaround for Apple Container -i bug)<\/span>\n&#91; -f \/workspace\/env-dir\/env ] &amp;&amp; export $(cat \/workspace\/env-dir\/env | xargs)\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">SECURITY.md \u5766\u8aa0\u5730\u6307\u51fa\u76ee\u524d\u7684\u9650\u5236\uff1aagent \u5728\u5bb9\u5668\u5167\u4ecd\u53ef\u900f\u904e Bash \u8b80\u53d6\u9019\u4e9b\u8a8d\u8b49\u3002\u9019\u7a2e\u300c\u627f\u8a8d\u9650\u5236\u4e26\u6a19\u8a18\u70ba\u5f85\u6539\u9032\u300d\u7684\u614b\u5ea6\uff0c\u6bd4\u5047\u88dd\u554f\u984c\u4e0d\u5b58\u5728\u66f4\u8ca0\u8cac\u4efb\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prompt \u8a2d\u8a08\u89e3\u8b80<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">NanoClaw \u7684 prompt \u8a2d\u8a08\u9ad4\u73fe\u4e86\u300c\u5206\u5c64\u7279\u6b0a\u300d\u7684\u7406\u5ff5\u3002\u5168\u57df <code>CLAUDE.md<\/code> \u5b9a\u7fa9\u57fa\u790e\u884c\u70ba\uff0c\u4e3b\u7fa4\u7d44\u7684 <code>CLAUDE.md<\/code> \u5247\u984d\u5916\u6388\u4e88\u7ba1\u7406\u80fd\u529b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\/blob\/main\/groups\/main\/CLAUDE.md\"><code>groups\/main\/CLAUDE.md<\/code><\/a> \u2014 \u4e3b\u983b\u9053\u7684 prompt \u8a2d\u8a08\uff0c\u5305\u542b\u7d50\u69cb\u5316\u7684\u7ba1\u7406\u64cd\u4f5c\u6307\u5f15<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5e7e\u500b\u503c\u5f97\u5b78\u7fd2\u7684 prompt \u8a2d\u8a08\u6280\u5de7\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n\n<li><strong>\u660e\u78ba\u7684\u80fd\u529b\u5ba3\u544a<\/strong>\uff1aprompt \u958b\u982d\u5c31\u5217\u51fa\u300cWhat You Can Do\u300d\uff0c\u8b93 agent \u6e05\u695a\u77e5\u9053\u81ea\u5df1\u7684\u80fd\u529b\u908a\u754c<\/li>\n\n\n<li><strong>\u64cd\u4f5c\u6d41\u7a0b\u5beb\u6210\u6b65\u9a5f<\/strong>\uff1a\u300cAdding a Group\u300d\u5206\u6210 6 \u500b\u660e\u78ba\u6b65\u9a5f\uff0c\u907f\u514d agent \u907a\u6f0f\u64cd\u4f5c<\/li>\n\n\n<li><strong>Fallback \u7b56\u7565<\/strong>\uff1a\u67e5\u8a62\u7fa4\u7d44\u6642\u5148\u7528 IPC \u6a94\u6848\uff0c\u5982\u679c\u627e\u4e0d\u5230\u518d\u67e5 SQLite\u2014\u2014\u76f4\u63a5\u628a fallback \u908f\u8f2f\u5beb\u9032 prompt<\/li>\n\n\n<li><strong>\u683c\u5f0f\u7d04\u675f<\/strong>\uff1a\u300cDo NOT use markdown headings (##) in WhatsApp messages\u300d\u2014\u2014\u907f\u514d agent \u4f7f\u7528 WhatsApp \u4e0d\u652f\u63f4\u7684\u683c\u5f0f<\/li>\n\n\n<li><strong>\u9577\u4efb\u52d9\u8655\u7406<\/strong>\uff1a\u300c\u5148\u7528 send_message \u56de\u8986\u78ba\u8a8d\uff0c\u518d\u505a\u4e8b\uff0c\u518d\u56de\u8986\u7d50\u679c\u300d\u2014\u2014\u907f\u514d\u4f7f\u7528\u8005\u7b49\u5f85\u7121\u56de\u61c9<\/li>\n\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">MCP tool \u7684 description \u540c\u6a23\u662f\u7cbe\u5fc3\u8a2d\u8a08\u7684 prompt\uff1a<\/p>\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">tool(<span class=\"hljs-string\">'schedule_task'<\/span>,\n  <span class=\"hljs-string\">`...CONTEXT MODE - Choose based on task type:\n   \u2022 \"group\" (recommended for most tasks): Task runs in the group's conversation context...\n   \u2022 \"isolated\": Task runs in a fresh session...\n\n   If unsure which mode to use, ask the user. Examples:\n   - \"Remind me about our discussion\" \u2192 group (needs conversation context)\n   - \"Check the weather every morning\" \u2192 isolated (self-contained task)`<\/span>,\n  ...\n)\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/div>\n\n\n<p class=\"wp-block-paragraph\">\u900f\u904e\u5177\u9ad4\u7684 examples \u5f15\u5c0e Claude \u5728\u6a21\u7cca\u5834\u666f\u4e2d\u505a\u51fa\u6b63\u78ba\u9078\u64c7\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u8a2d\u8a08\u7406\u5ff5\u8403\u53d6<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><strong>Container \u9694\u96e2\u512a\u65bc\u61c9\u7528\u5c64\u6b0a\u9650<\/strong> \u2014 \u8207\u5176\u5728\u7a0b\u5f0f\u78bc\u4e2d\u5beb\u4e00\u5806 <code>if (hasPermission)<\/code> \u6aa2\u67e5\uff0c\u4e0d\u5982\u8b93\u5b89\u5168\u908a\u754c\u5728 OS \u5c64\u9762\u3002Agent \u5728\u6c99\u7bb1\u88e1\u53ef\u4ee5 <code>bypassPermissions<\/code>\uff0c\u56e0\u70ba\u5b83\u7684\u4e16\u754c\u53ea\u6709\u4f60\u5141\u8a31\u5b83\u770b\u5230\u7684\u90e8\u5206\u3002\u9019\u500b\u6a21\u5f0f\u9069\u7528\u65bc\u4efb\u4f55\u9700\u8981\u57f7\u884c\u4e0d\u53d7\u4fe1\u4efb\u7a0b\u5f0f\u78bc\u7684\u5834\u666f\u3002<\/li>\n\n\n<li><strong>Filesystem-based IPC \u9069\u5408\u8de8\u4fe1\u4efb\u908a\u754c\u901a\u8a0a<\/strong> \u2014 \u7576\u5169\u500b process \u8655\u65bc\u4e0d\u540c\u4fe1\u4efb\u7b49\u7d1a\u6642\uff0c\u7528 JSON \u6a94\u6848\u4f5c\u70ba IPC \u6bd4 socket \u66f4\u5bb9\u6613\u5be9\u8a08\u548c\u63a7\u5236\u3002\u539f\u5b50\u5beb\u5165\uff08temp file + rename\uff09\u4fdd\u8b49\u4e86\u53ef\u9760\u6027\uff0c\u800c\u76ee\u9304\u8def\u5f91\u4f5c\u70ba\u8eab\u4efd\u8b58\u5225\u907f\u514d\u4e86\u8eab\u4efd\u507d\u9020\u3002<\/li>\n\n\n<li><strong>Prompt \u5c31\u662f\u8a2d\u5b9a\u6a94<\/strong> \u2014 NanoClaw \u6c92\u6709 YAML \u8a2d\u5b9a\u3001\u6c92\u6709 admin dashboard\u3002\u884c\u70ba\u8b8a\u66f4\u76f4\u63a5\u5beb\u5728 CLAUDE.md \u4e2d\uff0c\u7528\u81ea\u7136\u8a9e\u8a00\u63cf\u8ff0\u3002\u9019\u5728 AI-native \u61c9\u7528\u4e2d\u662f\u5408\u7406\u7684\u2014\u2014\u4f60\u7684\u300c\u4f7f\u7528\u8005\u300d\u662f LLM\uff0c\u81ea\u7136\u8a9e\u8a00\u5c31\u662f\u6700\u597d\u7684\u8a2d\u5b9a\u8a9e\u8a00\u3002<\/li>\n\n\n<li><strong>Skills over Features \u662f AI \u6642\u4ee3\u7684\u65b0\u64f4\u5c55\u6a21\u5f0f<\/strong> \u2014 \u4e0d\u628a\u6240\u6709\u529f\u80fd\u585e\u9032\u4e00\u500b codebase\uff0c\u800c\u662f\u7528 Skill \u6587\u4ef6\u6559 AI \u5982\u4f55\u6309\u9700\u4fee\u6539\u7a0b\u5f0f\u78bc\u3002\u6bcf\u500b\u4f7f\u7528\u8005\u7684 fork \u53ea\u5305\u542b\u4ed6\u5011\u771f\u6b63\u9700\u8981\u7684\u529f\u80fd\uff0c\u4e0d\u5b58\u5728\u529f\u80fd\u81a8\u8139\u554f\u984c\u3002<\/li>\n\n\n<li><strong>\u6975\u7c21\u67b6\u69cb\u7684\u53ef\u7dad\u8b77\u6027\u7d05\u5229<\/strong> \u2014 8 \u500b\u6838\u5fc3\u6a94\u6848\u30011500 \u884c\u7a0b\u5f0f\u78bc\uff0c\u4efb\u4f55\u4eba\uff08\u6216 AI\uff09\u90fd\u80fd\u5728 8 \u5206\u9418\u5167\u7406\u89e3\u6574\u500b\u7cfb\u7d71\u3002\u9019\u7a2e\u300c\u5c0f\u5230\u53ef\u4ee5\u7406\u89e3\u300d\u7684\u8a2d\u8a08\u54f2\u5b78\uff0c\u8b93 debug\u3001customization \u548c\u5b89\u5168\u5be9\u8a08\u90fd\u8b8a\u5f97\u53ef\u884c\u3002<\/li>\n\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\u5ef6\u4f38\u601d\u8003<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">NanoClaw \u7684\u8a2d\u8a08\u6a21\u5f0f\u53ef\u4ee5\u5ef6\u4f38\u5230\u8a31\u591a\u5834\u666f\uff1a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>AI Agent \u5b89\u5168\u6c99\u7bb1<\/strong>\uff1a\u5982\u679c\u4f60\u5728\u69cb\u5efa\u4efb\u4f55\u9700\u8981\u57f7\u884c AI \u751f\u6210\u7a0b\u5f0f\u78bc\u7684\u7cfb\u7d71\uff0cContainer \u9694\u96e2 + mount allowlist \u7684\u6a21\u5f0f\u6bd4 application-level sandboxing \u66f4\u53ef\u9760\u3002\u95dc\u9375\u662f\u628a\u5b89\u5168\u8a2d\u5b9a\u653e\u5728 agent \u4e0d\u53ef\u89f8\u53ca\u7684\u5730\u65b9\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Multi-tenant Agent \u7cfb\u7d71<\/strong>\uff1aper-group \u9694\u96e2\u7684\u6a21\u5f0f\u2014\u2014\u7368\u7acb\u7684 filesystem\u3001session\u3001IPC namespace\u2014\u2014\u53ef\u4ee5\u76f4\u63a5\u61c9\u7528\u5230 SaaS \u7522\u54c1\u4e2d\u7684 multi-tenant AI agent \u67b6\u69cb\u3002\u4fe1\u4efb\u7b49\u7d1a\u4e0d\u540c\u7684 tenant \u5c31\u7d66\u4e0d\u540c\u7684 mount \u6b0a\u9650\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>AI-native Configuration<\/strong>\uff1a\u5982\u679c\u4f60\u7684\u7cfb\u7d71\u4e3b\u8981\u7531 AI \u64cd\u4f5c\uff0c\u8003\u616e\u7528\u81ea\u7136\u8a9e\u8a00\u6587\u4ef6\uff08\u5982 CLAUDE.md\uff09\u53d6\u4ee3\u50b3\u7d71\u8a2d\u5b9a\u6a94\u3002\u9019\u4e0d\u53ea\u662f\u300c\u65b9\u4fbf\u300d\u2014\u2014LLM \u7406\u89e3\u81ea\u7136\u8a9e\u8a00\u7684\u80fd\u529b\u9060\u8d85\u7406\u89e3 YAML \u8a9e\u6cd5\uff0c\u884c\u70ba\u7684\u53ef\u9810\u6e2c\u6027\u53cd\u800c\u66f4\u9ad8\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Skills \u6a21\u5f0f<\/strong>\uff1a\u5c0d\u65bc\u4efb\u4f55\u9762\u5411\u958b\u767c\u8005\u7684 CLI \u5de5\u5177\u6216\u6846\u67b6\uff0c\u53ef\u4ee5\u8003\u616e\u7528\u300c\u6559 AI \u5982\u4f55\u4fee\u6539\u7a0b\u5f0f\u78bc\u300d\u7684 Skill \u6587\u4ef6\u4f86\u53d6\u4ee3 plugin system\u3002\u4f7f\u7528\u8005\u5f97\u5230\u7684\u662f\u4e7e\u6de8\u7684\u3001\u91cf\u8eab\u5b9a\u5236\u7684\u7a0b\u5f0f\u78bc\uff0c\u800c\u4e0d\u662f\u4e00\u5806\u53ef\u80fd\u885d\u7a81\u7684 plugin\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Vibe Coding\uff1a\u628a\u89c0\u5ff5\u8b8a\u6210\u7a0b\u5f0f\u78bc<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u4ee5\u4e0b\u662f\u4f60\u53ef\u4ee5\u76f4\u63a5\u7d66 AI \u7684 prompt \u6307\u4ee4\uff0c\u5c07\u672c\u6587\u4ecb\u7d39\u7684\u8a2d\u8a08\u89c0\u5ff5\u61c9\u7528\u5230\u4f60\u7684\u5c08\u6848\u4e2d\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Container \u9694\u96e2\u53d6\u4ee3\u61c9\u7528\u5c64\u6b0a\u9650\u6aa2\u67e5<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5834\u666f<\/strong>\uff1a\u4f60\u6b63\u5728\u8a2d\u8a08\u4e00\u500b\u9700\u8981\u57f7\u884c\u4e0d\u53d7\u4fe1\u4efb\u7a0b\u5f0f\u78bc\u7684\u7cfb\u7d71\uff08AI agent\u3001\u4f7f\u7528\u8005\u63d0\u4ea4\u7684\u7a0b\u5f0f\u78bc\u3001\u81ea\u52d5\u5316\u8173\u672c\uff09\uff0c\u76ee\u524d\u7528\u61c9\u7528\u5c64\u7684 <code>if (hasPermission)<\/code> \u4f86\u63a7\u5236\u5b58\u53d6\u6b0a\u9650\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7d66 AI \u7684\u6307\u4ee4<\/strong>\uff1a<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u6211\u7684\u7cfb\u7d71\u9700\u8981\u57f7\u884c\u4e0d\u53d7\u4fe1\u4efb\u7684\u7a0b\u5f0f\u78bc\uff0c\u76ee\u524d\u7528\u61c9\u7528\u5c64\u6b0a\u9650\u6aa2\u67e5\u4f86\u63a7\u5236\u5b89\u5168\u3002\u6211\u60f3\u6539\u7528 NanoClaw \u7684\u300cContainer \u9694\u96e2\u4f5c\u70ba\u4e3b\u8981\u5b89\u5168\u908a\u754c\u300d\u6a21\u5f0f\u2014\u2014\u628a\u6bcf\u6b21\u57f7\u884c\u653e\u9032\u7368\u7acb\u7684 Docker Container\uff0c\u7528 volume mount \u4f86\u63a7\u5236\u53ef\u898b\u7bc4\u570d\uff0c\u800c\u4e0d\u662f\u5728\u7a0b\u5f0f\u78bc\u4e2d\u5beb\u6b0a\u9650\u5224\u65b7\u3002<br><br>\u8acb\u5e6b\u6211\u8a2d\u8a08\u4e00\u500b container-runner \u6a21\u7d44\uff0c\u9700\u8981\uff1a<br>1. \u6839\u64da\u4f7f\u7528\u8005\u7684\u4fe1\u4efb\u7b49\u7d1a\u7d44\u88dd\u4e0d\u540c\u7684 volume mounts\uff08\u4f8b\u5982\uff1a\u7ba1\u7406\u54e1\u639b\u8f09\u6574\u500b\u5c08\u6848\u76ee\u9304\u53ef\u8b80\u5beb\u3001\u4e00\u822c\u4f7f\u7528\u8005\u53ea\u639b\u8f09\u81ea\u5df1\u7684\u8cc7\u6599\u593e\uff09<br>2. \u654f\u611f\u8def\u5f91\uff08.ssh\u3001.env\u3001credentials\uff09\u7121\u8ad6\u8a2d\u5b9a\u5982\u4f55\u90fd\u4e0d\u80fd\u88ab\u639b\u8f09\u2014\u2014\u5be6\u4f5c\u4e00\u500b blocked patterns \u6e05\u55ae<br>3. \u6240\u6709\u8def\u5f91\u90fd\u8981\u505a symlink resolution\uff08fs.realpathSync\uff09\uff0c\u9632\u6b62 symlink traversal attack<br>4. \u74b0\u5883\u8b8a\u6578\u53ea\u50b3\u905e\u767d\u540d\u55ae\u4e2d\u7684\u9805\u76ee\uff0c\u4e14\u4ee5 read-only \u65b9\u5f0f\u639b\u8f09<br><br>\u6211\u7684\u6280\u8853\u68e7\u662f [\u4f60\u7684\u8a9e\u8a00\/\u6846\u67b6]\uff0c\u5bb9\u5668\u6280\u8853\u7528 [Docker\/Podman]\u3002<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u6548\u679c<\/strong>\uff1aAI \u6703\u7522\u51fa\u4e00\u500b\u5b8c\u6574\u7684 container runner \u6a21\u7d44\uff0c\u5305\u542b volume mount \u7d44\u88dd\u908f\u8f2f\u3001blocked patterns \u5b89\u5168\u6aa2\u67e5\u3001symlink \u89e3\u6790\u9a57\u8b49\uff0c\u4ee5\u53ca\u74b0\u5883\u8b8a\u6578\u904e\u6ffe\u6a5f\u5236\u3002\u4f60\u7684\u5b89\u5168\u908a\u754c\u6703\u5f9e\u5206\u6563\u5728\u5404\u8655\u7684 if \u5224\u65b7\uff0c\u8b8a\u6210\u96c6\u4e2d\u5728 OS \u5c64\u7d1a\u7684\u9694\u96e2\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Filesystem-based IPC \u8de8\u4fe1\u4efb\u908a\u754c\u901a\u8a0a<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5834\u666f<\/strong>\uff1a\u4f60\u6709\u5169\u500b\u4e0d\u540c\u4fe1\u4efb\u7b49\u7d1a\u7684 process \u9700\u8981\u901a\u8a0a\uff08\u4f8b\u5982\u6c99\u7bb1\u4e2d\u7684 agent \u9700\u8981\u89f8\u767c host \u4e0a\u7684\u64cd\u4f5c\uff09\uff0c\u6b63\u5728\u8003\u616e\u7528 socket\u3001HTTP API\u3001\u6216 message queue\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7d66 AI \u7684\u6307\u4ee4<\/strong>\uff1a<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u6211\u7684\u7cfb\u7d71\u4e2d\u6709\u4e00\u500b\u5728 Container \u6c99\u7bb1\u5167\u57f7\u884c\u7684 agent process\uff0c\u5b83\u9700\u8981\u8acb\u6c42 host process \u57f7\u884c\u67d0\u4e9b\u64cd\u4f5c\uff08\u4f8b\u5982\u767c\u9001\u8a0a\u606f\u3001\u6392\u7a0b\u4efb\u52d9\uff09\uff0c\u4f46\u4e0d\u80fd\u76f4\u63a5\u5b58\u53d6 host \u7684\u8cc7\u6e90\u3002<br><br>\u6211\u60f3\u63a1\u7528 NanoClaw \u7684\u300cFilesystem-based IPC\u300d\u6a21\u5f0f\uff1aagent \u5c07\u64cd\u4f5c\u610f\u5716\u5e8f\u5217\u5316\u70ba JSON \u6a94\u6848\u5beb\u5165\u5171\u4eab\u7684 IPC \u76ee\u9304\uff0chost process \u5b9a\u671f\u6383\u63cf\u9019\u500b\u76ee\u9304\u4e26\u57f7\u884c\u3002\u8acb\u5e6b\u6211\u5be6\u4f5c\uff1a<br>1. IPC \u5beb\u5165\u7aef\uff1a\u7528 atomic write\uff08\u5148\u5beb temp file \u518d rename\uff09\u4fdd\u8b49\u6a94\u6848\u5b8c\u6574\u6027\uff0c\u6a94\u540d\u7528 timestamp + random string \u78ba\u4fdd\u552f\u4e00<br>2. IPC \u8b80\u53d6\u7aef\uff1ahost \u7684 watcher \u5b9a\u671f\u6383\u63cf\u76ee\u9304\uff0c\u89e3\u6790 JSON\uff0c\u6839\u64da\u76ee\u9304\u8def\u5f91\u8b58\u5225\u4f86\u6e90\u8eab\u4efd\uff08\u4e0d\u4fe1\u4efb payload \u4e2d\u7684\u8eab\u4efd\u5ba3\u544a\uff09\uff0c\u8655\u7406\u5b8c\u5f8c\u522a\u9664\u6216\u6b78\u6a94\u6a94\u6848<br>3. \u6388\u6b0a\u9a57\u8b49\uff1ahost \u6839\u64da IPC \u6a94\u6848\u6240\u5728\u7684\u76ee\u9304\u8def\u5f91\uff08\u800c\u975e\u6a94\u6848\u5167\u5bb9\uff09\u5224\u65b7\u8acb\u6c42\u8005\u8eab\u4efd\uff0c\u78ba\u4fdd\u7121\u6cd5\u507d\u9020\u8eab\u4efd<br><br>\u6211\u7684\u6280\u8853\u68e7\u662f [\u4f60\u7684\u8a9e\u8a00\/\u6846\u67b6]\u3002<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u6548\u679c<\/strong>\uff1aAI \u6703\u7522\u51fa IPC \u7684\u5beb\u5165\u7aef\u548c\u8b80\u53d6\u7aef\u7a0b\u5f0f\u78bc\uff0c\u5305\u542b\u539f\u5b50\u5beb\u5165\u3001\u76ee\u9304\u6383\u63cf\u3001\u8eab\u4efd\u9a57\u8b49\u908f\u8f2f\u3002\u9019\u7a2e\u6a21\u5f0f\u6bd4 socket \u66f4\u5bb9\u6613\u5be9\u8a08\uff08\u6a94\u6848\u7cfb\u7d71\u7559\u4e0b\u5b8c\u6574\u75d5\u8de1\uff09\uff0c\u6bd4 HTTP API \u66f4\u5b89\u5168\uff08\u4e0d\u9700\u8981\u66b4\u9732 port\uff09\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Sentinel Markers \u89e3\u6c7a\u6df7\u5408\u8f38\u51fa\u89e3\u6790<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5834\u666f<\/strong>\uff1a\u4f60\u7684\u7cfb\u7d71\u9700\u8981\u5f9e\u5b50 process \u7684 stdout \u4e2d\u63d0\u53d6\u7d50\u69cb\u5316\u8f38\u51fa\uff0c\u4f46 stdout \u88e1\u6df7\u96dc\u4e86\u5404\u7a2e debug log\u3001\u7b2c\u4e09\u65b9\u5eab\u7684\u8f38\u51fa\uff0c\u8b93 JSON \u89e3\u6790\u7d93\u5e38\u5931\u6557\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7d66 AI \u7684\u6307\u4ee4<\/strong>\uff1a<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u6211\u7684\u7cfb\u7d71\u6703 spawn \u5b50 process \u4e26\u9700\u8981\u5f9e stdout \u4e2d\u63d0\u53d6 JSON \u7d50\u679c\uff0c\u4f46 stdout \u88e1\u6df7\u5408\u4e86 debug log \u548c\u7b2c\u4e09\u65b9\u5eab\u7684\u8f38\u51fa\uff0c\u7121\u6cd5\u76f4\u63a5 JSON.parse \u6574\u500b stdout\u3002<br><br>\u8acb\u5e6b\u6211\u5be6\u4f5c NanoClaw \u7684\u300cSentinel Markers\u300d\u6a21\u5f0f\uff1a<br>1. \u5b9a\u7fa9\u4e00\u5c0d\u552f\u4e00\u7684\u6a19\u8a18\u5b57\u4e32\uff08\u4f8b\u5982 <code>---MY_OUTPUT_START---<\/code> \u548c <code>---MY_OUTPUT_END---<\/code>\uff09\uff0c\u5b50 process \u5728\u8f38\u51fa\u7d50\u679c\u524d\u5f8c\u5370\u51fa\u9019\u5c0d\u6a19\u8a18<br>2. \u7236 process \u5f9e stdout \u4e2d\u7528 indexOf \u627e\u5230\u6a19\u8a18\uff0c\u63d0\u53d6\u4e2d\u9593\u7684 JSON \u5167\u5bb9<br>3. \u52a0\u4e0a fallback \u6a5f\u5236\uff1a\u5982\u679c\u627e\u4e0d\u5230\u6a19\u8a18\uff0c\u5617\u8a66\u89e3\u6790\u6700\u5f8c\u4e00\u884c\u975e\u7a7a\u884c\uff08\u5411\u5f8c\u76f8\u5bb9\uff09<br>4. \u52a0\u4e0a\u932f\u8aa4\u8655\u7406\uff1a\u6a19\u8a18\u627e\u5230\u4f46 JSON \u89e3\u6790\u5931\u6557\u7684\u60c5\u6cc1<br><br>\u8acb\u540c\u6642\u7d66\u6211\u5b50 process \u7aef\u7684\u8f38\u51fa\u51fd\u5f0f\u548c\u7236 process \u7aef\u7684\u89e3\u6790\u51fd\u5f0f\u3002<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u6548\u679c<\/strong>\uff1aAI \u6703\u7522\u51fa\u4e00\u5c0d\u914d\u5408\u4f7f\u7528\u7684\u51fd\u5f0f\u2014\u2014\u5b50 process \u7aef\u7684 <code>emitResult()<\/code> \u548c\u7236 process \u7aef\u7684 <code>parseContainerOutput()<\/code>\uff0c\u5305\u542b sentinel markers\u3001fallback \u908f\u8f2f\u548c\u5b8c\u6574\u7684\u932f\u8aa4\u8655\u7406\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Skills over Features \u64f4\u5c55\u6a21\u5f0f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5834\u666f<\/strong>\uff1a\u4f60\u5728\u7dad\u8b77\u4e00\u500b\u958b\u6e90\u5c08\u6848\u6216\u5167\u90e8\u5de5\u5177\uff0c\u60f3\u8b93\u4f7f\u7528\u8005\u80fd\u81ea\u8a02\u529f\u80fd\uff0c\u4f46\u4e0d\u5e0c\u671b\u6240\u6709\u529f\u80fd\u90fd\u585e\u9032\u540c\u4e00\u500b codebase\uff08\u9020\u6210\u529f\u80fd\u81a8\u8139\uff09\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7d66 AI \u7684\u6307\u4ee4<\/strong>\uff1a<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u6211\u60f3\u70ba\u6211\u7684\u5c08\u6848 [\u5c08\u6848\u540d\u7a31] \u63a1\u7528 NanoClaw \u7684\u300cSkills over Features\u300d\u64f4\u5c55\u6a21\u5f0f\u2014\u2014\u4e0d\u76f4\u63a5\u52a0\u529f\u80fd\u5230 codebase\uff0c\u800c\u662f\u5beb SKILL.md \u6587\u4ef6\u6559 AI \u5982\u4f55\u6309\u9700\u4fee\u6539\u4f7f\u7528\u8005 fork \u4e2d\u7684\u7a0b\u5f0f\u78bc\u3002<br><br>\u8acb\u5e6b\u6211\u70ba\u4ee5\u4e0b\u529f\u80fd\u5beb\u4e00\u500b SKILL.md \u6a94\u6848\uff1a[\u8981\u65b0\u589e\u7684\u529f\u80fd\u63cf\u8ff0]<br><br>SKILL.md \u7684\u683c\u5f0f\u53c3\u8003\uff1a<br>&#8211; YAML frontmatter \u5305\u542b name \u548c description<br>&#8211; \u5217\u51fa\u76f8\u95dc\u7684 Key Files \u548c\u5b83\u5011\u7684\u7528\u9014<br>&#8211; \u5217\u51fa\u4e92\u52d5\u5f0f\u554f\u984c\uff08AI \u61c9\u8a72\u5148\u554f\u4f7f\u7528\u8005\u4ec0\u9ebc\uff1f\uff09<br>&#8211; \u5beb\u51fa\u5177\u9ad4\u7684 Implementation Pattern\uff08\u6b65\u9a5f\u5316\u7684\u4fee\u6539\u6307\u5f15\uff09<br>&#8211; \u5305\u542b\u6ce8\u610f\u4e8b\u9805\u548c\u5e38\u898b\u9677\u9631<br><br>\u76ee\u6a19\u662f\u8b93\u4f7f\u7528\u8005\u57f7\u884c\u9019\u500b Skill \u5f8c\uff0cAI \u80fd\u6839\u64da\u6307\u5f15\u76f4\u63a5\u4fee\u6539\u7a0b\u5f0f\u78bc\uff0c\u800c\u4e0d\u662f\u5b89\u88dd\u4e00\u500b plugin\u3002<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u6548\u679c<\/strong>\uff1aAI \u6703\u7522\u51fa\u4e00\u500b\u7d50\u69cb\u5316\u7684 SKILL.md \u6a94\u6848\uff0c\u5305\u542b\u5b8c\u6574\u7684\u4e92\u52d5\u6d41\u7a0b\u548c\u5be6\u4f5c\u6b65\u9a5f\u3002\u4f7f\u7528\u8005\u53ef\u4ee5\u628a\u9019\u500b Skill \u52a0\u5165 <code>.claude\/skills\/<\/code> \u76ee\u9304\uff0c\u8b93 Claude Code \u6839\u64da\u6307\u5f15\u81ea\u52d5\u5316\u5730\u4fee\u6539\u7a0b\u5f0f\u78bc\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prompt \u4f5c\u70ba\u8a2d\u5b9a\u6a94\u7684 AI-native \u8a2d\u8a08<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5834\u666f<\/strong>\uff1a\u4f60\u6b63\u5728\u958b\u767c\u4e00\u500b AI agent \u61c9\u7528\uff0c\u9700\u8981\u8b93\u4e0d\u540c\u7684\u4f7f\u7528\u8005\/\u79df\u6236\u6709\u4e0d\u540c\u7684\u884c\u70ba\u8a2d\u5b9a\uff0c\u76ee\u524d\u7528 YAML \u6216 JSON \u8a2d\u5b9a\u6a94\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u7d66 AI \u7684\u6307\u4ee4<\/strong>\uff1a<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u6211\u6b63\u5728\u958b\u767c\u4e00\u500b\u591a\u79df\u6236\u7684 AI agent \u7cfb\u7d71\uff0c\u76ee\u524d\u6bcf\u500b\u79df\u6236\u7684\u884c\u70ba\u8a2d\u5b9a\u7528 JSON \u8a2d\u5b9a\u6a94\u7ba1\u7406\u3002\u6211\u60f3\u6539\u7528 NanoClaw \u7684\u300cPrompt \u5c31\u662f\u8a2d\u5b9a\u6a94\u300d\u6a21\u5f0f\u2014\u2014\u6bcf\u500b\u79df\u6236\u6709\u81ea\u5df1\u7684 CLAUDE.md\uff08\u6216\u985e\u4f3c\u7684 system prompt \u6587\u4ef6\uff09\uff0c\u7528\u81ea\u7136\u8a9e\u8a00\u5b9a\u7fa9 agent \u7684\u884c\u70ba\u3001\u80fd\u529b\u548c\u9650\u5236\u3002<br><br>\u8acb\u5e6b\u6211\u8a2d\u8a08\u9019\u500b prompt-as-config \u67b6\u69cb\uff1a<br>1. \u8a2d\u8a08 prompt \u7684\u5206\u5c64\u7d50\u69cb\uff1a\u5168\u57df prompt\uff08\u6240\u6709\u79df\u6236\u5171\u4eab\u7684\u57fa\u790e\u884c\u70ba\uff09+ \u79df\u6236\u5c08\u5c6c prompt\uff08\u500b\u5225\u5ba2\u88fd\uff09<br>2. \u5168\u57df prompt \u7bc4\u672c\uff1a\u5305\u542b\u80fd\u529b\u5ba3\u544a\u3001\u9577\u4efb\u52d9\u8655\u7406\u6d41\u7a0b\uff08\u5148\u78ba\u8a8d\u518d\u57f7\u884c\u518d\u56de\u5831\uff09\u3001\u683c\u5f0f\u7d04\u675f\u3001fallback \u7b56\u7565<br>3. \u79df\u6236 prompt \u7bc4\u672c\uff1a\u5305\u542b\u8a72\u79df\u6236\u7279\u6709\u7684\u77e5\u8b58\u3001\u504f\u597d\u3001\u53ef\u7528\u5de5\u5177\u6e05\u55ae<br>4. prompt \u8f09\u5165\u6a5f\u5236\uff1aagent \u555f\u52d5\u6642\u6309\u512a\u5148\u9806\u5e8f\u5408\u4f75\u9019\u4e9b prompt<br><br>\u6211\u7684 agent \u4f7f\u7528 [Claude API \/ OpenAI API \/ \u5176\u4ed6]\u3002<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u6548\u679c<\/strong>\uff1aAI \u6703\u7522\u51fa\u4e00\u5957 prompt \u7bc4\u672c\u548c\u8f09\u5165\u6a5f\u5236\uff0c\u8b93\u4f60\u7528\u81ea\u7136\u8a9e\u8a00\u6587\u4ef6\u53d6\u4ee3 JSON \u8a2d\u5b9a\u6a94\u3002\u7576\u9700\u8981\u8abf\u6574 agent \u884c\u70ba\u6642\uff0c\u76f4\u63a5\u4fee\u6539 markdown \u6587\u4ef6\u800c\u975e\u7a0b\u5f0f\u78bc\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5ef6\u4f38\u95b1\u8b80<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><a href=\"https:\/\/github.com\/gavrielc\/nanoclaw\">NanoClaw GitHub Repository<\/a> \u2014 \u5c08\u6848\u539f\u59cb\u78bc\u8207\u5b8c\u6574\u6587\u4ef6\uff0c\u5305\u542b SECURITY.md \u4e2d\u5c0d\u5b89\u5168\u6a21\u578b\u7684\u5766\u8aa0\u8aaa\u660e<\/li>\n\n\n<li><a href=\"https:\/\/nanoclaw.bot\/\">NanoClaw \u5b98\u65b9\u7db2\u7ad9<\/a> \u2014 \u5c08\u6848\u7684\u5feb\u901f\u5165\u9580\u6307\u5357\u8207\u6838\u5fc3\u8a2d\u8a08\u7406\u5ff5\u8aaa\u660e<\/li>\n\n\n<li><a href=\"https:\/\/www.anthropic.com\/engineering\/claude-code-sandboxing\">Making Claude Code More Secure and Autonomous (Anthropic Engineering Blog)<\/a> \u2014 Anthropic \u5b98\u65b9\u6df1\u5ea6\u89e3\u8aaa Claude Code \u7684\u6c99\u7bb1\u6a5f\u5236\u8a2d\u8a08\uff0c\u5305\u542b OS \u5c64\u7d1a\u9694\u96e2\u7684\u6280\u8853\u7d30\u7bc0\uff0c\u8207 NanoClaw \u7684 Container \u9694\u96e2\u7406\u5ff5\u4e00\u8108\u76f8\u627f<\/li>\n\n\n<li><a href=\"https:\/\/code.claude.com\/docs\/en\/skills\">Extend Claude with Skills (Claude Code Docs)<\/a> \u2014 Claude Code Skills \u7684\u5b98\u65b9\u6587\u4ef6\uff0c\u8a73\u7d30\u8aaa\u660e SKILL.md \u7684\u683c\u5f0f\u898f\u7bc4\u8207\u9032\u968e\u7528\u6cd5\uff0c\u7406\u89e3 NanoClaw \u7684 Skills over Features \u6a21\u5f0f\u5fc5\u8b80<\/li>\n\n\n<li><a href=\"https:\/\/fumics.in\/posts\/2026-02-02-nanoclaw-agent-architecture\">500 Lines vs. 50 Modules: What NanoClaw Gets Right About AI Agent Architecture<\/a> \u2014 \u6df1\u5ea6\u5206\u6790 NanoClaw \u8207 OpenClaw \u7684\u67b6\u69cb\u5dee\u7570\uff0c\u63a2\u8a0e\u6975\u7c21\u8a2d\u8a08\u5728 AI agent \u9818\u57df\u7684\u512a\u52e2<\/li>\n\n\n<li><a href=\"https:\/\/northflank.com\/blog\/how-to-sandbox-ai-agents\">How to Sandbox AI Agents in 2026: MicroVMs, gVisor &#038; Isolation Strategies (Northflank)<\/a> \u2014 \u5168\u9762\u6bd4\u8f03\u5404\u7a2e AI agent \u6c99\u7bb1\u65b9\u6848\uff08Container\u3001MicroVM\u3001gVisor\u3001OS primitives\uff09\uff0c\u5e6b\u52a9\u4f60\u9078\u64c7\u9069\u5408\u81ea\u5df1\u5834\u666f\u7684\u9694\u96e2\u7b56\u7565<\/li>\n\n<\/ul>\n\n\n<div class=\"wp-like-post__wrapper\" style=\"\"><button type=\"button\" class=\"wp-like-post__button \" style=\"height: 30px; color: #000000;\" data-post-id=\"694\" data-total-likes=\"0\" data-likes-from-user=\"0\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" width=\"30\" height=\"30\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6.633 10.5c.806 0 1.533-.446 2.031-1.08a9.041 9.041 0 012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75 0 01.75-.75A2.25 2.25 0 0116.5 4.5c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987.729-1.605.729H13.48c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 00-1.423-.23H5.904M14.25 9h2.25M5.904 18.75c.083.205.173.405.27.602.197.4-.078.898-.523.898h-.908c-.889 0-1.713-.518-1.972-1.368a12 12 0 01-.521-3.507c0-1.553.295-3.036.831-4.398C3.387 10.203 4.167 9.75 5 9.75h1.053c.472 0 .745.556.5.96a8.958 8.958 0 00-1.302 4.665c0 1.194.232 2.333.654 3.375z\" \/><\/svg><\/button><div class=\"wp-like-post__count\"\">0<\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>NanoClaw \u662f\u4e00\u500b\u53ea\u6709\u5e7e\u500b\u6838\u5fc3\u6a94\u6848\u7684\u500b\u4eba Claude \u52a9\u7406\uff0c\u900f\u904e WhatsApp \u8207\u4f60\u5c0d\u8a71\uff0c\u6bcf\u6b21\u5c0d\u8a71 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rolpb_likes":0,"footnotes":""},"categories":[555],"tags":[617,400,615,616,618,619,26],"class_list":["post-694","post","type-post","status-publish","format-standard","hentry","category-ai-agent-github","tag-ai-native-design","tag-claude-agent-sdk","tag-container-isolation","tag-ipc","tag-security-architecture","tag-skills-pattern","tag-typescript"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":7}},"_links":{"self":[{"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/posts\/694","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/comments?post=694"}],"version-history":[{"count":0,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/posts\/694\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/media?parent=694"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/categories?post=694"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.itsmygo.uk\/wordpress\/wp-json\/wp\/v2\/tags?post=694"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}