<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>GenAI Tracing on RockB</title><link>https://baeseokjae.github.io/tags/genai-tracing/</link><description>Recent content in GenAI Tracing on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 20 Aug 2026 07:02:10 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/genai-tracing/index.xml" rel="self" type="application/rss+xml"/><item><title>TencentCloud AgentObs SDK for DeepSeek Harness: Zero-Collector GenAI Trace Observability</title><link>https://baeseokjae.github.io/posts/tencentcloud-agentobs-dsh-genai-traces/</link><pubDate>Thu, 20 Aug 2026 07:02:10 +0000</pubDate><guid>https://baeseokjae.github.io/posts/tencentcloud-agentobs-dsh-genai-traces/</guid><description>Add deepseek harness observability without an OTLP collector: the TencentCloud AgentObs DSH plugin pushes GenAI traces straight to CLS.</description><content:encoded><![CDATA[<p>DeepSeek Harness observability usually means standing up an OpenTelemetry collector, a tracing backend, and code instrumentation. The TencentCloud AgentObs SDK for DSH eliminates all of that: it is a DeepSeek Harness plugin that observes the harness&rsquo;s native session, agent-loop, LLM-stream, and tool lifecycles and uploads GenAI trace spans directly to Tencent Cloud Log Service (CLS) using Protobuf. There is no OTLP collector, no sidecar, and no extra service to deploy. If you already run agents on Tencent Cloud, this is the fastest path from &ldquo;where did my agent spend its tokens?&rdquo; to a dashboards-and-alerts answer.</p>
<h2 id="what-is-the-tencentcloud-agentobs-sdk-for-deepseek-harness">What is the TencentCloud AgentObs SDK for DeepSeek Harness?</h2>
<p>The TencentCloud AgentObs SDK for DSH (<code>tencentcloud-agentobs-sdk-dsh</code>) is a first-party observability plugin for DeepSeek Harness. Rather than treating DSH as a black box that you instrument from the outside, it hooks directly into the harness&rsquo;s own lifecycle events. Those events — a session starting, an agent being invoked, each ReAct round, an LLM stream, and every tool execution — are converted into spans that follow Tencent Cloud&rsquo;s AI Agent observability specification, then shipped to CLS through the official <code>tencentcloud-cls-sdk-js</code> transport.</p>
<p>The key architectural decision is that the plugin is transport-native to a cloud vendor&rsquo;s log service. Where most DSH observability plugins speak OpenTelemetry and require an OTLP endpoint, this one talks Protobuf directly to Tencent Cloud Log Service. For teams already paying for CLS, that removes an entire class of operational burden.</p>
<h2 id="how-does-dsh-genai-trace-observability-work-without-a-collector">How does DSH GenAI trace observability work without a collector?</h2>
<p>The zero-collector architecture is the plugin&rsquo;s defining feature. The data path is short and self-contained:</p>
<ol>
<li>DeepSeek Harness fires native lifecycle events during a session.</li>
<li>The AgentObs plugin&rsquo;s CLS Trace Coordinator receives those events.</li>
<li>Each event is normalized into Tencent Cloud&rsquo;s 5-layer span model.</li>
<li>Spans are batched and serialized with <code>tencentcloud-cls-sdk-js</code> (Protobuf).</li>
<li>Batches are uploaded directly to a CLS topic over HTTPS.</li>
</ol>
<p>There is no OpenTelemetry Collector to run, no Jaeger or SigNoz backend to maintain, and no sidecar container to schedule. Contrast that with the standard OpenTelemetry DeepSeek approach, which requires you to deploy an OTLP receiver and run <code>opentelemetry-distro</code> instrumentation on top of an OpenAI-compatible SDK. That approach is portable and vendor-neutral, but it is also infrastructure you must operate. The Tencent plugin trades portability for a dramatically simpler deploy.</p>
<h2 id="what-is-the-5-layer-span-model-for-ai-agent-observability">What is the 5-layer span model for AI agent observability?</h2>
<p>Tencent Cloud&rsquo;s AI Agent observability spec organizes traces into a five-layer span hierarchy: entry, agent, step, chat, and tool. The plugin maps DSH&rsquo;s native lifecycle onto that model, so a single DSH turn produces a nested trace:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 336 89"
      >
      <g transform='translate(8,16)'>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='0' y='20' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='8' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='16' y='20' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>R</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>Y</text>
