Backend use cases for file editing

更新时间:
复制 MD 格式

Lingma file editing provides multi-file editing and tool-use capabilities to help developers complete coding tasks, such as implementing requirements, solving problems, generating unit test cases, and performing batch code modifications. This topic describes typical backend development scenarios and provides use cases for core tasks, such as code generation, code refactoring, and automated test creation. By integrating with your private enterprise data, Lingma can provide personalized recommendations and code generation that meet your company's specific needs. This significantly improves development efficiency and quality.

Background information

In modern enterprise development, developers frequently face core tasks. These tasks include generating code based on in-house frameworks, replacing and refactoring code for different technical frameworks, and writing unit tests. However, traditional AI tools often rely on general-purpose models for code generation and recommendations. These tools struggle to integrate with private enterprise data for personalized support, which limits their effectiveness in real-world development.

Lingma provides two core features: file editing and enterprise knowledge bases. You can use file editing to generate project-level code and obtain personalized recommendations based on your private enterprise data. This provides developers with intelligent solutions that better fit their actual needs. This topic uses several use cases to demonstrate the practical application and value of Lingma in these scenarios.

Note

To obtain the best results from knowledge base retrieval augmentation, make sure your knowledge base documents, such as API reference and technical framework descriptions, follow the specifications in Prepare the enterprise knowledge base. For more information, see Knowledge Base Construction Guide and Retrieval Augmentation Guide.

Scenario 1: Code generation based on API reference

In enterprise development, Lingma can use your API reference to quickly generate high-quality business code. You can simply enter the requirement description and relevant parameters, and Lingma automatically generates the backend code. This improves development efficiency and allows your team to focus on more valuable development work.

1. Prepare the enterprise knowledge base

API reference preparation principles

  1. Clear structure: Organize the document logically into sections, such as interface description, request parameters, and response data. Each section must have a clear title.

  2. Interface information:

    • Function: Briefly describe the function of the interface, such as retrieving a list of knowledge bases.

    • Method: Specify the HTTP method used, such as GET.

    • Address: Provide a complete sample URL.

    • Authentication: Describe the required authentication method, such as identity verification using the Authorization field in the request header.

  3. Request parameters: List each parameter and specify its meaning, type, whether it is required, and its default value.

  4. Response data: Describe the data structure for successful and error responses.

  5. Error handling: Explain error scenarios and their corresponding status codes.

  6. Code examples: Provide a code implementation in any language or a curl example. For a specific example, see the "Get Knowledge Base File List API Reference" sample that follows.

Sample document

Get Knowledge Base File List API Reference.md

# ListKbFiles - Get a list of knowledge base files

Gets a list of knowledge base files.


### Endpoints and authorization information
- Get the endpoint and replace <domain> in the API request syntax: [Endpoints (domain)](../../API_Access.md).
- Get a personal access token. For more information, see [Get a personal access token](../../Get_Access_Token.md).

|Product|Resource|Required Permission|
|-|-|-|
|Lingma|Knowledge base file|Read-only|


### Request syntax
```
GET https://{domain}/oapi/v1/lingma/organizations/{organizationId}/knowledgeBases/{kbId}/files
```

### Request headers
|Parameter|Type|Required|Description|Sample Value|
|-|-|-|-|-|
|x-yunxiao-token|string|Yes|Personal access token.|pt-0fh3\*\*\*\*0fbG_35af\*\*\*\*0484|


### Request parameters
|Parameter|Type|Location|Required|Description|Sample Value|
|-|-|-|-|-|-|
| organizationId | string | path | Yes | The ID of the enterprise. | <nil> |
| kbId | string | path | Yes | The knowledge base ID. | <nil> |
| query | string | query | No | A fuzzy query for the file name. | <nil> |
| orderBy | string | query | No | The column to sort by. The following sorting fields are supported: gmt_added, kb_id, and file_state. | <nil> |
| sort | string | query | No | The sort order. Descending (desc) and ascending (asc) orders are supported. | <nil> |
| page | integer | query | No | The current page. The default is 1. | <nil> |
| perPage | integer | query | No | The number of data entries per page. The default is 20. | <nil> |



