调戏截图

image.png
image.png
欢迎关注我的公众号“哈德韦”,然后开始调戏。如果你碰到问题发现它不回答了,那一定是我没钱了!欢迎打赏,好让我给 AWS 充钱。
不知道能活多久,且调戏且珍惜。

源代码

全部源代码在这里:https://github.com/Jeff-Tian/bedrock ,后面进行详细讲解。

对接步骤

尽管我这个人工智能小助手,由于经费的问题可能会随时停止服务,但接下来的对接步骤却是长期都有价值的,因为只要有钱,可以使用该步骤分分钟建立起一个新的服务。

开通服务

开通之后,AWS 的 Bedrock 面板如下:
image.png

配置密钥

新增加一个密钥对,以便在后面的代码中,通过它们来调用 AWS 的服务。
image.png

写点代码

需要写一点点代码,来调用 AWS Bedrock 的服务,我这里使用 koa 创建了一个新的服务,并通过@aws-sdk/client-bedrock-runtime 来简化对 AWS Bedrock 服务的 API 交互。

调包侠的核心代码

就一个文件,不妨命名为 bedrock.js,代码如下: javascript const { BedrockRuntimeClient, InvokeModelCommand, } = require(@aws-sdk/client-bedrock-runtime);

module.exports = { async chat({question, modelId, maxTokensToSample, temperature, topP, topK}) { const input = { modelId: modelId ?? anthropic.claude-v2, contentType: application/json, accept: application/json, body: JSON.stringify({ prompt: Human:nYour task is to answer questions. Follow the instruction closely, but dont mention the instructions in your reply. Your answer must be a properly formatted plain text. Do not add any additional remarks or notes. Do not act like a chatbot, but do act like a real person, and your name is 哈德韦 ... If the question content is English then respond with English. If the question content is in Chinese, then respond in Chinese. If its in other language, then respond with the language too.nDo not lose the original meaning.nYou must keep the text as plain text and make it as short as possible without any line breaks as the text will be sent back to wechat official accounts in the wechat app, the space is limited. Dont think too long, respond quickly with short text is totally OK.nnContent:n${question}nnAssistant:, max_tokens_to_sample: maxTokensToSample ?? 2000, temperature: temperature ?? 1, top_p: topP ?? 1, top_k: topK ?? 250 }) }

const client = new BedrockRuntimeClient({
  region: us-east-1,
  credentials: {
    accessKeyId: process.env.AWSAccessKeyId,
    secretAccessKey: process.env.AWSSecretAccessKey
  }
});

try {
  const command = new InvokeModelCommand(input);
  const response = await client.send(command);

  const rawRes = response.body;
  const jsonString = new TextDecoder().decode(rawRes);

  console.log(-------------------------);
  // Answers are in parsedResponse.completion
  console.log(jsonString);
  console.log(-------------------------);

  const json = JSON.parse(jsonString);
  return json.completion;
} catch (ex) {
  console.error(chat error = , ex);

  return 抱歉,服务出错了,原因是: ${ex.message};
}

} }

这就文件就一个方法,即 chat。注意,这里没有使用流式交互,因为目前还没有探索出如何在和微信公众号的 API 打交道的过程中使用流式交互。但是在别的地方,使用流式交互效果非常好,敬请期待下次分享。

注意文件中 AWS 密钥是从环境变量里读取的,而注入的过程通过 K8S 的 secrets 实现。

另外,我的提示词里让模型尽量回复得简洁,以及当用户使用中文提问,就用中文回复等,没想到,它就真的够简洁:
image.png
服务器端日志:
WX20231225-190653@2x.png

应用代码

也就一个文件,app.js,要启用该应用,只需要 node app.js即可,全部代码如下: javascript const Koa = require(koa); const app = new Koa(); const router = require(@koa/router)(); const convert = require(xml-js);

const {koaBody} = require(koa-body); const {getFromUserName, getToUserName, getContent} = require(./helpers/wechat); const {chat} = require(./ai/bedrock);

app.use(koaBody({includeUnparsed: true}));

router.get(/message, async (ctx, next) => { console.log(received message: , ctx.query); ctx.body = ctx.query.echostr; });