<text text-anchor='middle' x='32' y='20' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='32' y='36' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='40' y='20' fill='currentColor' style='font-size:1em'>G</text>
<text text-anchor='middle' x='40' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='48' y='20' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='48' y='36' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='56' y='20' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='64' y='20' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='64' y='36' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='64' y='52' fill='currentColor' style='font-size:1em'>├</text>
<text text-anchor='middle' x='64' y='68' fill='currentColor' style='font-size:1em'>└</text>
<text text-anchor='middle' x='72' y='36' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='72' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='72' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='20' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='80' y='36' fill='currentColor' style='font-size:1em'>E</text>
<text text-anchor='middle' x='80' y='52' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='80' y='68' fill='currentColor' style='font-size:1em'>─</text>
<text text-anchor='middle' x='88' y='20' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='88' y='36' fill='currentColor' style='font-size:1em'>P</text>
<text text-anchor='middle' x='96' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='96' y='52' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='96' y='68' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='104' y='20' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='104' y='36' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='104' y='52' fill='currentColor' style='font-size:1em'>H</text>
<text text-anchor='middle' x='104' y='68' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='112' y='20' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='112' y='36' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='112' y='52' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='112' y='68' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='120' y='20' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='120' y='36' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='120' y='52' fill='currentColor' style='font-size:1em'>T</text>
<text text-anchor='middle' x='120' y='68' fill='currentColor' style='font-size:1em'>L</text>
<text text-anchor='middle' x='128' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='128' y='36' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='20' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='136' y='36' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='136' y='52' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='136' y='68' fill='currentColor' style='font-size:1em'>(</text>
<text text-anchor='middle' x='144' y='20' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='144' y='36' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='144' y='52' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='144' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='152' y='20' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='152' y='52' fill='currentColor' style='font-size:1em'>h</text>
<text text-anchor='middle' x='152' y='68' fill='currentColor' style='font-size:1em'>x</text>
<text text-anchor='middle' x='160' y='20' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='160' y='36' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='160' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='160' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='168' y='20' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='168' y='36' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='168' y='52' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='168' y='68' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='176' y='20' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='176' y='36' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='176' y='68' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='184' y='20' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='184' y='36' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='184' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='184' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='192' y='36' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='192' y='52' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='192' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='200' y='36' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='200' y='52' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='200' y='68' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='208' y='36' fill='currentColor' style='font-size:1em'>N</text>
<text text-anchor='middle' x='208' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='208' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='216' y='36' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='216' y='52' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='216' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='224' y='52' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='224' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='232' y='52' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='232' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='240' y='52' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='248' y='52' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='248' y='68' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='256' y='52' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='256' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='264' y='52' fill='currentColor' style='font-size:1em'>)</text>
<text text-anchor='middle' x='264' y='68' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='272' y='68' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='280' y='68' fill='currentColor' style='font-size:1em'>_</text>
<text text-anchor='middle' x='288' y='68' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='296' y='68' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='304' y='68' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='312' y='68' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='320' y='68' fill='currentColor' style='font-size:1em'>)</text>
</g>

    </svg>
  