### Request example
```
curl -X 'GET' \
  'https://test.rdc.aliyuncs.com/oapi/v1/lingma/organizations/{organizationId}/knowledgeBases/{kbId}/files?query={query}&orderBy={orderBy}&sort={sort}&page={page}&perPage={perPage}' \
  -H 'Content-Type: application/json' \
  -H 'x-yunxiao-token: pt-0fh3****0fbG_35af****0484'
```


### Response parameters
|Parameter|Type|Description|Sample Value|
|-|-|-|-|
| gmtCreated | integer | Creation time | 1722149052460 |
| gmtModified | integer  | Modification time | 1722149055731 |
| gmtAdded | integer  | Time added | 1722149052515 |
| fileId | string | The unique ID of the file | 30d4****aca4 |
| filename | string | The name of the file | history.txt |
| fileSize | string | The size of the file | 5 B |
| filePath | string | The full path of the file | lingma/rag_kb/... |
| fileState | string | The processing state of the file | PROCESSED |
| kdId | string | The unique ID of the knowledge base | 3a41****3eca |
| organizationId | string | The ID of the organization | 9a2****5ef1 |                     
| uploadUser | object | Information about the user who added the file |  |
| &nbsp;&nbsp;&nbsp;&nbsp;id | string | The unique ID of the user | d402****9ae6 |
| &nbsp;&nbsp;&nbsp;&nbsp;displayName | string | The logon name of the user | lingma |
| &nbsp;&nbsp;&nbsp;&nbsp;userName | string | The nickname of the user | lingma |
| &nbsp;&nbsp;&nbsp;&nbsp;avatarUrl | string | The profile picture URL of the user | /objects/ajax/... |


### Response example
```
[
    {
        "fileId": "",
        "filePath": "",
        "fileSize": "",
        "fileState": "",
        "filename": "",
        "gmtAdded": 0,
        "gmtCreated": 0,
        "gmtModified": 0,
        "kbId": "",
        "organizationId": "",
        "uploadUser": {
            "avatarUrl": "",
            "displayName": "",
            "id": "",
            "userName": ""
        }
    }
]
```



### Response headers
|Parameter|Description|Sample Value|
|-|-|-|
|x-next-page|The next page.| <nil> |
|x-page|The current page.| <nil> |
|x-per-page|The number of data entries per page.| <nil> |
|x-prev-page|The previous page.| <nil> |
|x-total|The total data volume.| <nil> |
|x-total-pages|The total number of pages.| <nil> |



### Error codes
|HTTP status code|Error code|Error message|Description|
|400|BadRequest|Bad Request|The request was invalid.|
|401|Unauthorized|Unauthorized|You are not authorized.|
|403|Forbidden|Forbidden|The current user has not joined the organization.|
|429|TooManyRequests|TooManyRequests|Too many requests were sent.|
|500|InternalServerError|InternalServerError|An internal error occurred.|

2. Demo: Querying the Lingma knowledge base list

Scenario 2: Code generation based on a business framework

When implementing AI-powered coding in an enterprise, a common problem is that the model-generated code does not use the company's in-house technical and business frameworks. Lingma adapts to in-house frameworks by integrating with your enterprise knowledge base. For example, in a product inventory system scenario, a company can add its distributed lock implementation solution and standardized business framework documents to the enterprise knowledge base. Developers can then generate code based on a standardized inventory management module. Reusing standardized code improves development efficiency, reduces redundant development costs, ensures business logic consistency, and improves code quality.

1. Prepare the enterprise knowledge base

Knowledge selection principles

  1. Define the target business model and specific implementation mechanisms:

    • Identify key business modules: Determine the key business logic modules in the system that require a unified implementation, such as inventory management, order processing, and payment systems.

    • Refine specific business implementation mechanisms: For example, inventory management might involve different scenarios, such as stock deduction, queries, and locking. Each scenario may require a different implementation mechanism.

  2. Select relevant code files from the business framework and add them to the enterprise code knowledge base:

    • Filter the core code that implements key business logic from the business framework. Organize and store this code in a separate knowledge base. For example, stock deduction requires a distributed lock mechanism to ensure concurrency safety. Therefore, you must select the core code module that implements the distributed lock.

