跳到主内容
快讯直播
AI智模界
教程

用 Floci 给编码智能体搭一套本地云沙箱

适用场景

当你写的编码智能体(Coding Agent)需要调用对象存储、消息队列这类云服务才能跑通完整流程时,用真实云账号做集成测试会带来三个麻烦:要配密钥、要花流量费、断网或 CI 环境里直接跑不起来。用 Floci 在本机把云服务模拟出来,就能拿到一个稳定的假 S3 和假队列,让智能体在没有云账号、没有外网、没有真实密钥的环境里把工具调用链路完整跑一遍。

环境与前置条件

项目要求
操作系统Linux / macOS;Windows 建议用 WSL2
容器运行时Docker 引擎 + Docker Compose 插件
Python3.9 及以上(示例用 boto3 调用)
内存建议 8 GB 起步,同时跑容器和本地模型时再往上加
磁盘预留 10 GB 以上给镜像和模拟数据
网络首次拉取镜像需要外网,之后可完全离线

不需要云账号,不需要真实的 Access Key。示例里的 test / test 是这类本地模拟服务的通用占位凭证,具体是否校验签名以 Floci 官方文档当前版本为准。

分步骤部署

第 1 步:确认容器运行时可用

```bash

docker version

docker compose version

```

这一步只是确认 Docker 客户端能连上守护进程。成功标志是 docker version 同时打印出 Client 和 Server 两段信息——如果只有 Client,说明 Docker 守护进程没启动。

第 2 步:启动 Floci 模拟服务

Floci 以容器方式运行最省事。镜像名称、标签和启动参数以官方文档当前版本为准,下面是通用写法:

```bash

docker run -d \

--name floci \

-p 4566:4566 \

--restart unless-stopped \

<floci-image>

```

如果官方提供了 CLI 启动方式,用官方命令即可,端口保持一致。

说明:-p 4566:4566 把容器端口映射到本机,4566 是本地云模拟服务常见的默认端口,如果你的版本默认端口不同,后面所有 --endpoint-url 都要跟着改。

成功标志:

```bash

docker ps --filter name=floci

```

STATUS 一列显示 Up ... 即为正常。若容器秒退,先看日志:

```bash

docker logs --tail 50 floci

```

第 3 步:准备假凭证与环境变量

```bash

export AWS_ACCESS_KEY_ID=test

export AWS_SECRET_ACCESS_KEY=test

export AWS_DEFAULT_REGION=us-east-1

export AWS_ENDPOINT_URL=http://localhost:4566

清掉可能干扰的配置来源

unset AWS_PROFILE

unset AWS_SESSION_TOKEN

unset HTTP_PROXY HTTPS_PROXY ALL_PROXY

```

这步在做什么:AWS SDK 的凭证链会优先读环境变量,再读 ~/.aws/credentials。把 AWS_PROFILE 和 AWS_SESSION_TOKEN 清掉,是为了防止智能体进程意外捞到本机真实凭证,从而连到线上环境。

把这些变量写进项目根目录的 .env 或 docker-compose.yml 里更稳妥,注意别提交包含真实密钥的文件。

第 4 步:用 AWS CLI 打通 S3 链路

```bash

aws s3 mb s3://agent-sandbox --endpoint-url http://localhost:4566

aws s3 ls --endpoint-url http://localhost:4566

echo "hello sandbox" > hello.txt

aws s3 cp hello.txt s3://agent-sandbox/hello.txt --endpoint-url http://localhost:4566

aws s3 ls s3://agent-sandbox/ --endpoint-url http://localhost:4566

```

mb 是 make bucket,ls 列出桶或桶内对象。成功标志是最后一条命令能看到 hello.txt 这一行。能看到它,说明假 S3 已经通了。

新版本 AWS CLI 支持通过 AWS_ENDPOINT_URL 环境变量自动改写端点,此时可以省略 --endpoint-url,以官方文档当前版本的行为为准。

第 5 步:创建假队列

```bash

aws sqs create-queue --queue-name agent-tasks --endpoint-url http://localhost:4566

aws sqs list-queues --endpoint-url http://localhost:4566

```

create-queue 会返回一个 JSON,里面的 QueueUrl 形如:

```

http://localhost:4566/000000000000/agent-tasks

```

把这个 URL 原样复制下来,导出成环境变量:

```bash

export QUEUE_URL=http://localhost:4566/000000000000/agent-tasks

```

这个 URL 里带端口和账号段,不要自己拼字符串,直接取返回值最省事。

第 6 步:写一个 Agent 工具层

智能体真正调用的是函数,所以在 SDK 外面包一层薄薄的工具函数,把端点固定成本地:

```python

tools.py

import os

import json

import boto3

from botocore.config import Config

ENDPOINT = os.environ.get("AWS_ENDPOINT_URL", "http://localhost:4566")

_client_kwargs = dict(

endpoint_url=ENDPOINT,

region_name="us-east-1",

aws_access_key_id="test",

aws_secret_access_key="test",

config=Config(retries={"max_attempts": 3, "mode": "standard"}),

)

s3 = boto3.client("s3", **_client_kwargs)

sqs = boto3.client("sqs", **_client_kwargs)

def put_object(bucket: str, key: str, body: str) -> str:

"""把字符串写进假 S3,返回对象地址。"""

s3.put_object(Bucket=bucket, Key=key, Body=body.encode("utf-8"))

return f"s3://{bucket}/{key}"

def get_object(bucket: str, key: str) -> str:

"""从假 S3 读回字符串。"""

obj = s3.get_object(Bucket=bucket, Key=key)

return obj["Body"].read().decode("utf-8")

def send_task(queue_url: str, payload: dict) -> str:

"""往假队列投递一条 JSON 任务。"""

resp = sqs.send_message(QueueUrl=queue_url, MessageBody=json.dumps(payload))

return resp["MessageId"]

def receive_tasks(queue_url: str, max_messages: int = 10) -> list:

"""从假队列拉取任务,返回带 receipt 的列表。"""

resp = sqs.receive_message(

QueueUrl=queue_url,

MaxNumberOfMessages=max_messages,

WaitTimeSeconds=1,

)

out = []

for msg in resp.get("Messages", []):

out.append({

"id": msg["MessageId"],

"body": json.loads(msg["MessageBody"]),

"receipt": msg["ReceiptHandle"],

})

return out

def delete_task(queue_url: str, receipt: str) -> None:

"""确认任务处理完毕,从队列里删掉。"""

sqs.delete_message(QueueUrl=queue_url, ReceiptHandle=receipt)

TOOLS = {

"put_object": put_object,

"get_object": get_object,

"send_task": send_task,

"receive_tasks": receive_tasks,

"delete_task": delete_task,

}

```

关键点是 endpoint_url 写死在本地地址上。这样无论宿主机的环境变量怎么变,智能体发出的请求都不会跑到真实云端。

接下来把 TOOLS 的键和函数签名转成 function calling 的 JSON Schema 注册给模型,或者在固定流程里直接按名字取函数调用,两种方式都可以。

第 7 步:跑一次端到端工具调用

```python

demo.py

import os

import json

from tools import put_object, get_object, send_task, receive_tasks, delete_task

BUCKET = "agent-sandbox"

QUEUE_URL = os.environ["QUEUE_URL"]

print("1) 写对象:", put_object(

BUCKET, "reports/run-001.json", json.dumps({"status": "ok", "score": 42})

))

print("2) 读对象:", get_object(BUCKET, "reports/run-001.json"))

message_id = send_task(QUEUE_URL, {

"action": "summarize",

"target": f"s3://{BUCKET}/reports/run-001.json",

})

print("3) 投递任务:", message_id)

for task in receive_tasks(QUEUE_URL):

print("4) 取到任务:", json.dumps(task["body"], ensure_ascii=False))

delete_task(QUEUE_URL, task["receipt"])

print("5) 收尾完成")

```

运行:

```bash

QUEUE_URL=http://localhost:4566/000000000000/agent-tasks python demo.py

```

预期输出五行,最后一行是 5) 收尾完成。这五步就是一个最小闭环:写报告 → 读回来 → 投递任务 → 消费任务 → 确认删除。把 put_object / send_task 这类函数注册成工具后,智能体自己就能按需串起这条链路。

第 8 步:断网验证

关掉 Wi-Fi、拔掉网线,或断开 VPN。因为模拟服务和 Agent 都跑在本机,localhost 通信不受影响。再跑一次:

```bash

python demo.py && echo "OFFLINE OK"

```

看到 OFFLINE OK,就说明整条链路确实没有偷偷访问真实云。这一步是整篇教程里价值的一部分——它能证明你的测试是真隔离的。

验证部署是否成功

按顺序核对下面几项,全过就算搭好了:

1. docker ps --filter name=floci 显示状态为 Up。

2. aws s3 ls --endpoint-url http://localhost:4566 能列出 agent-sandbox。

3. aws s3 ls s3://agent-sandbox/ --endpoint-url http://localhost:4566 能看到 hello.txt。

4. aws sqs list-queues --endpoint-url http://localhost:4566 返回的 URL 里包含 agent-tasks。

5. 模拟服务端口可直连:

```bash

curl -s -o /dev/null -w "%{http_code}\n" http://localhost:4566/

```

只要不是 000(连接失败),就说明端口在监听。

6. 断网后 python demo.py 正常退出并打印 OFFLINE OK。

常见报错与解决

报错 1:EndpointConnectionError: Could not connect to the endpoint URL: "http://localhost:4566/"

原因:模拟服务没启动、容器已退出,或者端口没有正确映射到宿主机。

解决:

```bash

docker ps -a --filter name=floci

docker logs --tail 50 floci

docker run -d --name floci -p 4566:4566 <floci-image>

```

报错 2:InvalidAccessKeyId 或 The AWS Access Key Id you provided does not exist in our records.

原因:进程读到的是真实云凭证或空凭证,AWS_PROFILE 指向了别的配置。

解决:

```bash

unset AWS_PROFILE AWS_SESSION_TOKEN AWS_CONFIG_FILE

export AWS_ACCESS_KEY_ID=test

export AWS_SECRET_ACCESS_KEY=test

export AWS_DEFAULT_REGION=us-east-1

```

报错 3:SignatureDoesNotMatch

原因:请求被 HTTP 代理拦截后重新签署,或者 AWS_SESSION_TOKEN 残留导致签名头和模拟服务预期不一致。

解决:

```bash

unset HTTP_PROXY HTTPS_PROXY ALL_PROXY

unset AWS_SESSION_TOKEN

```

报错 4:An error occurred (NoSuchBucket) when calling the PutObject operation

原因:桶还没建,或者建桶时用了 A 端点、写入时用了 B 端点。

解决:统一使用同一个端点重新建桶。

```bash

aws s3 mb s3://agent-sandbox --endpoint-url http://localhost:4566

```

报错 5:An error occurred (QueueDoesNotExist)

原因:QueueUrl 是手写的,漏了账号段或端口。

解决:从返回值里原样取。

```bash

aws sqs list-queues --endpoint-url http://localhost:4566

export QUEUE_URL=http://localhost:4566/000000000000/agent-tasks # 以实际返回为准

```

报错 6:容器启动即退出,日志里有 exec format error

原因:镜像架构和本机 CPU 架构不匹配(例如在 Apple Silicon 上跑 amd64 镜像)。

解决:

```bash

docker run -d --platform linux/amd64 --name floci -p 4566:4566 <floci-image>

```

报错 7:bind: address already in use

原因:4566 端口已被别的进程占用。

解决:

```bash

lsof -i :4566

换一个宿主机端口,例如 -p 4567:4566

docker run -d --name floci -p 4567:4566 <floci-image>

```

换端口后,记得同步更新 AWS_ENDPOINT_URL、QueueUrl 和代码里的 endpoint_url。

后续维护

数据持久化与备份。 容器删除后模拟数据一并消失。建议挂载数据卷启动:

```bash

docker run -d --name floci -p 4566:4566 \

-v floci-data:/var/lib/floci \

<floci-image>

```

(容器内数据目录以官方文档说明为准。)除此之外,定期把假 S3 里的对象同步到本地目录做离线备份更直接:

```bash

aws s3 sync s3://agent-sandbox ./backup/agent-sandbox --endpoint-url http://localhost:4566

```

把 backup/ 加进版本控制或对象存储,就是一份可回滚的快照。

升级。 拉新镜像前先导出数据,再删旧容器重建。具体的镜像标签策略以 Floci 官方文档当前版本为准。升级后重点回归两件事:桶和队列能否正常创建、demo.py 是否仍然全绿。

```bash

docker pull <floci-image>

docker rm -f floci

docker run -d --name floci -p 4566:4566 -v floci-data:/var/lib/floci <floci-image>

python demo.py

```

日志与排查。 服务侧看容器日志:

```bash

docker logs -f floci

```

客户端侧开 SDK 调试日志,能看到每条请求打到哪个端点:

```bash

export AWS_DEBUG=1

或在 Python 里

import logging; logging.basicConfig(level=logging.DEBUG)

```

放进 CI 当健康检查。 把第 7 步的 demo.py 当作冒烟测试,在流水线里先起容器、再跑脚本、最后退出码非零就失败。跑完加一段清理,避免用例之间互相污染:

```bash

aws s3 rm s3://agent-sandbox --recursive --endpoint-url http://localhost:4566

aws s3 rb s3://agent-sandbox --endpoint-url http://localhost:4566

aws sqs delete-queue --queue-url "$QUEUE_URL" --endpoint-url http://localhost:4566

```

凭证卫生。 沙箱里只用 test / test 这类占位值。不要把真实 Access Key 写进 .env、docker-compose.yml 或 CI Secrets,否则某天环境变量加载顺序一变,智能体的测试请求就可能打到真实账号上,产生意料之外的账单和数据污染。

资源限制。 在 CI 这类共享机器上给容器加内存和 CPU 上限,避免沙箱挤占其他任务:

```bash

docker run -d --name floci -p 4566:4566 \

--memory 2g --cpus 2 \

<floci-image>

```

把这套沙箱接进智能体之后,开发循环会变得很短:改代码、跑测试、看结果,全程不需要网络也不需要密钥。等逻辑在假 S3 和假队列上稳定了,再切到真实云做一次小规模验证,风险和维护成本都会低不少。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。