</div>
<p>This nesting is what makes the trace useful. Instead of a flat list of spans, you see the whole shape of an agent turn: which round of ReAct a tool call happened in, which model answered which prompt, and where the time and tokens actually went. When you open a trace in CLS, you can drill from the top-level entry down to a single tool invocation in a specific reasoning step.</p>
<h2 id="what-span-semantics-are-recorded-under-the-hood">What span semantics are recorded under the hood?</h2>
<p>The plugin does not invent a proprietary trace format; it aligns with OpenTelemetry conventions. Each span carries the standard identifiers and metadata you would expect from a well-behaved tracer:</p>
<ul>
<li><code>traceID</code> as a 32-hex-character string</li>
<li><code>spanID</code> and <code>parentSpanID</code> as 16-hex-character strings</li>
<li>OpenTelemetry span kinds (<code>INTERNAL</code>, <code>CLIENT</code>, <code>SERVER</code>)</li>
<li>Status codes (<code>OK</code>, <code>ERROR</code>, <code>UNSET</code>)</li>
<li>Nanosecond-precision start, end, and duration fields</li>
<li>Token usage and model identifiers on chat spans</li>
</ul>
<p>This OTel alignment matters for two reasons. First, it keeps the data recognizable to engineers already comfortable with distributed tracing. Second, it makes correlation possible inside Tencent Cloud: because spans live in CLS, you can join GenAI traces against logs, metrics, and cost analytics in the same log platform, enabling session-to-token-cost analysis without a separate tracing database.</p>
<h2 id="how-do-i-install-the-dsh-plugin">How do I install the DSH plugin?</h2>
<p>Installation uses DeepSeek Harness&rsquo;s native plugin mechanism. You add the plugin to whichever profile you are running, then restart the harness for it to take effect:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>dsh plugin --profile web add tencentcloud-agentobs-sdk-dsh
</span></span><span style="display:flex;"><span><span style="color:#75715e"># or</span>
</span></span><span style="display:flex;"><span>dsh plugin --profile headless add tencentcloud-agentobs-sdk-dsh
</span></span><span style="display:flex;"><span><span style="color:#75715e"># or</span>
</span></span><span style="display:flex;"><span>dsh plugin --profile harness add tencentcloud-agentobs-sdk-dsh
</span></span></code></pre></div><p>Because the profile flag selects where the plugin applies, you can enable tracing only on the headless automation profile and leave interactive sessions untouched — or vice versa. The restart requirement is worth remembering: adding the plugin is not enough, DeepSeek Harness must be restarted before the observer attaches.</p>
<p>One installation pitfall applies to pnpm v9 or later. The plugin depends on <code>protobufjs</code>, which has a build script. Modern pnpm blocks build scripts by default, so you must approve them or the Protobuf serialization layer will not build:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pnpm approve-builds
</span></span></code></pre></div><h2 id="how-do-i-configure-the-plugin">How do I configure the plugin?</h2>
<p>Configuration is handled either through environment variables or through a <code>cordis.patch.yml</code> file in the profile directory. The environment variable path is the quickest for a single deployment:</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Environment variable</th>
          <th>Default</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CLS endpoint</td>
          <td><code>CLS_ENDPOINT</code></td>
          <td>—</td>
      </tr>
      <tr>
          <td>CLS topic ID</td>
          <td><code>CLS_TOPIC_ID</code></td>
          <td>—</td>
      </tr>
      <tr>
          <td>Secret ID</td>
          <td><code>CLS_SECRET_ID</code></td>
          <td>—</td>
      </tr>
      <tr>
          <td>Secret key</td>
          <td><code>CLS_SECRET_KEY</code></td>
          <td>—</td>
      </tr>
      <tr>
          <td>Service name</td>
          <td><code>CLS_SERVICE_NAME</code></td>
          <td>—</td>
      </tr>
      <tr>
          <td>Content capture</td>
          <td><code>OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT</code></td>
          <td><code>true</code></td>
      </tr>
  </tbody>