Business framework documentation principles

  1. Structured presentation: The document must include an introduction, features, architecture design, usage instructions, and code examples. Use ## Module Name and ### Level Marker for unambiguous titles.

  2. Standardized terminology: Use unified and commonly used enterprise terms, such as Lua script, synchronous message, and asynchronous message. This helps you accurately retrieve information when you write prompts in the IDE.

  3. Code example requirements

    1. Dependency declaration: Show the complete pom.xml or Gradle import method.

    2. Core implementation: Provide the complete code block for Redis locking. For a specific example, see the "Redis Lock Protection" sample that follows. Make sure the document is in the enterprise knowledge base.

Sample document

Redis Lock Protection.md

# Documentation for a Redis-based distributed lock framework

## Introduction

In a distributed system, multiple instances might access shared resources at the same time. This can lead to data inconsistency or resource competition. A distributed lock coordinates access from multiple service instances. It ensures that only one instance can operate on a specific resource at any given time. This framework implements a distributed lock based on Redis. It provides a simple and efficient locking and unlocking mechanism for Java applications.

---

## Features

- **Simple and easy to use**: Provides a concise API for easy integration into existing projects.
- **Efficient and reliable**: Based on Redis atomic operations to ensure lock reliability.
- **Highly configurable**: Supports configuration of parameters such as lock time-to-live (TTL) and maximum wait time.
- **Supports reentrant locks**: The same thread can acquire the lock multiple times to avoid deadlocks.

---

## Architecture design

This distributed lock framework is implemented based on the Redis `SET` command and a Lua script. The design is as follows:

1. **Locking**: Uses the Redis `SET NX PX` command to set the lock, which guarantees the atomicity of the operation.
2. **Unlocking**: Uses a Lua script to verify the lock owner before deleting it. This prevents accidentally deleting locks held by other clients.
3. **Reentrant lock support**: Stores a thread identifier and a reentry count in the lock's value. This allows the same thread to acquire the lock multiple times.


---

## Instructions

### Prerequisites

- A Redis service is deployed and running.
- A Java environment is set up, and you use Maven or Gradle for project management.

### Integration steps

1. **Add dependencies**: Add a Redis client library, such as Jedis or Lettuce, to your project.
2. **Configure the Redis connection**: Configure connection information such as the Redis host, port, and password.
3. **Initialize the lock framework**: Get a lock instance through the factory class provided by the framework.
4. **Use the lock**: Call the lock and unlock methods in the code section that requires locking.

---

## Code examples

The following example is based on the Jedis client. Adjust it according to the Redis client you are using.

### Dependency configuration

Use Maven to manage dependencies. Add the Jedis dependency to your `pom.xml` file:

```xml
<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>4.3.1</version>
    </dependency>
</dependencies>
```

### Lock implementation

```java
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

import java.util.UUID;

public class RedisDistributedLock {
    private Jedis jedis;
    private String lockKey;
    private String lockValue;
    private long expireTime; // Lock time-to-live (TTL) in milliseconds
    private long acquireTimeout; // Maximum wait time to acquire the lock in milliseconds

    public RedisDistributedLock(Jedis jedis, String lockKey, long expireTime, long acquireTimeout) {
        this.jedis = jedis;
        this.lockKey = lockKey;
        this.expireTime = expireTime;
        this.acquireTimeout = acquireTimeout;
        this.lockValue = UUID.randomUUID().toString();
    }

    /**
     * Tries to acquire the lock
     * @return Whether the lock was acquired successfully
     */
    public boolean lock() {
        long end = System.currentTimeMillis() + acquireTimeout;
        SetParams params = new SetParams();
        params.nx().px(expireTime);

        while (System.currentTimeMillis() < end) {
            String result = jedis.set(lockKey, lockValue, params);
            if ("OK".equals(result)) {
                return true;
            }
            try {
                Thread.sleep(100); // Wait for a period and retry
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
        return false;
    }

    /**
     * Releases the lock
     * @return Whether the lock was released successfully
     */
    public boolean unlock() {
        String luaScript =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "   return redis.call('del', KEYS[1]) " +
                "else " +
                "   return 0 " +
                "end";

        Object result = jedis.eval(luaScript, 1, lockKey, lockValue);
        return "1".equals(result.toString());
    }
}
```

