<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Aigent - Medium]]></title>
        <description><![CDATA[Creating the future of Artificial Intelligence for Customer Service - Medium]]></description>
        <link>https://medium.com/aigent?source=rss----41f2d656099d---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Aigent - Medium</title>
            <link>https://medium.com/aigent?source=rss----41f2d656099d---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 23:39:36 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/aigent" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Meet nanoQ — high-performance brokerless Pub/Sub for streaming real-time data with Golang]]></title>
            <link>https://medium.com/aigent/meet-nanoq-high-performance-brokerless-pub-sub-for-streaming-real-time-data-with-golang-6630d3067f4e?source=rss----41f2d656099d---4</link>
            <guid isPermaLink="false">https://medium.com/p/6630d3067f4e</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[it]]></category>
            <category><![CDATA[go]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Iurii Krasnoshchok]]></dc:creator>
            <pubDate>Thu, 07 Jan 2021 15:28:11 GMT</pubDate>
            <atom:updated>2021-01-07T15:28:11.044Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>Meet nanoQ — high-performance brokerless Pub/Sub for streaming real-time data with Golang</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/738/1*EfwlqOT7a13v5vWrJdmj3w.png" /><figcaption>Image credit: <a href="https://github.com/egonelbre/gophers">https://github.com/egonelbre/gophers</a></figcaption></figure><p>nanoQ (nQ for short) was made for streaming real-time data: audio, video, clicks, sensors — where <strong><em>it’s okay to lose data</em></strong> to get the most up to date messages quicker.</p><p>The main goal is instant “fire and forget” publish with a best-effort delivery guarantee and support for many (hundreds, thousands) simultaneous streams.</p><p>If you’re “show me the code” kind of programmer, without further due <a href="https://github.com/aigent/nq">github.com/aigent/nq</a></p><h3><strong>Rationale</strong></h3><p>We firmly believe that good software products were made to solve one specific problem (and get bloated and abused for non-intended purposes only afterward).</p><p>At <a href="https://aigent.com/">Aigent</a> we use nQ to transfer VoIP audio and signaling streams through our backend infrastructure. Existing message brokers don’t cover this scenario too well. If a service wants to transfer data to another service, a scheme:</p><pre>Pub → broker → Sub</pre><p>creates additional roundtrip with increased latency and hardware load.</p><p>In some cases, a central broker may become a bottleneck, and especially in mixed workload: fast and slow clients, big and small messages, attempts to scale a broker may result in pain in the neck.</p><p>Instead, using nQ publishers connects directly to subscribers.</p><pre>    /→ Sub1</pre><pre>Pub  → Sub2</pre><pre>    \→ Sub3</pre><p>This approach is very simplistic but powerful: you can build scalable pipelines of arbitrary complexity. Combine this with Kubernetes or cloud service mesh routing and auto-scaler, and you’re golden.</p><p>In the scenario above, service Pub publishes to Sub1, Sub2, and Sub3 simultaneously, imagine services Sub1 and Sub2 are fast but Sub3 is being slow or even inaccessible. What options do we have?</p><ol><li>Slow down processing for all subscribers and create backpressure.</li><li>Buffer incoming messages until Sub3 will be able to keep up.</li><li>Deliver messages to Sub1 and Sub2 at their pace, and drop messages directed to Sub3 — <strong><em>this is what nanoQ does</em></strong>.</li></ol><p>This is an easy implementation of graceful degradation — overall pipeline continues to run, despite some services are underperforming or malfunctioning, all with very low maintenance cost (you don’t need to monitor growing queues or chase the slowest component).</p><p>It works in our case — we want that received data to be fresh anyway. At <a href="https://aigent.com/">Aigent</a> we send hints to call center agents in real-time during a conversation, and to receive a hint about something you spoke about a few minutes ago is not helpful — it’s irritating at best.</p><h3><strong>How nanoQ works</strong></h3><p>I wish every software project has one-two paragraph description of its internal kitchen and design decision instead of thousands of pages of marketing material. So here we go.</p><p>There are some spectacular failures, trials, and errors behind nanoQ that eventually made it.</p><h4><strong>In a Nutshell</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*K9Waqjv9_HP99DV5b2BVDA.png" /><figcaption>nanoQ high-level design</figcaption></figure><h4><strong>TCP vs. UDP</strong></h4><p>You may think that UDP is low latency and “fire and forget” by design, so it has to be an obvious choice. There are two reasons against it: packet loss and congestions.</p><p>UDP drops packet on too many occasions: send rate is too high, CPU is busy with another process, a kernel queue is full. We want to drop packets as a last resort when a receiver is inaccessible but we don’t want to deal with cracks, hisses, and missing pieces of audio because a CPU on of the machines was busy.</p><p>Congestions are even harder to tackle. Imagine you have a 100Mbit/s network, and you send at the 80Mbit/s rate because, hey, hardware must work for the money. All of a sudden, bandwidth drops to 15Mbit/s. The first question is: how do we know that it dropped? The second question is: what to do? We can not continue to send at the previous rate of 80Mbit/s. In the worst-case case scenario, we’ll have congestive collapse, when the link is available but due to exceeding incoming traffic, no packets can reach the destination before timeout. The worst of all is that bandwidth may return to the previous number of 100Mbit/s but due to the existing congestion destinations may seem unreachable, or data may arrive too broken to be meaningful.</p><p>The network protocols built on top of UDP usually have complementary protocols or messages for bandwidth control, jitter avoidance, QoS control, retries on packet loss. For instance, RTP — Real-time Transport Protocol has RTCP — RTP Control Protocol.</p><p>These protocols are quite complex. But then — TCP does all of this, and often very efficient on the hardware level.</p><p>In our case, we can tolerate hundreds of milliseconds delay, and we want to transfer thousands of streams through the same link so we let TCP to take care of all the heavy lifting, and use all the existing diagnostic tools.</p><p>We use TCP/IP, and every stream has its own connection to utilize bandwidth.</p><h4><strong>Wire Protocol</strong></h4><p>The protocol has only one message: data transfer, and it’s as simple as:</p><pre>+--...---+---------...<br>|&lt;length&gt;| payload ...<br>+--...---+---------...</pre><p>The <em>&lt;length&gt;</em> is <a href="https://developers.google.com/protocol-buffers/docs/encoding">Varint</a> 1–5 octets depending on the payload size.</p><blockquote>Varints are a method of serializing integers using one or more bytes. Smaller numbers take a smaller number of bytes.</blockquote><blockquote>Each byte in a varint, except the last byte, has the <em>most significant bit</em> (msb) set — this indicates that there are further bytes to come. The lower 7 bits of each byte are used to store the two’s complement representation of the number in groups of 7 bits, least significant group first.</blockquote><p>So a payload of 0–127 bytes long requires only 1 additional byte of metadata.</p><p>We use nanoQ in the existing backend infrastructure where integrity checking, routing, encryption are already in place. This minimalistic protocol is easy to implement using almost any programming language.</p><h4><strong>API v0 and Implementation details</strong></h4><p>At the very beginning, we decided that nanoQ should be data oblivious: some of our services speak JSON, some — Protobuf or msgpack — whatever works best.</p><p>The publisher’s API is:</p><pre>Publish(ctx context.Context, payload []byte, streamKey interface{}) error</pre><p>So our <em>payload</em> is simply a slice of bytes. The <em>streamKey</em> defines whether to use an existing connection or to create a new one — every new <em>streamKey</em> is a new connection.</p><p>Our subscriber’s API v0 was:</p><pre>type IncomingMsg struct {<br>    Payload            []byte<br>    StreamDescriptor   interface{}<br>}</pre><pre>Subscribe(ctx context.Context) &lt;-chan IncomingMsg</pre><p>And it was a no-brainer to use go channels as internal queues. It was <em>chan []byte</em> all the way.</p><p>It turned out that this design has several critical flaws.</p><p>Let’s start with the publisher.</p><pre>payload := []byte(“Example payload”)<br>// We store the message into the internal queue<br>Publish(ctx, payload, “example”)<br>payload[3] = 0 // Wait! What?! This is illegal!!!</pre><p>Because slice is essentially a pointer, we can not just send <em>payload</em> down the channel — user can still modify the contents, and then:</p><ol><li>it’s a race condition, and</li><li>we’re transferring spoiled data</li></ol><p>So at this point, we had to either explain to every user not to do this, pretty please, or to allocate a copy of each incoming message (guess what we’ve chosen, and write a comment).</p><p>From the subscriber’s part, we had the same problem — API promised that every incoming message will be delivered as its own <em>[]byte</em> slice, so we had to allocate memory for every incoming packet too.</p><p>Is there a problem with allocations? Go is not magical. Nothing is magical. Our job as engineers is to fight magic and to explain how things actually work, a plausible explanation at least — no magic.</p><p>Go goes a great length to convince us that garbage collector and runtime don’t exist (and it’s amazingly convincing). But when you allocate here and there a couple hundred thousand times per second, suddenly the garbage collectors would like a word with you.</p><p>Oh man, CPU usage was high as a kite.</p><h4><strong>Redesign and the current API</strong></h4><p>The pillar of the new design became <a href="https://en.wikipedia.org/wiki/Circular_buffer">circular buffer</a> — we used it for internal queues instead of <em>chan []byte</em>.</p><p>So that we can read and write using the same pre-allocated memory arena.</p><p>Publisher’s API stayed the same:</p><pre>Publish(ctx context.Context, payload []byte, streamKey interface{}) error</pre><p>We just copy the contents of the <em>payload</em> into a circular buffer already in the wire protocol format. Background thread will flush the buffer depending on a <em>FlushFrequency</em> option. This and <em>NoDelay</em> (disable or enable Nagle’s algorithm) options control latency vs. bandwidth “slider”.</p><p>Subscriber’s API became more <em>io.Reader</em> -like:</p><pre>Receive(ctx context.Context, p []byte) (payload []byte, stream StreamDescriptor, err error)</pre><p>The <em>payload</em> is the same slice as <em>p</em> but of the length of the received message.</p><p>Having this API, a user can decide whether to reuse the same memory for each incoming message or to allocate.</p><h4>Built-in Prometheus metrics</h4><p>This part may seem like an overkill for a library, calling itself minimalistic.<br>nanoQ can lose data, and we want a user to know if this is the case. The usual way of a program to programmer communication — logging doesn’t work well. If you’re losing thousands of messages per second it’s not the best idea to produce thousands of log lines. And that’s exactly the reason why metrics were invented.</p><p>We hope that nanoQ will solve some of your problems too. <a href="http://github.com/aigent/nq">Fork us on GitHub</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6630d3067f4e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/aigent/meet-nanoq-high-performance-brokerless-pub-sub-for-streaming-real-time-data-with-golang-6630d3067f4e">Meet nanoQ — high-performance brokerless Pub/Sub for streaming real-time data with Golang</a> was originally published in <a href="https://medium.com/aigent">Aigent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using Automated Speech Recognition to Support Human Transcriptions]]></title>
            <link>https://medium.com/aigent/using-automated-speech-recognition-to-support-human-transcriptions-39c6b0ae7b76?source=rss----41f2d656099d---4</link>
            <guid isPermaLink="false">https://medium.com/p/39c6b0ae7b76</guid>
            <category><![CDATA[annotation-tools]]></category>
            <category><![CDATA[rsa]]></category>
            <category><![CDATA[human-transcription]]></category>
            <category><![CDATA[annotations]]></category>
            <category><![CDATA[data-acquisition]]></category>
            <dc:creator><![CDATA[Eoin Watts]]></dc:creator>
            <pubDate>Thu, 08 Oct 2020 12:27:40 GMT</pubDate>
            <atom:updated>2020-10-09T12:41:08.904Z</atom:updated>
            <content:encoded><![CDATA[<p>In early 2020 Aigent built a Speech Model with a Word Error Rate (WER) of 9%. For those new to Speech Recognition, WER is a common metric used to measure the <a href="https://medium.com/@bethworthy/word-error-rate-mechanism-asr-transcription-and-challenges-in-accuracy-measurement-d9fbe3773dda">accuracy of speech model transcriptions</a>. Aigent’s Speech Model’s WER is particularly impressive given the model is trained upon data from a notoriously tricky domain - call center conversations between US consumers and customer service agents from the Philippines.</p><p>To get the quality of transcriptions required<strong> </strong>to train such a model we built an extremely resource-heavy Transcription Process. For a transcription to be defined as Gold Standard, we required that at least four human transcribers came into contact with it. Such a resource-heavy process was an option for us because we have a huge Data Acquisition Team in the Philippines (see <a href="https://medium.com/aigent/building-a-data-acquisition-team-from-scratch-b1f74558568c">here</a> for more information on how we set this team up).</p><p>Now, we want to streamline this entire process and utilize our state-of-the-art Speech Model to support our Data Acquisition Team. Ultimately, we hope to reduce the 30.5 active human working hours it takes for one hour of audio to pass through the entire process. This will not only allow us to get transcriptions in a more timely manner, but also free up our human resources to do a wider range of data acquisition tasks, from labelling text to annotating audio.</p><h3>The Plan</h3><p>Our current time-consuming Transcription Process looks as follows:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lG5gONLJcTtPeqj1Gtfj2A.png" /><figcaption>Image 1: Aigent’s current Transcription Process Flow</figcaption></figure><p>Audio (in our case complete two-channel phone conversations) is passed through this process from left to right.</p><ul><li>First the audio is manually labelled in our Audio Labelling Tool, with unusable audio weaned out and other labels such as emotion and speaking rate added.</li><li>Next, the audio is split into sentence-sized segments by an automated tool.</li><li>The audio is then routed to a human transcriber who transcribes the audio from scratch.</li><li>To avoid bias, the very same audio is then transcribed again from scratch by a different transcriber.</li><li>A human Quality Assurance (QA) round is then completed. An experienced transcriber is presented with both of the previous round transcriptions and has to make a decision on which is correct or make adjustments where both are wrong.</li><li>A final human QA verification round is completed before the audio can be classified as Gold Standard.</li></ul><p>For this initial investigation into utilizing Automated Speech Recognition (ASR) to support our Transcription Process, we decided to simply replace one of the most resource-heavy initial transcription rounds (Transcription Round 2) with an automated transcription completed by our Speech Model.</p><p>To give our model the ultimate test, we created a fifteen-hour dataset containing audio recordings from a call center program that our Speech Model has not been trained on. Within ten days we had our results.</p><p>To evaluate the success of replacing a human transcription round we accounted for a number of quantitative and qualitative metrics. They are as follows:</p><p><strong>Transcription Speed</strong></p><ul><li>How is the speed of the overall process affected?</li><li>How is the speed of the specific stages affected?</li></ul><p><strong>Transcription Quality</strong></p><ul><li>What is the WER between QA Round 1 and the ASR Transcription Round? How much worse is this WER than that of a human round?</li><li>Is the QA Round 1 quality affected? (this is determined by calculating the WER between QA Round 1 and QA Round 2).</li></ul><p><strong>User Observations</strong></p><ul><li>What do the impacted users (particularly the QA Round 1 Team) think of the change? I.e. do they use ASR transcriptions? Or do they just ignore them and focus on the human round?</li></ul><h3>Transcription Speed</h3><p>We were certain going into this task, that by substituting one of the two human transcription rounds with a transcription completed by our ASR model that we would see a significant decrease in the overall time taken for audio to be transcribed.</p><p>We were, however, less sure about the impact of this on the QA rounds, particularly the first QA round. Would introducing a non-human transcription round make these subsequent QA rounds more difficult and thus increase the time taken to QA?</p><p>The below table outlines the time taken for one hour of audio to pass through each of the 5 stages. In the <strong>Normal Transcription Process (1 hour)</strong> column you can see how long it takes for 1 hour of audio to be transcribed using just human labor. In the <strong>ASR Supported Transcription Process (1 hour)</strong> column you can see how long it took<strong> </strong>for 1 hour of audio to be transcribed after incorporating ASR transcriptions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p-03_NslD6DMKj77W-TAaA.png" /><figcaption>Table 1: Transcription Speed</figcaption></figure><p>The results are conclusive and clearly show that by incorporating ASR transcriptions into the process, Aigent will significantly reduce the time taken for audio to pass from non-transcribed to Gold Standard. For each hour of audio we would save over nine hours of manual human work.</p><p>However, it should be noted that the time taken to transcribe in QA Round 1 does increase substantially after introducing ASR transcriptions. This is partly because the first QA stage is now harder and requires more effort from these users, who can rely less upon a second round transcription. This spike can probably also be partly explained by the QA Team making observations about the ASR quality while transcribing this dataset. In all likelihood, as the QA Team adapt to this change, the time spent will converge with the Old Transcription Process.</p><h3>Transcription Quality</h3><p>When evaluating the benefits of incorporating ASR transcriptions into the Transcription Process it is vital to take into account the impact on transcription quality- i.e. the WER of transcriptions. For instance, if the overall speed of the Transcription Process increases, but we also witness a significant dip in overall transcription quality, then the costs of implementing such a change may outweigh the benefits.</p><h4>ASR Transcription Quality</h4><p>We calculate the quality of the transcriptions by comparing the ASR Transcription Round and the Human Transcription Round to the first human Quality Assurance Round.</p><p>In the below table, the WER percentage, where the lower the percentage the more accurate the transcription, is displayed. For this 15 hour dataset of 250 calls, you can see the <strong>Average WER</strong> for a human transcriber and the ASR round. You can also see the <strong>Max WER</strong> and <strong>Min WER</strong> to get a grasp of the quality range within and between the two rounds.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8DYeP1dWLcQk2SqYbPEbVA.png" /><figcaption>Table 2: Transcription Quality (compared to Quality Assurance Round)</figcaption></figure><p>In line with expectations, the ASR transcriptions are on average three times less accurate than human transcriptions. The accuracy range (difference between Max and Min WER) of these ASR transcriptions also varies much more than with human transcribers. This is because when a human makes a mistake it generally is a word here or there. The ASR on the other hand, although often accurate, can get whole speech segments incorrect.</p><p>Other interesting observations in terms of the quality difference between the human and ASR transcriptions are that:</p><ul><li>on one occasion the ASR Transcription round has a lower WER than the Human Transcription round (12% vs 18%).</li><li>the average difference between the WER score of the ASR and Human transcription is 16.5%. Normally the WER difference between the Round 1 &amp; Round 2 transcriptions is closer to 4%.</li></ul><h4>QA Round 1 Transcription Quality</h4><p>As mentioned, we expected the ASR transcription quality to be significantly worse than that of a trained human transcriber, particularly as the Speech Model is not trained on this audio. However, we were unsure how strong the impact of introducing this new round would be on the quality of the first QA Round. It is certainly feasible that the added distraction of this new, less accurate and non-human round will negatively impact the quality of the first QA transcription (which is measured by calculating the WER between this round and the second QA Round).</p><p>To evaluate this we compared the WER between the QA Round 1 and QA Round 2 for a normal 15- hour dataset and the WER between the QA Round 1 and QA Round 2 for the dataset which included ASR transcriptions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1MBzF5Iq6mLB4Cy7jUsAxQ.png" /><figcaption>Table 3: QA Round 1 Quality (compared to Quality Assurance Round 2)</figcaption></figure><p>The above table shows that the quality output for the first QA Round does not seem to be particularly impacted by the introduction of the ASR Transcription Round. The Average WER does increase slightly but the spike is negligible. Also, the Max WER is actually lower for this the ASR supported dataset and in both datasets the Min WER is 0% (i.e. the QA Round 2 transcription is identical to the QA Round 1 transcription).</p><h3>User Observations</h3><p>Lastly, it’s important to look at the observations that the users most affected by this change — the QA Round 1 Team — noted while correcting the transcriptions.</p><p>As the higher WER would imply, the team pointed out that they were much less likely to select the ASR transcription than the human round. They reported that for audios with minimal background noise and clear speech the ASR transcription was more accurate and thus more helpful. But when the audio became noisier or the speaker had a stronger accent, the Speech Model struggled. On a number of these occasions the Speech Model produced truncated transcriptions, missing large parts of speaker turns.</p><p>As a result of this a number of the QA Round 1 Team admitted that they lost confidence in the ASR transcription. Many began ignoring this round’s transcription altogether, making the conscious decision to simply focus on the human round and their own judgement. Our user behavior metrics confirmed that users were considerably more likely to click on the human transcription round and correct it, even when both rounds contained similar transcriptions.</p><h3>Next Steps</h3><p>By simply examining the hard facts from the above investigation it would seem obvious that permanently replacing one of the resource-heavy human transcription rounds with an ASR transcription round would be a logical next step. Such a move would shred over nine hours of human labor from the overall transcription speed and have little detriment to the overall transcription quality output.</p><p>But, these quantitative metrics fail to represent the full story. The observations from our QA Team suggest that these improvements are more a consequence of removing the human transcription round, rather than a positive consequence of adding an ASR transcription round.</p><p>Due to these observations we have decided to investigate a further shake-up of our Transcription Process. Rather than directly replacing a human transcription with an ASR transcription, we want to cease manually transcribing audio from scratch and alternatively have a human correct an initial ASR transcription. Image 2 outlines how this future flow could look.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HU_7B0Jbp2fumz9mPVtYKg.png" /><figcaption>Image 2: Aigent’s potential Transcription Process Flow</figcaption></figure><p>We expect such a change to have a profoundly greater impact on our Transcription Process. Further reducing the time taken for Aigent’s Speech Team to get the vast amount of transcriptions required to train the model.</p><p>Nevertheless, there are certainly risks that such a method will cement and exasperate the model’s inherent bias. Potentially, transcribers will subconsciously fail to correct some of the model’s errors, which will then be fed back into the model through its training.</p><p>We look forward to finding out and sharing the results with you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=39c6b0ae7b76" width="1" height="1" alt=""><hr><p><a href="https://medium.com/aigent/using-automated-speech-recognition-to-support-human-transcriptions-39c6b0ae7b76">Using Automated Speech Recognition to Support Human Transcriptions</a> was originally published in <a href="https://medium.com/aigent">Aigent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Working from home? Aigent is here to help]]></title>
            <link>https://medium.com/aigent/working-from-home-aigent-is-here-to-help-d6cbaf518336?source=rss----41f2d656099d---4</link>
            <guid isPermaLink="false">https://medium.com/p/d6cbaf518336</guid>
            <category><![CDATA[compliance]]></category>
            <category><![CDATA[call-center]]></category>
            <category><![CDATA[covid-19-crisis]]></category>
            <category><![CDATA[contact-center]]></category>
            <category><![CDATA[working-from-home]]></category>
            <dc:creator><![CDATA[Eoin Watts]]></dc:creator>
            <pubDate>Wed, 06 May 2020 15:59:32 GMT</pubDate>
            <atom:updated>2020-05-06T15:59:32.429Z</atom:updated>
            <content:encoded><![CDATA[<p>The impact from Covid-19 has sent shock waves through the business world. All of us have been forced to adapt entrenched habits in order to navigate new realities. This shock has been particularly felt in contact centers, where the Work From Home (WFH) culture has never really taken off. Industry bosses have long resisted WFH for a range of security and technical reasons. High employee turnover rates mean that setting up WFH teams isn’t always practical. Moreover, the amount of sensitive data agents come across on a daily basis means that a centralized office environment provides companies a necessary security safety-net.</p><p>The recent pandemic has changed this overnight. Leaders have been forced to transition large parts of their workforce away from the office and to their own homes. This rapid transition is causing sleepless nights for executives, who first had to make this possible by setting up their new remote workforce and now have the arguably more difficult task of maintaining current service standards.</p><p>After speaking to a number of industry experts, from executives to team leads and to agents themselves, we uncovered some of their main concerns. These range from the suitability of the home environment to questions of security and compliance. Aigent’s state-of-the-art speech recognition can alleviate many of the challenges related to both of these concerns.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IUEssKQ787pWGrpVEl3_wQ.png" /><figcaption>Aigent’s Promise</figcaption></figure><h3>Security &amp; Compliance</h3><p>Transitioning to WFH has led to a lot of executives and team leads feeling as though they’ve lost the oversight of their agents which they normally had in the office. Without colleagues and supervisors in earshot, agents may be more willing to go off-script, cut corners in the verification process or even worse, ask the customer for sensitive information which they do not need.</p><p>Aigent analyses calls in real-time and immediately detects when an agent goes off-script. For instance, if an agent asks for a customer’s card expiry date, Aigent will send a warning message to the agent. This call will also be flagged in our Client Dashboard for team leads to quickly monitor and take necessary action.</p><p>This in-call guidance also helps with less critical, but more common compliance issues, such as agents leaving customers on hold for too long or failing to offer a client survey at the end of the call. Many of these problems existed previously but have <a href="https://hbr.org/2020/04/supporting-customer-service-through-the-coronavirus-crisis?utm_campaign=hbr&amp;utm_source=facebook&amp;utm_medium=social&amp;fbclid=IwAR1_OaVJuiUzAWmGcoQnEiWFYwXM9VMBUp2-E_xIHLKOGdD1VKQxg_i6jiM">proliferated since agents have begun WFH</a>. In these cases, Aigent will send agents reminder messages if they fail to follow the program’s policy.</p><p>These compliance features compliment Aigent’s guidance features, which provide agents with tips on how to solve a customer’s concern in real time. For example, if a caller is requesting for a password reset to be done on their account, Aigent will send the agent a message with step-by-step by instructions on how to do this. This improves call-handle time and first-call-resolution metrics, therefore helping to address a range of customer challenges in a more timely manner.</p><p>By combining these two methods, Aigent can address the root cause of many compliance problems and maintain a safety net for edge cases.</p><h3>Suitable Working Environment</h3><p>While working from home serves convenience, many contact center agents are worried about this setting, especially those who live with others or in a noisy neighborhood. Can the customer hear the kids in the other room or the dog barking outside? Aigent’s background noise detector uses acoustics to determine the level of background noise stemming from the agent’s line and gives agents tips on how to decrease its impact on the call. When background noise levels creep up, we remind agents to “adjust their headset speaker”, or if the noise gets too disruptive, to “find somewhere quieter”.</p><p>Junior agents also expressed increased concerns about transferring calls to supervisors who they no longer have regular personal interaction with. Aigent’s Regulatory Complaint Prevention feature determines when an agent is in danger of losing control of a conversation and should transfer the customer to their supervisor. Ultimately, we help provide confidence that <strong>Aigent is here to help, 24/7.</strong></p><p>The switch to remote working is forcing the contact center industry to rethink their operations and take drastic measures to continue providing critical service to their customers. Aigent offers solutions through the lens of change and uncertainty.</p><p><a href="https://aigent.com/">Contact us</a> to learn more about building and maintaining a successful work from home environment.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d6cbaf518336" width="1" height="1" alt=""><hr><p><a href="https://medium.com/aigent/working-from-home-aigent-is-here-to-help-d6cbaf518336">Working from home? Aigent is here to help</a> was originally published in <a href="https://medium.com/aigent">Aigent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Makefiles for Python and beyond]]></title>
            <link>https://medium.com/aigent/makefiles-for-python-and-beyond-5cf28349bf05?source=rss----41f2d656099d---4</link>
            <guid isPermaLink="false">https://medium.com/p/5cf28349bf05</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[build-software]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[makefile]]></category>
            <category><![CDATA[make]]></category>
            <dc:creator><![CDATA[Iurii Krasnoshchok]]></dc:creator>
            <pubDate>Fri, 28 Feb 2020 15:19:38 GMT</pubDate>
            <atom:updated>2020-02-28T15:19:38.157Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WcMxdVA_nKoyq6yiII1OOQ.png" /></figure><p>Makefiles are quite unpopular in the dynamic languages world. Some Python and JavaScript developers consider GNU make an ancient, deprecated, outdated, and dying tool, used by some dinosaurs.</p><p>The truth is: make is a wonderful and often misunderstood tool. It is fairly simple yet very powerful. Hell, source-based Linux distros and BSD operating systems heavily rely on make.</p><p>There are many application for Makefiles outside of C/C++ world, and I think that make deserves more attention and love.</p><h3>Why does Python even need a Makefile?</h3><p>The short answer is: to avoid the “It worked on my machine” situation. Python does a very good job to abstract OS layer, although Python application still needs some kind of packaging before it can be distributed, or deployed to a server.</p><p>In general, Makefile is a good alternative to a bunch of bash scripts used to automate tedious tasks.</p><p>First of all, instead of remembering and typing ill <em>./do_this_one_thing.sh</em> <em>./do_the_other_one</em> (was it dash or underscore?), you can confidently strike: <em>make one two</em>.</p><p>But that’s a joke, of course.</p><p>The real reason is: as soon as there is more than one bash script, the hidden dependencies may arrive. You have to run <em>script_1</em> before running <em>script_2</em>. Another problem is copy-paste code (each bash file tends to hold the same paths, artifacts names, and whatnot).</p><h3>Make for the rescue</h3><p><em>Feel free to skip this intro if you’re already familiar with make.</em></p><p>All make does is tracks files’ <strong>dependency graph</strong>, and “<strong>last modified</strong>” timestamps of the files. If one of dependencies timestamp is greater than the one of the original file, the original file needs to be rebuilt.</p><p>Every time, one file has to be built from another, make is a good alternative for the job. You may think of building a .jpg from .dot files, or .mpeg4 from video, audio and subtitles files, or .html files build from .j2 or .pug templates. Python itself does the very same thing with .pyc files.</p><p>A Makefile consists of “<strong>rules</strong>” in the following form:</p><pre>target: dependency1, dependency2, …<br>&lt;TAB&gt;recipe line<br>&lt;TAB&gt;recipe line<br>…</pre><p><strong>target</strong> is a file needs to be built; <strong>dependency1 </strong>and <strong>dependency2 </strong>are source files used to build the <strong>target</strong>, and recipe is a shell command used as a build step.</p><p>If you want to just copy a file, you may write something like:</p><pre>dist/config.yaml: src/config.yaml<br>    cp -f src/config.yaml dist/config.yaml</pre><h3>What is all this have to do with Python again?</h3><p>Okay, let us make a simple application.</p><p>Let’s say we want to make an application that shows random cat gifs (<em>the example is shamelessly hijacked from the glorious </em><a href="https://elm-lang.org/examples/cat-gifs"><em>Elm tutorial</em></a>)</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5bebbb0d5fbb772a24ea9e60ab7ecb8a/href">https://medium.com/media/5bebbb0d5fbb772a24ea9e60ab7ecb8a/href</a></iframe><p>So now you want to show this application to your buddies. No — the whole World must to see it in its magnificence.</p><p>The application should render something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/455/1*ZL36kHW8jDD379MeUfdL2w.gif" /></figure><p>The only problem is:</p><pre>Traceback (most recent call last):<br> File “./app.py”, line 3, in &lt;module&gt;<br> import requests<br> ModuleNotFoundError: No module named ‘requests’</pre><p>Okay, apparently simple <em>python3 app.py</em> may not work.</p><p>Let’s make a virtual environment, and install requests module in it. requirements.txt file with the long list of all the requirements:</p><p>requests</p><p>Long, long list.</p><p>(Trying to pull out real-world example out of the thin air here).</p><p>And the virtual environment itself:</p><pre>$ python3 -m venv venv<br>$ ./venv/bin/pip install -r requirements.txt</pre><p>We may even want to save this into some <em>build.sh</em> file.</p><p>Now all we need is:</p><pre>$ ./venv/bin/python3 app.py</pre><p>And we also may want to save this one-liner into some run.sh.</p><p>So this is the very common situation I was talking about in the beginning: we need to run <em>build.sh</em> <strong><em>before</em></strong> <em>run.sh</em>, and this dependency is hidden and nowhere stated.</p><p>Let’s see if this make stuff will be more clear.</p><p>The first rule in our Makefile will be the virtual environment, but “<em>venv”</em> is a directory, and we need a plain file, so let’s take any file from the “<em> venv”</em>. For instance, “<em>venv/bin/activate”</em> will do:</p><pre>venv/bin/activate: requirements.txt<br>    python3 -m venv venv<br>    ./venv/bin/pip install -r requirements.txt</pre><p>Now if we run <em>make</em> in the project’s directory, we’ll get virtual environment build with requests installed. We had it before, with bash, but now dependency is tracked, so running make the second time will give us:</p><pre>$ make<br>make: `venv/bin/activate’ is up to date.</pre><p>If we will edit requirements.txt — add or remove dependencies, make will rebuild the virtual environment for us automagically:</p><pre>$ touch requirements.txt  # simulate edit<br>$ make<br>python3 -m venv venv<br>./venv/bin/pip install -r requirements.txt<br>Requirement already satisfied: requests in ./venv/lib/python3.7/site-packages…</pre><p>Now, let’s make the second target called <em>run</em> to run our application:</p><pre>run: venv/bin/activate<br>    ./venv/bin/python3 app.py</pre><p>run explicitly depends on the virtual environment target, so now you only have to type <strong><em>make run</em></strong> and dependencies will be created or updated before running the application.</p><p>So far so good, but we need to go deeper.</p><p>First of all, we don’t have the file named run; make thinks that this file will be created after executing the recipe, so it all works. Although, if someone would decide to created a file named <em>run</em> the behavior of make would become unexpected.</p><p>Let’s fix it by explicitly saying that we don’t expect a file to be produced.</p><p>.PHONY: run</p><p>Now <em>run </em>is a phony target, and it will be re-evaluated every time it is called (that’s exactly what we need).</p><p>We may want to add the <em>clean</em> target to remove what we don’t need:</p><pre>clean:<br>    rm -rf venv<br>    find . -type f -name ‘*.pyc’ -delete</pre><p>Don’t forget that the clean target also doesn’t produce a file:</p><p>.PHONY: run clean</p><p>By default, running make without arguments will execute the first rule from the Makefile. It is considered a good style to define an entry target called all that should define a default behavior. In our case, it will create the virtual environment.</p><p>all: venv/bin/activate</p><p>And again:</p><p>.PHONY: all run clean</p><h3>Want more make?</h3><p>This is where some people draw the line in the sand, but make also has variables, functions, and macros.</p><p>Variables are really simple:</p><p>NAME := VALUE</p><p>defines a variable, that can be refered by ${NAME} or $(NAME)</p><p>There is also some useful shortcuts:</p><p>NAME ?= VALUE</p><p>Assign VALUE to the variable NAME if it’s not yet defined.</p><p>This is especially useful because you may define variables while executing make:</p><p>$ make BUILD=RELEASE ENVIRONMENT=PRODUCTION</p><p>There is also a possibility to declare an alias, like this:</p><p>NAME = VALUE</p><p>In this case, right hand side will be evaluated every time variable is accessed.</p><p>This can be explained easily by the following example</p><pre>v1 := $(shell ls)<br>v2 = $(shell ls)</pre><p>Both variables hold the result of the shell ls command execution, but v1 will be evaluated only once, and will never show any new files.</p><p>By the way, shell is a make functions. Hooray, we’ve also learned the make functions!</p><p>Let’s wrap up everything we’ve learned just now:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/96e99babf150b20124dcd593c0a119f0/href">https://medium.com/media/96e99babf150b20124dcd593c0a119f0/href</a></iframe><p>Easy, right?</p><h3>What else?</h3><p>You may want to add your favorite linter and formatter: pep8, yapf, pyflakes — you name it. You can generate gRPC or swagger stub, you can generate documentation from .rst or .md files.</p><h3>A shiny new hammer</h3><p>Why not use make for everything? Literally everything?!</p><p>It is very good tool for tracking dependencies between files with benefits, but when targets are dynamics, like docker images, or deployments… Well, things may get ugly and unpredictable. Builds may be haunted.</p><h3>TL;DR</h3><p>Makefiles still rocks in 2020! Feel free to use make outside of the C/C++ world having fun joy and pleasure.</p><p>Every time a file needs to be created from other files, GNU make is a good candidate for the job.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5cf28349bf05" width="1" height="1" alt=""><hr><p><a href="https://medium.com/aigent/makefiles-for-python-and-beyond-5cf28349bf05">Makefiles for Python and beyond</a> was originally published in <a href="https://medium.com/aigent">Aigent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Data Acquisition Team from Scratch]]></title>
            <link>https://medium.com/aigent/building-a-data-acquisition-team-from-scratch-b1f74558568c?source=rss----41f2d656099d---4</link>
            <guid isPermaLink="false">https://medium.com/p/b1f74558568c</guid>
            <category><![CDATA[annotation-tools]]></category>
            <category><![CDATA[annotations]]></category>
            <category><![CDATA[transcription]]></category>
            <category><![CDATA[annotation-processing]]></category>
            <category><![CDATA[data-acquisition]]></category>
            <dc:creator><![CDATA[Eoin Watts]]></dc:creator>
            <pubDate>Fri, 24 Jan 2020 17:12:33 GMT</pubDate>
            <atom:updated>2020-01-27T08:51:18.695Z</atom:updated>
            <content:encoded><![CDATA[<p>At Aigent we are using artificial intelligence to provide real-time support to call center agents. This in-call assistance decreases overall call-handling time, reduces agent training, improves customer satisfaction and ultimately increases company profits.</p><p>In order to create features which make the above possible we needed to train our own Automatic Speech Recognition system. As anyone in the field will testify these features and products require a lot of data. This data comes in the form of transcriptions (in our case the literal verbatim transcription of speech into text) and annotations (i.e. the labelling of both audio and text).</p><p>This blog post presents the reader with a comprehensive overview of Aigent’s evolving data acquisition process. Detailing how we got to where we are today and offering a glimpse of what future steps we expect to introduce.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*hJFVvOvQJJ19o5T_.jpeg" /><figcaption>Image 1: Meet the Bacolod Team</figcaption></figure><h3>The Journey</h3><p>Conveniently, I find myself writing this blog post on my way home from Bacolod, Philippines after visiting Aigent’s main data acquisition source. This is the third time I have visited the aptly nicknamed “City of Smiles”, located on Negros Island, in the Western Pacific. Since the first time I visited Bacolod in 2018 a lot has changed. In terms of numbers, the program has witnessed exponential growth. From the eleven employees (ten Transcribers and one Team Lead) that we had in November 2018 we now have forty-eight Annotators, three Team Leads and one Operations Manager.</p><p>To accommodate this growth, Aigent has relocated to a bigger office in a new building (with plenty of room to accommodate further growth). In this large space, the team’s identity can really be observed. Aigent posters line the wall and Annotators who have recently been rewarded for their performance, show off their new Aigent t-shirts and coffee mugs.</p><p>However, before we established our own Data Acquisition Team, we pursued a number of other data sourcing methods.</p><h3>Method 1: Online Datasets</h3><p>We utilized online datasets to satisfy our search for data, such as the extensive <a href="https://catalog.ldc.upenn.edu/LDC97S62">Switchboard Corpus</a>. These datasets, due to their high word accuracy, have certainly been pivotal in the evaluation of our speech models.</p><p>But the lack of domain bias in these datasets limits their use when it comes to training a model for our specific business context.</p><h3>Method 2: Limited Internal Drive</h3><p>When it comes to raw data, we found ourselves in the unique and advantageous position of having access to an unlimited pool of domain specific audio files. However, we spent the best part of a year attempting to find a reliable, effective and cost-efficient way of converting this raw audio into usable transcriptions and annotations.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*f2KNW-QWEyxasimz.png" /><figcaption>Image 2: Aigent’s own Transcription Tool</figcaption></figure><p>Our initial efforts to solve this problem led us to employ a small number of part-time students. Aigent’s prime location in Amsterdam, makes it relatively straightforward to attract native English-speaking students looking for part-time work to support their studies and gain work experience. These students certainly helped us create and define our own data acquisition process. The fact that these students worked in the office allowed developers to quickly test out new transcription tools and get instant feedback. Nevertheless, the nature of part-time work meant this couldn’t provide us the quantity of data needed to train our models. Also in terms of return, it is a relatively expensive data acquisition method.</p><h3>Method 3: Outsourcing</h3><p>The third method we explored involved outsourcing this transcription task to a third-party. This multinational company was contracted to provide us 100 hours of high-quality transcription, using our client’s audio. Undoubtedly, these transcriptions will prove to be very useful in the development of our domain biased speech model.</p><p>But to yield 100 hours of transcription a lot of work has been logged on our own end. For instance, the pre-processing of the initial datasets, to align with their particular format, took weeks of work.</p><p>On the return of the transcriptions we also found that the quality did not meet the expected standard and guidelines were only loosely adhered to. Thus, these transcriptions required an internal quality check from our side. Due to this quality lapse, some data was even returned to the third-party to be transcribed again.</p><p>Essentially, the outsourcing of these transcriptions involved a significant amount of insourcing. Yet it was this insourcing that motivated us to seek alternative solutions. The work logged here, laid the groundwork and developed the internal infrastructure that allowed Aigent to realistically pursue the following method of establishing our own, permanent, Data Acquisition Team.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3Pk7q382QsMdc2YF.png" /><figcaption>Image 3: Aigent’s Data Acquisition Flow</figcaption></figure><h3>Method 4: Large-scale Internal</h3><p>The above experiences, as well as our insider access to Ubiquity’s (Aigent’s parent company and a large US call center) resources, led us to make the leap of establishing the Data Acquisition Team in Bacolod in November of 2018. Thus far, having our own full-time team has proved by some distance the most successful way of satisfying our data deficiency. Having our own large team means we can quickly gather domain specific transcriptions and annotations, without sacrificing direct control over the process.</p><p>In the first twelve months, roughly 3000 hours of audio were transcribed by the team in Bacolod. To put this in perspective, these figures not only dwarf what our part-time students produced, but also the third-party company. In half the time it took us to receive 100 hours of transcriptions from the third-party, our Data Acquisition Team in Bacolod have produced over ten times as many transcriptions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OGpkbMfMC5nni7_Z5dfehQ.png" /><figcaption>Image 4: Data Source Method Comparison</figcaption></figure><h3>Challenges</h3><p>This is not to say that having our own team has magically solved all of our data sourcing problems. The balance between transcription <em>quality</em> and transcription <em>quantity</em>, is one which still defines our process. However, the direct control we have over a large team allows us to quickly implement changes, adapt our processes accordingly and witness instant results. As mentioned above, this flexibility is absent when it’s outsourced to a third-party.</p><p>It would be disingenuous not to mention the logistical obstacles in running an annotation center on the other side of the world. Unlike many careers in the Philippines, Aigent offers its employees a day-time shift. This six to seven-hour time difference, depending on the time of the year, means there is just a two to three-hour window where the Amsterdam team and the Bacolod team are both working. Therefore, technical hiccups can result in a few hours of lost work.</p><p>Regular formal and informal communication channels help to address many of the above challenges. In fact, these logistical problems have been noticeably less damaging than our initial estimates suggested.</p><p>There are also a number of benefits associated with the time difference. The time delay allows us to differentiate our processes. Meaning our team in Bacolod can work independently of our Amsterdam office and vice versa. This space facilitates positive change as the Amsterdam office can analyse the work of the annotators and have clear concrete feedback ready for when the Bacolod team begin working again the following day.</p><p>A good example of this time difference working to our benefit is in regards to a pilot we are currently running with one of our US based clients. This client takes calls during the US daytime, and by the time they finish receiving calls our Bacolod team arrive to their office to begin their daytime shift. A sub-section of the team then annotate all the calls from the previous day, usually 1,200 recordings, which have been automatically placed in a purpose-built tool using a script developed by one of the developers here in Amsterdam. By the time the Amsterdam team arrive to the office, they can observe these annotations and, if needed, have the necessary time to make adjustments to the model or process. These changes are implemented before the client starts taking calls. This process is repeated daily and allows for clear and accurate analysis which results in positive change.</p><p>Similarly, the threat of outages has led to improvements in internal troubleshooting steps, the creation of monitoring logs and driven general tool enhancements. Without the threat of outages and lost working time, it is arguable that these permanent solutions would have been sidelined in the place of more ad-hoc workarounds.</p><p>Finally, these sociable working hours help to maintain a positive working environment in Bacolod and ensures that Aigent remains an attractive employment option. These unseen benefits are by their nature difficult to measure, but their importance shouldn’t be underestimated.</p><h3>Mission Accomplished?</h3><p>The first fourteen months of the program have been a success. The target of establishing a sizeable skilled and motivated workforce has been completed. As a result of this, Aigent now has a stable, efficient and scalable way of converting raw audio into usable transcriptions and annotations.</p><p>Looking ahead, 2020 will be a big year for Aigent and the Data Acquisition Team. The reality of onboarding and integrating multiple new clients will test the speed that raw data can be transformed into usable data.</p><p>Yet, we’re confident that Aigent’s infrastructure and personnel can handle this pressure. Bring on 2020.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b1f74558568c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/aigent/building-a-data-acquisition-team-from-scratch-b1f74558568c">Building a Data Acquisition Team from Scratch</a> was originally published in <a href="https://medium.com/aigent">Aigent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>