최근에 여러 플랫폼에서 단계들을 연결하는 작업에 대해 다양한 실험을 해보았는데, 그중에서도 특히 흥미를 느낀 하나를 공유하려 합니다. 바로 이미지 내 텍스트를 감지하는 광학 문자 인식(OCR)을 수행한 후, 그 결과를 Discourse 게시글에 추가하는 것입니다.
내 샌드박스 토픽에서 어떻게 작동하는지 확인할 수 있습니다: https://notes.maiki.interi.org/t/testing-ocr-calls/403
처음에는 Pipedream을 사용했으며, 단계별로 진행 과정을 설명드리겠습니다. 해당 서비스에는 워크플로를 공유할 수 있는 베타 기능이 있으며, 아래 내용은 https://pipedream.com/new?h=tch_3Z6fa9에서 공유하고 있습니다.
이 글은 Pipedream을 Discourse와 함께 사용하는 방법에 대한 심층 가이드가 될 수 있지만, 저는 이미 해당 플랫폼을 떠나 더 많은 내용을 나중에 공유할 예정이므로 세부 사항은 가볍게 다룰 것입니다. 
단계
대략적인 개요는 다음과 같습니다: 이미지 URL을 Google Cloud Vision API로 전송하고, 결과를 ChatGPT를 통해 처리한 뒤, 그 결과를 Discourse의 게시글에 추가합니다.
트리거

이것은 데이터를 전송할 웹훅을 제공합니다. Discourse에서는 두 가지 특정 설정으로 웹훅을 생성했습니다:

'Post Events’에서만 발동되도록 하면 초기 토픽이 프로세스를 트리거하지 않습니다. 이는 외부 함수를 적용하기 위한 용도로 토픽을 홀더로 사용할 수 있어 저에게 유용합니다(저는 이를 "기능적 노트"라고 부릅니다).
해당 태그에서만 발동되도록 하면 태그를 사용하여 토픽별로 생성되는 웹훅을 제어할 수 있습니다. 일반적으로 프로세스 로직을 단순하게 유지하기 위해 “functions” 태그 하나만 사용합니다.
웹훅은 많은 정보를 포함한 페이로드를 전송하며, 프로세스 후반부에 토픽 ID와 게시글 ID를 사용할 것입니다.
조건부 종료
이 단계는 수정 이유가 포함되었는지 확인합니다. 포함되면 워크플로가 중단됩니다.
마지막 단계에서 게시글을 업데이트하고 수정 이유를 포함하며, 이 확인 단계는 게시글을 계속 업데이트하는 것을 방지합니다… 
Pipedream 사용을 중단한 이유 중 하나는 웹훅 체크가 서비스 크레딧을 소모했기 때문입니다. 조건부로 웹훅을 처리하는 데 비용을 지불해야 한다고 생각하지는 않으므로, 다른 곳으로 옮겼습니다…
이미지 URL 추출
이 테스트에서는 각 게시글에 하나의 이미지만 업로드하기로 결정했습니다. 이 단계는 “cooked” 값을 확인하고 다음 정규식을 사용하여 URL을 가져옵니다:
/https?:\/\/[^\s"]+/
Google Cloud Vision API 호출
이것은 Pipedream의 사용자 정의 코드 단계입니다. 미리 만들어진 컴포넌트는 제가 원하는 것을 수행하지 못했고, 해당 서비스에는 프롬프트로 코드를 작성할 수 있는 코드 어시스턴트도 있습니다. 이러한 API 호출은 직관적이므로 이 방법으로 생성하기가 쉬웠습니다.
이전 단계의 값({{steps.extract_by_regular_expression.$return_value[0].match}})을 받아들이며, 코드는 다음과 같습니다:
import { axios } from "@pipedream/platform";
export default defineComponent({
props: {
imageUrl: {
type: "string",
label: "Image URL",
description: "URL of the image to be processed by Google Vision API",
},
apiKey: {
type: "string",
label: "API Key",
description: "Your Google Cloud API Key",
secret: true,
},
},
async run() {
const url = `https://vision.googleapis.com/v1/images:annotate?key=${this.apiKey}`;
const body = {
requests: [
{
image: {
source: {
imageUri: this.imageUrl,
},
},
features: [
{
type: "TEXT_DETECTION",
},
],
},
],
};
const config = {
method: "POST",
url,
data: body,
};
const response = await axios(this, config);
return response;
},
});
편집을 위한 ChatGPT
이전 단계의 출력({{steps.google_cloud.$return_value.responses[0].fullTextAnnotation.text}})을 가져와 User 메시지로 전달합니다. 시스템 메시지는 다음과 같습니다:
당신은 이미지에서 텍스트를 감지한 비전 API의 출력을 읽고 있습니다. 메시지를 검토하여 명확성을 위해 편집하세요. 주석 없이 편집된 텍스트만 반환하세요.
Discourse 게시글에 추가
또 다른 사용자 정의 코드 섹션으로, 미리 만들어진 Pipedream의 Discourse 액션은 몇 가지 시나리오(토픽 또는 게시글 생성)만 다루기 때문에, 저는 텍스트를 게시글에 추가하고 싶습니다.
먼저, 코드는 다음과 같습니다:
import { axios } from "@pipedream/platform";
export default defineComponent({
props: {
discourse: {
type: "app",
app: "discourse",
},
postId: {
type: "string",
label: "Post ID",
description: "The ID of the post to append text to",
},
text: {
type: "string",
label: "Text",
description: "The text to append to the post",
},
editReason: {
type: "string",
label: "Edit Reason",
description: "The reason for editing the post",
optional: true,
},
},
async run({ steps, $ }) {
const url = `https://${this.discourse.$auth.domain}/posts/${this.postId}.json`;
const response = await axios($, {
method: "GET",
url: url,
headers: {
"Api-Username": `${this.discourse.$auth.api_username}`,
"Api-Key": `${this.discourse.$auth.api_key}`,
},
});
const updatedText = `${response.raw} ${this.text}`;
return await axios($, {
method: "PUT",
url: url,
headers: {
"Api-Username": `${this.discourse.$auth.api_username}`,
"Api-Key": `${this.discourse.$auth.api_key}`,
},
data: {
post: {
raw: updatedText,
edit_reason: this.editReason,
},
},
});
},
});
해당 단계의 속성은 다음과 같이 채워집니다:
Post ID
원래 페이로드에서 게시글 ID를 가져옵니다: {{steps.trigger.event.body.post.id}}
이것은 해당 게시글을 직접 편집하는 데 사용됩니다.
Text
---
<blockquote>
{{steps.chat.$return_value.generated_message.content}}
</blockquote>
[details="Detected text"]
{{steps.google_cloud.$return_value.responses[0].textAnnotations[0].description}}
[/details]
기본적으로 각 이미지 아래에 수평선, 편집된 텍스트의 인용 블록, 그리고 원시 출력을 확인하기 위한 세부 정보를 추가하려고 합니다.
각 게시글에 이미지가 하나씩 있으므로 이것은 매우 쉽게 작동합니다. 여러 이미지를 동시에 처리하는 방법은 어떻게 될지 궁금합니다. 
Edit Reason
OCR Text Detection
이것은 게시글 업데이트의 수정 이유로 추가되며, 시작 부분의 단계로 인한 게시글 업데이트 루프를 방지합니다.

특히 외부 서비스를 다룰 때 수정 이유를 항상 포함하는 것이 매우 유용하다고 생각합니다.
이것으로 끝입니다! 샌드박스에서 볼 수 있듯이 꽤 잘 작동합니다!
곧 여행을 가는데, OpenAI 편집을 미세 조정하여 필요시 영어로 번역하도록 할 계획입니다. 이 워크플로의 시스템 프롬프트에 추가할 수 있는 옵션 중 하나입니다.