### Usage example

```java
import redis.clients.jedis.Jedis;

public class LockExample {
    public static void main(String[] args) {
        // Connect to Redis
        Jedis jedis = new Jedis("localhost", 6379);
        try {
            // Initialize the distributed lock
            RedisDistributedLock lock = new RedisDistributedLock(jedis, "my_lock_key", 5000, 10000);

            // Try to acquire the lock
            if (lock.lock()) {
                try {
                    // Business logic after successfully acquiring the lock
                    
                } finally {
                    // Release the lock
                    boolean unlocked = lock.unlock();
                    if (unlocked) {
                        System.out.println("Lock released successfully");
                    } else {
                        System.out.println("Failed to release the lock");
                    }
                }
            } else {
                System.out.println("Could not acquire the lock. Execute other logic or retry");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
    }
}
```

2. Demo: Protecting resources with a distributed lock for e-commerce inventory management

Scenario 3: Code refactoring based on an in-house technical framework

When an enterprise adjusts its technical architecture, replacing technical components, such as replacing RabbitMQ with RocketMQ, often presents challenges. These challenges include complex code changes and difficulties in ensuring quality. Lingma uses file editing and the enterprise knowledge base to help companies efficiently replace components. This process ensures system quality and consistency while it shortens the development cycle and reduces risks.

1. Prepare the enterprise knowledge base document

Technical framework documentation principles

  1. Structural requirements

    • Title levels: Use # Main Title to define functional modules, such as the message sending mechanism. Use ##/### Subtitle to break down operational steps, such as topic creation or synchronous sending.

    • Content isolation: Mandate separate sections for independent functional units. Describe each message sending method (synchronous, asynchronous, one-way) separately.

  2. Description of key technical parameters

    • Annotate important configurations, such as autoCreateTopicEnable, with their scope and risk scenarios.

    • Provide specific command-line interface (CLI) command examples, such as mqadmin updateTopic -t ORDER_TOPIC, and their output.

  3. Code block formatting: Use Markdown's code block syntax (```) to format command lines and code. Differentiate between languages, such as Shell and Java.

  4. Fault-tolerant design: Synchronous and asynchronous operations must include mandatory exception catching and retry mechanisms. One-way transmission is unreliable. If data loss is unacceptable, choose another method.

  5. Code demonstration standards [Important]

    • End-to-end examples: For each message mode (synchronous, asynchronous, one-way), provide complete code for initialization→sending→shutdown.

    • Key comments: Annotate key technical points at nodes such as producer creation and message body construction.

  6. For a code example, see the "Sending Messages with the In-house Middleware RocketMQ" sample that follows. Make sure the document is in the enterprise knowledge base.

Sample document

Sending Messages with the In-house Middleware RocketMQ.md

# Sending normal messages

## 1. Create a topic in the cluster

Before sending a message, make sure the target topic has been created and initialized. You can use the RocketMQ Admin tool to create the target topic.

The RocketMQ deployment package has the **autoCreateTopicEnable** configuration enabled by default. This automatically creates a topic for the sent message. However, this feature is recommended only for initial testing.

**In a production environment, it is strongly recommended to manage the lifecycle of all topics and disable the automatic creation parameter.** This prevents the production cluster from having many invalid topics that cannot be managed or reclaimed. This would put pressure on cluster registration and affect the stability of the production environment.

```shell
$ sh bin/mqadmin updateTopic -c DefaultCluster -t TopicTest -n 127.0.0.1:9876
create topic to 127.0.0.1:10911 success.
TopicConfig [topicName=TopicTest, readQueueNums=8, writeQueueNums=8, perm=RW-, topicFilterType=SINGLE_TAG, topicSysFlag=0, order=false, attributes=null]
```

You can see that after the command is executed, eight queues are created on this broker machine for the topic named TopicTest.