</table>
<p>The four credentials — endpoint, topic, secret ID, and secret key — are mandatory; without them the plugin has nowhere to write spans. The service name is how you identify this deployment inside CLS, so give it a value that distinguishes environments, for example <code>dsh-prod-web</code>.</p>
<p>For teams that prefer declarative config checked into the repo, <code>cordis.patch.yml</code> under the profile is the alternative. It carries the same options in YAML form and keeps secrets out of shell history — though you should still externalize the actual secret values through your secret manager rather than committing them.</p>
<h2 id="should-i-capture-prompts-responses-and-tool-content">Should I capture prompts, responses, and tool content?</h2>
<p>Content capture is on by default, and this is the feature that demands the most attention. With capture enabled, the plugin attaches prompts, LLM responses, and tool arguments and results to their spans. That gives you searchable, debuggable traces — but it also means credentials, source code, and personally identifiable information can be shipped to CLS as part of a trace.</p>
<p>The trade-off is real, and the brief is unambiguous: content capture can leak sensitive data into a cloud log service. You control the blast radius with two levers:</p>
<ul>
<li><code>captureContent: false</code> (or <code>OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=false</code>) disables content attachment entirely, leaving only span structure, timing, and metadata.</li>
<li><code>contentMaxChars</code>, defaulting to 128000, caps how much content a single span carries so a pathological tool result does not bloat a topic.</li>
</ul>
<p>For anything that touches production data, plan a content policy before you enable the plugin. Trace structure and timing usually survive with capture off; if you need content, apply the same redaction rules you use for your application logs.</p>
<h2 id="how-do-batching-buffering-and-retries-behave">How do batching, buffering, and retries behave?</h2>
<p>The plugin buffers spans in memory and flushes them in batches to avoid hammering CLS with a request per span. Four knobs control that behavior:</p>
<table>
  <thead>
      <tr>
          <th>Option</th>
          <th>Default</th>
          <th>Purpose</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>batchMaxSize</code></td>
          <td>32</td>
          <td>Maximum spans per upload</td>
      </tr>
      <tr>
          <td><code>maxQueueSize</code></td>
          <td>2048</td>
          <td>In-memory queue cap; oldest span is dropped when full</td>
      </tr>
      <tr>
          <td><code>flushIntervalMs</code></td>
          <td>5000</td>
          <td>How often buffered spans are flushed</td>
      </tr>
      <tr>
          <td><code>retryTimes</code></td>
          <td>3</td>
          <td>Retries per failed upload</td>
      </tr>
  </tbody>
</table>
<p>The most important implication is the drop policy. When the in-memory queue hits <code>maxQueueSize</code>, the plugin drops the oldest span rather than blocking the agent. That is a deliberate availability choice: observability must never stall the harness. Under normal load 2048 buffered spans is generous, but a burst of heavy tool calls can fill it, so treat dropped spans as a signal that batching needs tuning or the flush interval needs lowering.</p>
<h2 id="what-are-the-compatibility-requirements">What are the compatibility requirements?</h2>
<p>The plugin targets a specific window of DeepSeek Harness releases and a recent Node.js runtime:</p>
<table>
  <thead>
      <tr>
          <th>Component</th>
          <th>Requirement</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DeepSeek Harness</td>
          <td><code>&gt;=0.1.0-rc.6</code> and <code>&lt;0.2.0</code></td>
      </tr>
      <tr>
          <td>Node.js</td>
          <td><code>&gt;=18.0.0</code></td>
      </tr>
      <tr>
          <td>Language</td>
          <td>TypeScript</td>
      </tr>
      <tr>
          <td>License</td>
          <td>Apache-2.0</td>
      </tr>
      <tr>
          <td>Package manager note</td>
          <td>pnpm v9+ needs <code>approve-builds</code></td>
      </tr>
  </tbody>