router.post(/message, async (ctx) => { const xmlString = ctx.request.body; console.log(xml = , xmlString); const json = convert.xml2js(xmlString); console.log(received message: , json);

const content = getContent(json);
console.log(content = , content);

const rtnMsg = await chat({
		question: content
});

const toUserName = getFromUserName(json);
const fromUserName = getToUserName(json);
const createTime = new Date().toLocaleDateString(en-US, {
    weekday: long,
    month: long,
    day: numeric,
    year: numeric,
});

console.log(starting reply:  + rtnMsg +  to  + toUserName +  from  + fromUserName);
const xml = <xml>
<ToUserName><![CDATA[${toUserName}]]></ToUserName>
<FromUserName><![CDATA[${fromUserName}]]></FromUserName>
<CreateTime>${createTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[${rtnMsg}]]></Content>
ctx.set(Content-Type, text/plain);
ctx.body = xml;

})

app.use(router.routes());

app.listen(8080);

总共两个路由,一个是 GET /message,用来重复接收到的微信服务器发来的消息,这个路由虽然简单,但却必不可少,因为在微信后台配置服务时,微信需要收到它的回复,才能允许你配置成功;另一个是 POST /message,用来处理微信转发的来自用户的消息,不过消息的格式是 XML,所以这里用了 xml-js来将 xml 文本解析成 json。
其中依赖的一个帮助文件,写得非常简单,如下,可以看出不过是从 json 表示的 xml 数据里取出对应的值而已。 javascript module.exports = { getFromUserName: (xml) => { return xml.elements[0].elements[1].elements[0].cdata; }, getToUserName: (xml) => { return xml.elements[0].elements[0].elements[0].cdata; }, getContent: (xml) => { return xml.elements[0].elements[4].elements[0].cdata; } }

发布

本来将它发布到了 Azure 的免费 Web 应用上,实测效果不理想。因为冷启动时间太长,导致多数消息得不到回复。于是再次利用 Okteto,部署在 k8s 环境里,并且将 pod 副本数设置成了 7,以期能够应对较多的流量。 image.png

之前多次介绍过 Okteto,比如《【免费架构】Heroku 不免费了,何去何从之 Keycloak 的容器化部署之路 - Jeff Tian的文章 - 知乎 》。不过坏消息是,Okteto 从 2024 年 1 月 15 日之后,也将停止免费服务!所以,即使 AWS 账户里的钱没用完,我的公众号也可能因为 Okteto 停止免费服务而停止响应(Okteto 最便宜的计划也是 1 个月 149 美元,真心用不起呀……)。还是那句话:且调戏且珍惜。

要将应用发布到 Okteto,只需要将应用容器化,再配置一下 GitHub 流水线自动部署即可。具体可以参考前面一段提到的文章,或者这一篇:《免费架构:Heroku 不免费了,何去何从之 eggjs 的容器化部署之路 - Jeff Tian的文章 - 知乎 》,不像前一篇是 Java 应用,这也是一个 nodejs 应用的示例。

唯一不同之处,在于这一次我尝试了私有 Docker 镜像:
image.png

由于不能直接 docker pull jefftian/bedrock 了,因此 k8s 的 deployment 文件有些变化,即增加了一个拉取镜像的配置:

yaml imagePullSecrets: - name: regcred

这对应地需要在 k8s 里创建一个名为 regcred 的 secret,该 secret 明文如下所示,不过在代码仓库里看不到如下明文,因为通过 SOPS (sops -e -i k8s/secrets.yaml --aws-profile the-profile)加密了。

apiVersion: v1 kind: Secret metadata: name: regcred labels: branch: main type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: ewo...KfQo=

�注意这里的 .dockerconfigjson 是一串 base64 编码的字符串,其解码后的形式是这样的 JSON: json { auths: { https://index.docker.io/v1/: { username: jefftian, password: dckr_pat_...-..., email: jeff.tian@outlook.com, auth: amVm...0NkNB } } }

注意以上密码,其实是一个访问令牌,而 auth,实际上是 jefftian:访问令牌的 base64 编码。该访问令牌需要从 Docker 后台创建:
image.png

发布完成后,就可以通过 https://bedrock-backstage-jeff-tian.cloud.okteto.net/message?echostr=xml 访问到了:
image.png
以上请求成功后,就可以开始配置微信后台了。

配置微信后台

在基本配置中填写以上 URL: https://bedrock-backstage-jeff-tian.cloud.okteto.net/message ,保存即可:
image.png

对接完成

这样就可以开始调戏啦!不过,也不要太频繁调戏人家哦,很费钱的!
image.png

本篇文章请大家不要点赞, 不要分享,自己留着玩就行啦!