## 2. Add the client dependency

First, add the RocketMQ client dependency to your Java program.

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<Tabs>
<TabItem value="Maven" label="Maven" default >

```java
<dependency>
  <groupId>org.apache.rocketmq</groupId>
  <artifactId>rocketmq-client</artifactId>
  <version>4.9.4</version>
</dependency>
```
</TabItem>
<TabItem value="Gradle" label="Gradle">

```java 
compile 'org.apache.rocketmq:rocketmq-client:4.9.4'
```

</TabItem>

</Tabs>


## 3. Send messages

Apache RocketMQ can be used to send messages in three ways: **synchronous, asynchronous, and one-way transmission**. The first two message types are reliable because they receive a response indicating whether the message was sent successfully.

### 3.1 Synchronous sending

Synchronous sending is the most common method. The message sender sends a message and then waits for a synchronous response from the server before sending the next message. Reliable synchronous transmission is widely used in various scenarios, such as important notifications and short message notifications.


The entire code flow for synchronous sending is as follows:
1. **First, create a producer**. For normal messages, you can create a DefaultMQProducer. When creating it, you must specify the name of the producer group. A producer group is a collection of producers of the same type that send the same type of messages and have the same sending logic.
2. **Set the NameServer address**. Apache RocketMQ provides many ways to set the NameServer address. The client configuration is described later. Here, the producer's setNamesrvAddr API is called in the code to set it. If there are multiple NameServers, separate them with semicolons, for example, "127.0.0.2:9876;127.0.0.3:9876". 
3. **The third step is to construct the message**. Specify information such as the topic, tag, and body. A tag can be understood as a label for further classifying messages. RocketMQ can filter messages by tag on the consumer side.
4. **Finally, call the send interface to send the message**. Synchronous sending waits for the result and finally returns a SendResult. The SendResult includes the actual sending status, such as SEND_OK (sent successfully), FLUSH_DISK_TIMEOUT (disk flush timeout), FLUSH_SLAVE_TIMEOUT (synchronization to slave timeout), and SLAVE_NOT_AVAILABLE (slave not available). If the sending fails, an exception is thrown.
``` java
public class SyncProducer {
  public static void main(String[] args) throws Exception {
    // (1) Initialize a producer and set the producer group name
    DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name"); 
    // (2) Set the NameServer address
    producer.setNamesrvAddr("localhost:9876");  
    // Start the producer
    producer.start();
  
      // (3) Create a message and specify its topic, tag, body, and other information. A tag can be understood as a label for further classifying messages. RocketMQ can filter messages by tag on the consumer side.
      Message msg = new Message("TopicTest" /* Topic */,
        "TagA" /* Tag */,
        ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
        );   
      // (4) Use the producer to send the message and wait synchronously for the result
      SendResult sendResult = producer.send(msg);   
      System.out.printf("%s%n", sendResult);
    
    // Once the producer is no longer in use, shut it down
    producer.shutdown();
  
}
```

:::note
For synchronous sending, be sure to catch sending exceptions and implement a fallback logic on the business side. If you ignore exceptions, messages may fail to be sent.
:::


### 3.2 Asynchronous sending


Asynchronous sending is a communication method where the sender sends a message and then sends the next message without waiting for a response from the server.
:::note
Asynchronous sending requires implementing the **asynchronous sending callback interface** (SendCallback).
:::
After sending a message, the message sender does not need to wait for a server response before sending the next message. The sender receives the server response through a callback interface and processes the result. Asynchronous sending is generally used for business scenarios with long link latency and high sensitivity to response times. For example, after a video is uploaded, a notification is sent to start the transcoding service. After transcoding is complete, a notification is sent to push the transcoding result.

The following is a sample code.