</table>
<p>The version pin matters. Because the plugin hooks DSH&rsquo;s internal lifecycle events, it depends on the event shapes of a particular harness generation. If you are on a Harness older than <code>0.1.0-rc.6</code> or have already moved to <code>0.2.0</code> and beyond, verify plugin compatibility before assuming your traces will appear.</p>
<h2 id="how-does-the-tencent-plugin-compare-to-other-dsh-observability-options">How does the Tencent plugin compare to other DSH observability options?</h2>
<p>DeepSeek Harness already has an observability ecosystem, and the Tencent plugin is not the only choice. The realistic decision is less &ldquo;which tracer&rdquo; and more &ldquo;which architecture&rdquo;:</p>
<table>
  <thead>
      <tr>
          <th>Approach</th>
          <th>Transport</th>
          <th>Collector needed?</th>
          <th>Best for</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>TencentCloud AgentObs DSH</td>
          <td>Protobuf → CLS</td>
          <td>No</td>
          <td>Teams already on Tencent Cloud wanting CLS-native dashboards</td>
      </tr>
      <tr>
          <td>dsh-plugin-langfuse</td>
          <td>OpenTelemetry → Langfuse</td>
          <td>Langfuse self-host or cloud</td>
          <td>Teams standardizing on Langfuse as the LLM observability platform</td>
      </tr>
      <tr>
          <td>dsh-Agentlink</td>
          <td>OTel bridge to Codex</td>
          <td>OTLP endpoint</td>
          <td>Cross-framework observability bridging</td>
      </tr>
      <tr>
          <td>dsh-plugin-opentelemetry-tracing</td>
          <td>OTel → generic OTLP</td>
          <td>OTLP backend (e.g., SigNoz, Jaeger)</td>
          <td>Vendor-neutral, self-hosted stacks</td>
      </tr>
  </tbody>
</table>
<p>The structural difference is collector versus no collector. <code>dsh-plugin-langfuse</code> and the generic OpenTelemetry plugin both lean on OTLP and expect an endpoint you run or subscribe to. Langfuse, the incumbent open-source LLM observability platform, also auto-traces OpenAI-compatible DeepSeek API calls through <code>langfuse.openai</code>, but it still requires running Langfuse as a backend. The Tencent plugin is the first DSH plugin that targets a specific cloud vendor&rsquo;s native log service, so it carries no collector and no extra backend — you only need the CLS topic you already pay for.</p>
<h2 id="who-should-use-it-and-who-should-skip-it">Who should use it, and who should skip it?</h2>
<p>This plugin is a strong fit for teams that are already invested in Tencent Cloud. If your infrastructure, log pipeline, and alerting all live in CLS, pushing GenAI traces into the same platform gives you unified dashboards, cost correlation, and retention policy management without introducing a second observability silo. The zero-collector setup is genuinely attractive for a small agent fleet where running an OTLP stack is disproportionate overhead.</p>
<p>It is a less obvious fit when you are not on Tencent Cloud. The plugin locks your trace data to a single vendor&rsquo;s log service, and if your organization standardizes on OpenTelemetry or a self-hosted stack, <code>dsh-plugin-langfuse</code> or the generic OTel plugin keeps you portable. If your DeepSeek Harness version has drifted outside the supported window, portability also wins. In short: Tencent Cloud users get the cleanest setup; everyone else should weigh vendor lock-in against the convenience.</p>
<h2 id="hands-on-from-install-to-your-first-cls-trace">Hands-on: from install to your first CLS trace</h2>
<p>A minimal end-to-end run looks like this. First, add the plugin to the profile you run:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>dsh plugin --profile harness add tencentcloud-agentobs-sdk-dsh
</span></span><span style="display:flex;"><span>pnpm approve-builds   <span style="color:#75715e"># required on pnpm v9+</span>
</span></span></code></pre></div><p>Second, set the CLS credentials. With environment variables:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export CLS_ENDPOINT<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;ap-guangzhou.cls.tencentcs.com&#34;</span>
</span></span><span style="display:flex;"><span>export CLS_TOPIC_ID<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;your-topic-id&#34;</span>
</span></span><span style="display:flex;"><span>export CLS_SECRET_ID<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;your-secret-id&#34;</span>
</span></span><span style="display:flex;"><span>export CLS_SECRET_KEY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;your-secret-key&#34;</span>
</span></span><span style="display:flex;"><span>export CLS_SERVICE_NAME<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;dsh-prod&#34;</span>
</span></span></code></pre></div><p>Third, restart DeepSeek Harness so the observer attaches. Fourth, run a session that invokes the agent and at least one tool. Within the flush interval, spans will appear in your CLS topic, nested as entry → agent → step → chat → tool. From there, build a dashboard for token usage and per-tool latency, and add an alert on ERROR-status spans — you now have deepseek harness observability without a single extra service to run.</p>
<h2 id="what-pitfalls-should-i-watch-for">What pitfalls should I watch for?</h2>
<p>The practical gotchas cluster around configuration, privacy, and environment:</p>
<ul>
<li><strong>Content leakage.</strong> Capture is on by default; disable it or set <code>contentMaxChars</code> before tracing anything sensitive.</li>
<li><strong>pnpm build scripts.</strong> On pnpm v9+, forgetting <code>pnpm approve-builds</code> breaks the Protobuf layer silently at build time.</li>
<li><strong>Restart required.</strong> Adding the plugin does not attach it; you must restart DeepSeek Harness.</li>
<li><strong>Queue overflow drops.</strong> At <code>maxQueueSize</code> the oldest span is dropped to keep the harness responsive — not a sign of a network failure.</li>
<li><strong>Version drift.</strong> Outside <code>&gt;=0.1.0-rc.6 &lt;0.2.0</code> the lifecycle hooks may not fire, so traces can silently not appear.</li>
<li><strong>Secrets in config.</strong> Prefer a secret manager for <code>CLS_SECRET_ID</code> / <code>CLS_SECRET_KEY</code> over committing them to <code>cordis.patch.yml</code>.</li>
</ul>
<h2 id="faq">FAQ</h2>
<h3 id="what-does-deepseek-harness-observability-mean-in-practice">What does deepseek harness observability mean in practice?</h3>
<p>It means capturing the full lifecycle of a DeepSeek Harness run — session, agent invocation, each ReAct step, LLM calls, and tool executions — as structured trace spans so you can inspect latency, token usage, and errors per agent turn.</p>
<h3 id="does-the-tencentcloud-agentobs-sdk-require-an-otlp-collector">Does the TencentCloud AgentObs SDK require an OTLP collector?</h3>
<p>No. It uploads GenAI trace spans directly to Tencent Cloud Log Service via Protobuf using <code>tencentcloud-cls-sdk-js</code>. There is no OTLP collector, sidecar, or extra backend to deploy.</p>
<h3 id="what-span-hierarchy-does-the-plugin-produce-for-a-single-dsh-turn">What span hierarchy does the plugin produce for a single DSH turn?</h3>
<p>Each turn produces an ENTRY span containing an AGENT span, which contains STEP spans per ReAct round. Each STEP in turn has CHAT (model) and TOOL (tool execution) children, following Tencent Cloud&rsquo;s 5-layer entry → agent → step → chat → tool model.</p>
<h3 id="is-prompt-and-tool-content-captured-by-default">Is prompt and tool content captured by default?</h3>
<p>Yes, content capture is enabled by default, attaching prompts, responses, and tool arguments to spans. Disable it with <code>captureContent: false</code> or the <code>OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=false</code> environment variable.</p>
<h3 id="which-deepseek-harness-versions-does-the-plugin-support">Which DeepSeek Harness versions does the plugin support?</h3>
<p>It supports DeepSeek Harness <code>&gt;=0.1.0-rc.6</code> and <code>&lt;0.2.0</code>, with Node.js <code>&gt;=18.0.0</code>. It is Apache-2.0 licensed and written in TypeScript.</p>
]]></content:encoded></item></channel></rss>