``` java
public class AsyncProducer {
  public static void main(String[] args) throws Exception {
    // Initialize a producer and set the producer group name
    DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
    // Set the NameServer address
    producer.setNamesrvAddr("localhost:9876");
    // Start the producer
    producer.start();
    producer.setRetryTimesWhenSendAsyncFailed(0);
    
    final CountDownLatch countDownLatch = new CountDownLatch(messageCount);
   
      try {
          final int index = i;
          // Create a message and specify its topic, tag, body, and other information. A tag can be understood as a label for further classifying messages. RocketMQ can filter messages by tag on the consumer side.
          Message msg = new Message("TopicTest",
            "TagA",
            "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
          // Send the message asynchronously. The result is returned to the client through a callback.
          producer.send(msg, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
              System.out.printf("%-10d OK %s %n", index,
                sendResult.getMsgId());
              countDownLatch.countDown();
            }
            @Override
            public void onException(Throwable e) {
              System.out.printf("%-10d Exception %s %n", index, e);
              e.printStackTrace();
              countDownLatch.countDown();
            }
          });
        } catch (Exception e) {
            e.printStackTrace();
            countDownLatch.countDown();
        }
    
    // For asynchronous sending, if reliable transmission is required, you must wait for the callback interface to return a clear result before ending the logic. Otherwise, shutting down the producer immediately may cause some messages to fail to be transmitted.
    countDownLatch.await(5, TimeUnit.SECONDS);
    // Once the producer is no longer in use, shut it down
    producer.shutdown();
  }
}
```

:::note
The only difference between the code for asynchronous and synchronous sending is the parameters of the send interface call. Asynchronous sending does not wait for a return. Instead, the send method requires an implementation of SendCallback. The SendCallback interface has two main methods: onSuccess and onException, which indicate that the message was sent successfully or failed to be sent.
:::

### 3.3 One-way sending


The sender is only responsible for sending the message. It does not wait for a server response and no callback function is triggered. This means it only sends the request and does not wait for an acknowledgement. The process of sending a message this way is very short, usually on the microsecond level. It is suitable for scenarios that are very time-sensitive but do not have high reliability requirements, such as log collection.

``` java
public class OnewayProducer {
  public static void main(String[] args) throws Exception{
    // Initialize a producer and set the producer group name
    DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
    // Set the NameServer address
    producer.setNamesrvAddr("localhost:9876");
    // Start the producer
    producer.start();
    
      // Create a message and specify its topic, tag, body, and other information. A tag can be understood as a label for further classifying messages. RocketMQ can filter messages by tag on the consumer side.
      Message msg = new Message("TopicTest" /* Topic */,
        "TagA" /* Tag */,
        ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
      );
      // Because there is no request-acknowledgement handling when sending messages in one-way mode, if a message fails to be sent, data will be lost because there is no retry. If data loss is not acceptable, we recommend using reliable synchronous or asynchronous sending.
      producer.sendOneway(msg);
    }
     // Once the producer is no longer in use, shut it down
     producer.shutdown();
  
}
```

The one-way mode calls sendOneway and does not wait for or process any return result.

2. Demo: Replacing the message queue component from RabbitMQ to RocketMQ

Scenario 4: Unit test agent

The unit test agent is a specialized feature of file editing. It generates unit test files in batches for code changes (#codeChanges) or for one or more code files. You can simply enter the content to be tested and the requirements. The AI automatically generates a test plan and test cases. It then compiles, runs, and automatically fixes them. This process improves test coverage and quality, and reduces your test-writing costs.

Note

Lingma has launched the unit test agent feature, which includes automatic compilation, running, and fixing capabilities.

1. Unit test agent metrics

Technical assessment dimensions, such as compilation pass rate and run pass rate, are based on the assessment results of a local Java code assessment set:

  1. One-time compilation pass rate: This metric measures the success rate of the code generated by the unit test agent during the compilation phase. The current compilation pass rate is 81.00%.

  2. One-time run pass rate: The run pass rate focuses on the performance of the generated unit tests in the actual running environment. The current run pass rate is 80.55%, which shows that the performance during the execution phase is similar to that of the compilation phase.

Business effectiveness assessment - business team unit test coverage: Unit test coverage is a key metric for business teams because it reflects the extent to which automated unit tests cover the codebase. Increasing this rate not only enhances product quality but also promotes faster development cycles and higher customer satisfaction.

2. Demo: Automatic generation, compilation, and running of unit tests